]> gitweb.stoutner.com Git - PrivacyCell.git/blob - app/src/main/java/com/stoutner/privacycell/services/RealtimeMonitoringService.kt
68f92284ac0194557aa22cfa4348fb3a905b6187
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / services / RealtimeMonitoringService.kt
1 /*
2  * Copyright © 2021-2022 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 // The suppression of deprecation lint can be removed once the minimum API >= 31.
21 @file:Suppress("DEPRECATION")
22
23 package com.stoutner.privacycell.services
24
25 import android.Manifest
26 import android.app.Notification
27 import android.app.NotificationChannel
28 import android.app.NotificationChannelGroup
29 import android.app.NotificationManager
30 import android.app.PendingIntent
31 import android.app.Service
32 import android.content.Context
33 import android.content.Intent
34 import android.content.pm.PackageManager
35 import android.os.Binder
36 import android.os.IBinder
37 import android.telephony.PhoneStateListener  // This can be replaced by `TelephonyCallback` once the minimum API >= 31.
38 import android.telephony.TelephonyDisplayInfo
39 import android.telephony.TelephonyManager
40
41 import androidx.core.app.ActivityCompat
42 import androidx.preference.PreferenceManager
43 import androidx.work.ExistingPeriodicWorkPolicy
44 import androidx.work.PeriodicWorkRequestBuilder
45 import androidx.work.WorkManager
46
47 import com.stoutner.privacycell.R
48 import com.stoutner.privacycell.activities.PrivacyCellActivity
49 import com.stoutner.privacycell.workers.RegisterRealtimeListenerWorker
50
51 import java.util.concurrent.TimeUnit
52
53 // Define the class constants.
54 const val REALTIME_MONITORING = "realtime_monitoring"
55 const val NOTIFICATION_ID = 1
56 const val UNKNOWN_NETWORK = "unknown_network"
57
58 class RealtimeMonitoringService : Service() {
59     companion object {
60         // Define the public constants.  These are used in the settings fragment to launch intents to edit the sound that plays for each channel.
61         const val SECURE_NETWORK = "secure_network"
62         const val INSECURE_NETWORK = "insecure_network"
63         const val ANTIQUATED_NETWORK = "antiquated_network"
64     }
65
66     // Define the class variables.
67     private var currentStatus = ""
68     private lateinit var phoneStateListener: PhoneStateListener  // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
69
70     inner class ServiceBinder : Binder() {
71         // Get a copy of this service as a binder.
72         fun getService(): RealtimeMonitoringService = this@RealtimeMonitoringService
73     }
74
75     override fun onBind(intent: Intent?): IBinder {
76         // Return a copy of the service binder.
77         return ServiceBinder()
78     }
79
80     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
81         // Get a handle for the shared preferences.
82         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
83
84         // Check to see if realtime monitoring is enabled.  Sometimes the shared preferences can't return a value in time, because Android sucks.
85         // So, the default value is set to true, which is the safest value if the shared preferences can't be queried.
86         if (sharedPreferences.getBoolean(applicationContext.getString(R.string.realtime_monitoring_key), true)) {  // Realtime monitoring is enabled.
87             // Get a handle for the notification manager.
88             val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
89
90             // Create a notification channel group.
91             notificationManager.createNotificationChannelGroup(NotificationChannelGroup(REALTIME_MONITORING, getString(R.string.realtime_monitoring)))
92
93             // Prepare the notification channels.
94             val secureNetworkChannel = NotificationChannel(SECURE_NETWORK, getString(R.string.secure_network_channel), NotificationManager.IMPORTANCE_HIGH)
95             val insecureNetworkChannel = NotificationChannel(INSECURE_NETWORK, getString(R.string.insecure_network_channel), NotificationManager.IMPORTANCE_HIGH)
96             val antiquatedNetworkChannel = NotificationChannel(ANTIQUATED_NETWORK, getString(R.string.antiquated_network_channel), NotificationManager.IMPORTANCE_HIGH)
97             val unknownNetworkChannel = NotificationChannel(UNKNOWN_NETWORK, getString(R.string.unknown_network_channel), NotificationManager.IMPORTANCE_LOW)
98
99             // Set the notification channel group.
100             secureNetworkChannel.group = REALTIME_MONITORING
101             insecureNetworkChannel.group = REALTIME_MONITORING
102             antiquatedNetworkChannel.group = REALTIME_MONITORING
103             unknownNetworkChannel.group = REALTIME_MONITORING
104
105             // Disable the notification dots.
106             secureNetworkChannel.setShowBadge(false)
107             insecureNetworkChannel.setShowBadge(false)
108             antiquatedNetworkChannel.setShowBadge(false)
109             unknownNetworkChannel.setShowBadge(false)
110
111             // Set the primary channel notifications to be public.
112             secureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
113             insecureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
114             antiquatedNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
115
116             // Create the notification channels.
117             notificationManager.createNotificationChannel(secureNetworkChannel)
118             notificationManager.createNotificationChannel(insecureNetworkChannel)
119             notificationManager.createNotificationChannel(antiquatedNetworkChannel)
120             notificationManager.createNotificationChannel(unknownNetworkChannel)
121
122             // Create a notification builder.
123             val notificationBuilder = Notification.Builder(this, UNKNOWN_NETWORK)
124
125             // Create an intent to open Privacy Cell.
126             val privacyCellIntent = Intent(this, PrivacyCellActivity::class.java)
127
128             // Create a pending intent from the Privacy Cell intent.
129             val privacyCellPendingIntent = PendingIntent.getActivity(this, 0, privacyCellIntent, PendingIntent.FLAG_IMMUTABLE)
130
131             // Set the notification to open Privacy Cell.
132             notificationBuilder.setContentIntent(privacyCellPendingIntent)
133
134             // Set the notification text.
135             notificationBuilder.setContentText(getString(R.string.unknown_network))
136
137             // Set the notification icon.
138             notificationBuilder.setSmallIcon(R.drawable.antiquated_notification_enabled)
139
140             // Set the color.
141             notificationBuilder.setColor(getColor(R.color.red_notification_icon))
142
143             // Start the foreground notification.
144             startForeground(NOTIFICATION_ID, notificationBuilder.build())
145
146             // Define the phone state listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
147             phoneStateListener = object : PhoneStateListener() {
148                 @Deprecated("Deprecated in Java")
149                 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
150                     // Get the consider 3G antiquated preference.
151                     val consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)
152
153                     // Populate the notification according to the network type.
154                     if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_IWLAN) ||
155                         (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN)) {  // This is a secure network.
156                         // Only update the notification if the network status has changed.
157                         if (currentStatus != SECURE_NETWORK) {
158                             // Create a secure network notification builder.
159                             val secureNetworkNotificationBuilder = Notification.Builder(applicationContext, SECURE_NETWORK)
160
161                             // Set the notification to open Privacy Cell.
162                             secureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
163
164                             // Set the notification text.
165                             secureNetworkNotificationBuilder.setContentText(getString(R.string.secure_network))
166
167                             // Set the notification icon.
168                             secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification_enabled)
169
170                             // Set the color.
171                             secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_icon))
172
173                             // Update the notification.
174                             notificationManager.notify(NOTIFICATION_ID, secureNetworkNotificationBuilder.build())
175
176                             // Store the new network status.
177                             currentStatus = SECURE_NETWORK
178                         }
179                     } else if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_LTE) || (!consider3gAntiquated && (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_1xRTT ||
180                                 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
181                                 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_B) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EHRPD) ||
182                                 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UMTS) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_TD_SCDMA) ||
183                                 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSDPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSUPA) ||
184                                 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPAP)))) {
185                                 // This is an insecure network.
186                         // Only update the notification if the network status has changed.
187                         if (currentStatus != INSECURE_NETWORK) {
188                             // Create an insecure network notification builder.
189                             val insecureNetworkNotificationBuilder = Notification.Builder(applicationContext, INSECURE_NETWORK)
190
191                             // Set the notification to open Privacy Cell.
192                             insecureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
193
194                             // Set the notification text.
195                             insecureNetworkNotificationBuilder.setContentText(getString(R.string.insecure_network))
196
197                             // Set the notification icon.
198                             insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification_enabled)
199
200                             // Set the color.
201                             insecureNetworkNotificationBuilder.setColor(getColor(R.color.yellow_notification_icon))
202
203                             // Update the notification.
204                             notificationManager.notify(NOTIFICATION_ID, insecureNetworkNotificationBuilder.build())
205
206                             // Store the new network status.
207                             currentStatus = INSECURE_NETWORK
208                         }
209                     } else {  // This is an antiquated network.
210                         // Only update the notification if the network status has changed.
211                         if (currentStatus != ANTIQUATED_NETWORK) {
212                             // Create an antiquated network notification builder.
213                             val antiquatedNetworkNotificationBuilder = Notification.Builder(applicationContext, ANTIQUATED_NETWORK)
214
215                             // Set the notification to open Privacy Cell.
216                             antiquatedNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
217
218                             // Set the notification text.
219                             antiquatedNetworkNotificationBuilder.setContentText(getString(R.string.antiquated_network))
220
221                             // Set the notification icon.
222                             antiquatedNetworkNotificationBuilder.setSmallIcon(R.drawable.antiquated_notification_enabled)
223
224                             // Set the color.
225                             antiquatedNetworkNotificationBuilder.setColor(getColor(R.color.red_notification_icon))
226
227                             // Update the notification.
228                             notificationManager.notify(NOTIFICATION_ID, antiquatedNetworkNotificationBuilder.build())
229
230                             // Store the new network status.
231                             currentStatus = ANTIQUATED_NETWORK
232                         }
233                     }
234                 }
235             }
236
237             // Check to see if the read phone state permission has been granted.
238             if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
239                 // Create a register realtime listener work request that fires every hour.
240                 // 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.
241                 val registerRealtimeListenerWorkRequest = PeriodicWorkRequestBuilder<RegisterRealtimeListenerWorker>(1, TimeUnit.HOURS).build()
242
243                 // Register the realtime listener work request.
244                 WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest)
245             }
246         } else {  // Realtime monitoring is disabled.  This can happen if the restart listener work request fires after realtime monitoring has been disabled.
247             // Cancel the realtime listener work request.
248             WorkManager.getInstance(applicationContext).cancelUniqueWork(applicationContext.getString(R.string.register_listener_work_request))
249         }
250
251         // Return a sticky service.
252         return START_STICKY
253     }
254
255     fun registerTelephonyManagerListener() {
256         // Check to see if the read phone state permission has been granted.
257         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
258             // Get a handle for the telephony manager.
259             val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
260
261             // Cancel the current listener if it exists.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
262             telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
263
264             // Listen for changes to the phone state.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
265             telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
266         }
267     }
268 }