Add a higher level warning for antiquated protocols. https://redmine.stoutner.com...
[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         // Get a handle for the notification manager.
85         val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
86
87         // Create a notification channel group.
88         notificationManager.createNotificationChannelGroup(NotificationChannelGroup(REALTIME_MONITORING, getString(R.string.realtime_monitoring)))
89
90         // Prepare the notification channels.
91         val secureNetworkChannel = NotificationChannel(SECURE_NETWORK, getString(R.string.secure_network_channel), NotificationManager.IMPORTANCE_HIGH)
92         val insecureNetworkChannel = NotificationChannel(INSECURE_NETWORK, getString(R.string.insecure_network_channel), NotificationManager.IMPORTANCE_HIGH)
93         val antiquatedNetworkChannel = NotificationChannel(ANTIQUATED_NETWORK, getString(R.string.antiquated_network_channel), NotificationManager.IMPORTANCE_HIGH)
94         val unknownNetworkChannel = NotificationChannel(UNKNOWN_NETWORK, getString(R.string.unknown_network_channel), NotificationManager.IMPORTANCE_LOW)
95
96         // Set the notification channel group.
97         secureNetworkChannel.group = REALTIME_MONITORING
98         insecureNetworkChannel.group = REALTIME_MONITORING
99         antiquatedNetworkChannel.group = REALTIME_MONITORING
100         unknownNetworkChannel.group = REALTIME_MONITORING
101
102         // Disable the notification dots.
103         secureNetworkChannel.setShowBadge(false)
104         insecureNetworkChannel.setShowBadge(false)
105         antiquatedNetworkChannel.setShowBadge(false)
106         unknownNetworkChannel.setShowBadge(false)
107
108         // Set the primary channel notifications to be public.
109         secureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
110         insecureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
111         antiquatedNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
112
113         // Create the notification channels.
114         notificationManager.createNotificationChannel(secureNetworkChannel)
115         notificationManager.createNotificationChannel(insecureNetworkChannel)
116         notificationManager.createNotificationChannel(antiquatedNetworkChannel)
117         notificationManager.createNotificationChannel(unknownNetworkChannel)
118
119         // Create a notification builder.
120         val notificationBuilder = Notification.Builder(this, UNKNOWN_NETWORK)
121
122         // Create an intent to open Privacy Cell.
123         val privacyCellIntent = Intent(this, PrivacyCellActivity::class.java)
124
125         // Create a pending intent from the Privacy Cell intent.
126         val privacyCellPendingIntent = PendingIntent.getActivity(this, 0, privacyCellIntent, PendingIntent.FLAG_IMMUTABLE)
127
128         // Set the notification to open Privacy Cell.
129         notificationBuilder.setContentIntent(privacyCellPendingIntent)
130
131         // Set the notification text.
132         notificationBuilder.setContentText(getString(R.string.unknown_network))
133
134         // Set the notification icon.
135         notificationBuilder.setSmallIcon(R.drawable.antiquated_notification_enabled)
136
137         // Set the color.
138         notificationBuilder.setColor(getColor(R.color.red_notification_icon))
139
140         // Start the foreground notification.
141         startForeground(NOTIFICATION_ID, notificationBuilder.build())
142
143         // Define the phone state listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
144         phoneStateListener = object : PhoneStateListener() {
145             override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
146                 // Get the consider 3G antiquated preference.
147                 val consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)
148
149                 // Populate the notification according to the network type.
150                 if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_IWLAN) ||
151                     (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN)) {  // This is a secure network.
152                     // Only update the notification if the network status has changed.
153                     if (currentStatus != SECURE_NETWORK) {
154                         // Create a secure network notification builder.
155                         val secureNetworkNotificationBuilder = Notification.Builder(applicationContext, SECURE_NETWORK)
156
157                         // Set the notification to open Privacy Cell.
158                         secureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
159
160                         // Set the notification text.
161                         secureNetworkNotificationBuilder.setContentText(getString(R.string.secure_network))
162
163                         // Set the notification icon.
164                         secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification_enabled)
165
166                         // Set the color.
167                         secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_icon))
168
169                         // Update the notification.
170                         notificationManager.notify(NOTIFICATION_ID, secureNetworkNotificationBuilder.build())
171
172                         // Store the new network status.
173                         currentStatus = SECURE_NETWORK
174                     }
175                 } else if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_LTE) || (!consider3gAntiquated && (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_1xRTT ||
176                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
177                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_B) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EHRPD) ||
178                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UMTS) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_TD_SCDMA) ||
179                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSDPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSUPA) ||
180                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPAP)))) {
181                             // This is an insecure network.
182                     // Only update the notification if the network status has changed.
183                     if (currentStatus != INSECURE_NETWORK) {
184                         // Create an insecure network notification builder.
185                         val insecureNetworkNotificationBuilder = Notification.Builder(applicationContext, INSECURE_NETWORK)
186
187                         // Set the notification to open Privacy Cell.
188                         insecureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
189
190                         // Set the notification text.
191                         insecureNetworkNotificationBuilder.setContentText(getString(R.string.insecure_network))
192
193                         // Set the notification icon.
194                         insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification_enabled)
195
196                         // Set the color.
197                         insecureNetworkNotificationBuilder.setColor(getColor(R.color.yellow_notification_icon))
198
199                         // Update the notification.
200                         notificationManager.notify(NOTIFICATION_ID, insecureNetworkNotificationBuilder.build())
201
202                         // Store the new network status.
203                         currentStatus = INSECURE_NETWORK
204                     }
205                 } else {  // This is an antiquated network.
206                     // Only update the notification if the network status has changed.
207                     if (currentStatus != ANTIQUATED_NETWORK) {
208                         // Create an antiquated network notification builder.
209                         val antiquatedNetworkNotificationBuilder = Notification.Builder(applicationContext, ANTIQUATED_NETWORK)
210
211                         // Set the notification to open Privacy Cell.
212                         antiquatedNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
213
214                         // Set the notification text.
215                         antiquatedNetworkNotificationBuilder.setContentText(getString(R.string.antiquated_network))
216
217                         // Set the notification icon.
218                         antiquatedNetworkNotificationBuilder.setSmallIcon(R.drawable.antiquated_notification_enabled)
219
220                         // Set the color.
221                         antiquatedNetworkNotificationBuilder.setColor(getColor(R.color.red_notification_icon))
222
223                         // Update the notification.
224                         notificationManager.notify(NOTIFICATION_ID, antiquatedNetworkNotificationBuilder.build())
225
226                         // Store the new network status.
227                         currentStatus = ANTIQUATED_NETWORK
228                     }
229                 }
230             }
231         }
232
233         // Check to see if the read phone state permission has been granted.
234         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
235             // Create a register realtime listener work request that fires every hour.
236             // 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.
237             val registerRealtimeListenerWorkRequest = PeriodicWorkRequestBuilder<RegisterRealtimeListenerWorker>(1, TimeUnit.HOURS).build()
238
239             // Register the realtime listener work request.
240             WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest)
241         }
242
243         // Return a sticky service.
244         return START_STICKY
245     }
246
247     fun registerTelephonyManagerListener() {
248         // Check to see if the read phone state permission has been granted.
249         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
250             // Get a handle for the telephony manager.
251             val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
252
253             // Cancel the current listener if it exists.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
254             telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
255
256             // Listen for changes to the phone state.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
257             telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
258         }
259     }
260 }