5aa98f8284d5e201b2ac433f302e65ba294f2a95
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / services / RealtimeMonitoringService.kt
1 /*
2  * Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
5  *
6  * Privacy Cell is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Cell is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Cell.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacycell.services
21
22 import android.Manifest
23 import android.app.Notification
24 import android.app.NotificationChannel
25 import android.app.NotificationChannelGroup
26 import android.app.NotificationManager
27 import android.app.PendingIntent
28 import android.app.Service
29 import android.content.Context
30 import android.content.Intent
31 import android.content.pm.PackageManager
32 import android.os.Binder
33 import android.os.IBinder
34 import android.telephony.PhoneStateListener  // This can be replaced by `TelephonyCallback` once the minimum API >= 31.
35 import android.telephony.TelephonyDisplayInfo
36 import android.telephony.TelephonyManager
37
38 import androidx.core.app.ActivityCompat
39 import androidx.work.ExistingPeriodicWorkPolicy
40 import androidx.work.PeriodicWorkRequestBuilder
41 import androidx.work.WorkManager
42
43 import com.stoutner.privacycell.R
44 import com.stoutner.privacycell.activities.PrivacyCellActivity
45 import com.stoutner.privacycell.workers.RegisterRealtimeListener
46
47 import java.util.concurrent.TimeUnit
48
49 // Define the class constants.
50 const val REALTIME_MONITORING = "realtime_monitoring"
51 const val NOTIFICATION_ID = 1
52 const val UNKNOWN_NETWORK = "unknown_network"
53
54 class RealtimeMonitoringService : Service() {
55     companion object {
56         // Define the public constants.  These are used in the settings fragment to launch intents to edit the sound that plays for each channel.
57         const val SECURE_NETWORK = "secure_network"
58         const val INSECURE_NETWORK = "insecure_network"
59     }
60
61     // Define the class variables.
62     private var currentStatus = ""
63     private lateinit var phoneStateListener: PhoneStateListener  // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
64
65     inner class ServiceBinder : Binder() {
66         // Get a copy of this service as a binder.
67         fun getService(): RealtimeMonitoringService = this@RealtimeMonitoringService
68     }
69
70     override fun onBind(intent: Intent?): IBinder {
71         // Return a copy of the service binder.
72         return ServiceBinder()
73     }
74
75     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
76         // Get a handle for the notification manager.
77         val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
78
79         // Create a notification channel group.
80         notificationManager.createNotificationChannelGroup(NotificationChannelGroup(REALTIME_MONITORING, getString(R.string.realtime_monitoring)))
81
82         // Prepare the notification channels.
83         val secureNetworkChannel = NotificationChannel(SECURE_NETWORK, getString(R.string.secure_network_channel), NotificationManager.IMPORTANCE_HIGH)
84         val insecureNetworkChannel = NotificationChannel(INSECURE_NETWORK, getString(R.string.insecure_network_channel), NotificationManager.IMPORTANCE_HIGH)
85         val unknownNetworkChannel = NotificationChannel(UNKNOWN_NETWORK, getString(R.string.unknown_network_channel), NotificationManager.IMPORTANCE_LOW)
86
87         // Set the notification channel group.
88         secureNetworkChannel.group = REALTIME_MONITORING
89         insecureNetworkChannel.group = REALTIME_MONITORING
90         unknownNetworkChannel.group = REALTIME_MONITORING
91
92         // Disable the notification dots.
93         secureNetworkChannel.setShowBadge(false)
94         insecureNetworkChannel.setShowBadge(false)
95         unknownNetworkChannel.setShowBadge(false)
96
97         // Set the notifications to be public for the secure and insecure networks.
98         secureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
99         insecureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
100
101         // Create the notification channels.
102         notificationManager.createNotificationChannel(secureNetworkChannel)
103         notificationManager.createNotificationChannel(insecureNetworkChannel)
104         notificationManager.createNotificationChannel(unknownNetworkChannel)
105
106         // Create a notification builder.
107         val notificationBuilder = Notification.Builder(this, UNKNOWN_NETWORK)
108
109         // Create an intent to open Privacy Cell.
110         val privacyCellIntent = Intent(this, PrivacyCellActivity::class.java)
111
112         // Create a pending intent from the Privacy Cell intent.
113         val privacyCellPendingIntent = PendingIntent.getActivity(this, 0, privacyCellIntent, PendingIntent.FLAG_IMMUTABLE)
114
115         // Set the notification to open Privacy Cell.
116         notificationBuilder.setContentIntent(privacyCellPendingIntent)
117
118         // Set the notification text.
119         notificationBuilder.setContentText(getString(R.string.unknown_network))
120
121         // Set the notification icon.
122         notificationBuilder.setSmallIcon(R.drawable.insecure_notification)
123
124         // Set the color.
125         notificationBuilder.setColor(getColor(R.color.red_text))
126
127         // Start the foreground notification.
128         startForeground(NOTIFICATION_ID, notificationBuilder.build())
129
130         // Define the phone state listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
131         phoneStateListener = object : PhoneStateListener() {
132             override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
133                 // Populate the notification according to the network type.
134                 if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) {  // This is a secure 5G NR SA network.
135                     // Only update the notification if the network status has changed.
136                     if (currentStatus != SECURE_NETWORK) {
137                         // Create a secure network notification builder.
138                         val secureNetworkNotificationBuilder = Notification.Builder(applicationContext, SECURE_NETWORK)
139
140                         // Set the notification to open Privacy Cell.
141                         secureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
142
143                         // Set the notification text.
144                         secureNetworkNotificationBuilder.setContentText(getString(R.string.secure_network))
145
146                         // Set the notification icon.
147                         secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification)
148
149                         // Set the color.
150                         secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_text))
151
152                         // Update the notification.
153                         notificationManager.notify(NOTIFICATION_ID, secureNetworkNotificationBuilder.build())
154
155                         // Store the new network status.
156                         currentStatus = SECURE_NETWORK
157                     }
158                 } else {  // This is not a secure 5G NR SA network.
159                     // Only update the notification if the network status has changed.
160                     if (currentStatus != INSECURE_NETWORK) {
161                         // Create an insecure network notification builder.
162                         val insecureNetworkNotificationBuilder = Notification.Builder(applicationContext, INSECURE_NETWORK)
163
164                         // Set the notification to open Privacy Cell.
165                         insecureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
166
167                         // Set the notification text.
168                         insecureNetworkNotificationBuilder.setContentText(getString(R.string.insecure_network))
169
170                         // Set the notification icon.
171                         insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification)
172
173                         // Set the color.
174                         insecureNetworkNotificationBuilder.setColor(getColor(R.color.red_text))
175
176                         // Update the notification.
177                         notificationManager.notify(NOTIFICATION_ID, insecureNetworkNotificationBuilder.build())
178
179                         // Store the new network status.
180                         currentStatus = INSECURE_NETWORK
181                     }
182                 }
183             }
184         }
185
186         // Check to see if the read phone state permission has been granted.
187         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
188             // Create a register realtime listener work request that fires every hour.
189             // This periodic request will fire shortly after being created (it fires about every hour near the beginning of the hour) and will reregister the listener if it gets garbage collected.
190             val registerRealtimeListenerWorkRequest = PeriodicWorkRequestBuilder<RegisterRealtimeListener>(1, TimeUnit.HOURS).build()
191
192             // Register the realtime listener work request.
193             WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest)
194         }
195
196         // Return a sticky service.
197         return START_STICKY
198     }
199
200     fun registerTelephonyManagerListener() {
201         // Check to see if the read phone state permission has been granted.
202         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
203             // Get a handle for the telephony manager.
204             val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
205
206             // Cancel the current listener if it exists.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
207             telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
208
209             // Listen for changes to the phone state.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
210             telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
211         }
212     }
213 }