Fix problems with the realtime notifications. https://redmine.stoutner.com/issues/764
[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.app.Notification
23 import android.app.NotificationChannel
24 import android.app.NotificationChannelGroup
25 import android.app.NotificationManager
26 import android.app.PendingIntent
27 import android.app.Service
28 import android.content.Context
29 import android.content.Intent
30 import android.os.Binder
31 import android.os.IBinder
32 import android.telephony.PhoneStateListener
33 import android.telephony.TelephonyDisplayInfo
34 import android.telephony.TelephonyManager
35
36 import androidx.work.ExistingPeriodicWorkPolicy
37 import androidx.work.PeriodicWorkRequestBuilder
38 import androidx.work.WorkManager
39
40 import com.stoutner.privacycell.R
41 import com.stoutner.privacycell.activities.PrivacyCellActivity
42 import com.stoutner.privacycell.workers.RegisterRealtimeListener
43
44 import java.util.concurrent.TimeUnit
45
46 // Define the class constants.
47 const val REALTIME_MONITORING = "realtime_monitoring"
48 const val NOTIFICATION_ID = 1
49 const val UNKNOWN_NETWORK = "unknown_network"
50
51 class RealtimeMonitoringService : Service() {
52     companion object {
53         // Define the public constants.  These are used in the settings fragment to launch intents to edit the sound that plays for each channel.
54         const val SECURE_NETWORK = "secure_network"
55         const val INSECURE_NETWORK = "insecure_network"
56     }
57
58     // Define the class variables.
59     private var currentStatus = ""
60     private lateinit var phoneStateListener: PhoneStateListener
61
62     inner class ServiceBinder : Binder() {
63         // Get a copy of this service as a binder.
64         fun getService(): RealtimeMonitoringService = this@RealtimeMonitoringService
65     }
66
67     override fun onBind(intent: Intent?): IBinder {
68         // Return a copy of the service binder.
69         return ServiceBinder()
70     }
71
72     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
73         // Get a handle for the notification manager.
74         val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
75
76         // Create a notification channel group.
77         notificationManager.createNotificationChannelGroup(NotificationChannelGroup(REALTIME_MONITORING, getString(R.string.realtime_monitoring)))
78
79         // Prepare the notification channels.
80         val secureNetworkChannel = NotificationChannel(SECURE_NETWORK, getString(R.string.secure_network_channel), NotificationManager.IMPORTANCE_HIGH)
81         val insecureNetworkChannel = NotificationChannel(INSECURE_NETWORK, getString(R.string.insecure_network_channel), NotificationManager.IMPORTANCE_HIGH)
82         val unknownNetworkChannel = NotificationChannel(UNKNOWN_NETWORK, getString(R.string.unknown_network_channel), NotificationManager.IMPORTANCE_LOW)
83
84         // Set the notification channel group.
85         secureNetworkChannel.group = REALTIME_MONITORING
86         insecureNetworkChannel.group = REALTIME_MONITORING
87         unknownNetworkChannel.group = REALTIME_MONITORING
88
89         // Disable the notification dots.
90         secureNetworkChannel.setShowBadge(false)
91         insecureNetworkChannel.setShowBadge(false)
92         unknownNetworkChannel.setShowBadge(false)
93
94         // Set the notifications to be public for the secure and insecure networks.
95         secureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
96         insecureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
97
98         // Create the notification channels.
99         notificationManager.createNotificationChannel(secureNetworkChannel)
100         notificationManager.createNotificationChannel(insecureNetworkChannel)
101         notificationManager.createNotificationChannel(unknownNetworkChannel)
102
103         // Create a notification builder.
104         val notificationBuilder = Notification.Builder(this, UNKNOWN_NETWORK)
105
106         // Create an intent to open Privacy Cell.
107         val privacyCellIntent = Intent(this, PrivacyCellActivity::class.java)
108
109         // Create a pending intent from the Privacy Cell intent.
110         val privacyCellPendingIntent = PendingIntent.getActivity(this, 0, privacyCellIntent, PendingIntent.FLAG_IMMUTABLE)
111
112         // Set the notification to open Privacy Cell.
113         notificationBuilder.setContentIntent(privacyCellPendingIntent)
114
115         // Set the notification text.
116         notificationBuilder.setContentText(getString(R.string.unknown_network))
117
118         // Set the notification icon.
119         notificationBuilder.setSmallIcon(R.drawable.insecure_notification)
120
121         // Set the color.
122         notificationBuilder.setColor(getColor(R.color.red_text))
123
124         // Start the foreground notification.
125         startForeground(NOTIFICATION_ID, notificationBuilder.build())
126
127         // Define the phone state listener.
128         phoneStateListener = object : PhoneStateListener() {
129             override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
130                 // Populate the notification according to the network type.
131                 if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) {  // This is a secure 5G NR SA network.
132                     // Only update the notification if the network status has changed.
133                     if (currentStatus != SECURE_NETWORK) {
134                         // Create a secure network notification builder.
135                         val secureNetworkNotificationBuilder = Notification.Builder(applicationContext, SECURE_NETWORK)
136
137                         // Set the notification to open Privacy Cell.
138                         secureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
139
140                         // Set the notification text.
141                         secureNetworkNotificationBuilder.setContentText(getString(R.string.secure_network))
142
143                         // Set the notification icon.
144                         secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification)
145
146                         // Set the color.
147                         secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_text))
148
149                         // Update the notification.
150                         notificationManager.notify(NOTIFICATION_ID, secureNetworkNotificationBuilder.build())
151
152                         // Store the new network status.
153                         currentStatus = SECURE_NETWORK
154                     }
155                 } else {  // This is not a secure 5G NR SA network.
156                     // Only update the notification if the network status has changed.
157                     if (currentStatus != INSECURE_NETWORK) {
158                         // Create an insecure network notification builder.
159                         val insecureNetworkNotificationBuilder = Notification.Builder(applicationContext, INSECURE_NETWORK)
160
161                         // Set the notification to open Privacy Cell.
162                         insecureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
163
164                         // Set the notification text.
165                         insecureNetworkNotificationBuilder.setContentText(getString(R.string.insecure_network))
166
167                         // Set the notification icon.
168                         insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification)
169
170                         // Set the color.
171                         insecureNetworkNotificationBuilder.setColor(getColor(R.color.red_text))
172
173                         // Update the notification.
174                         notificationManager.notify(NOTIFICATION_ID, insecureNetworkNotificationBuilder.build())
175
176                         // Store the new network status.
177                         currentStatus = INSECURE_NETWORK
178                     }
179                 }
180             }
181         }
182
183         // Create a register realtime listener work request that fires every hour.  For some reason, when the service launches it will initially register the listener and then unregister it.
184         // This periodic request will fire shortly thereafter (it fires about every hour near the beginning of the hour) and will reregister the listener, which will stick this time.
185         val registerRealtimeListenerWorkRequest = PeriodicWorkRequestBuilder<RegisterRealtimeListener>(1, TimeUnit.HOURS).build()
186
187         // Register the realtime listener work request.
188         WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest)
189
190         // Return a sticky service.
191         return START_STICKY
192     }
193
194     fun registerTelephonyManagerListener() {
195         // Get a handle for the telephony manager.
196         val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
197
198         // Cancel the current listener if it exists.
199         telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
200
201         // Listen for changes to the phone state.
202         telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
203     }
204 }