2 * Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
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.
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.
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/>.
20 package com.stoutner.privacycell.services
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
35 import android.telephony.TelephonyDisplayInfo
36 import android.telephony.TelephonyManager
38 import androidx.core.app.ActivityCompat
39 import androidx.work.ExistingPeriodicWorkPolicy
40 import androidx.work.PeriodicWorkRequestBuilder
41 import androidx.work.WorkManager
43 import com.stoutner.privacycell.R
44 import com.stoutner.privacycell.activities.PrivacyCellActivity
45 import com.stoutner.privacycell.workers.RegisterRealtimeListener
47 import java.util.concurrent.TimeUnit
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"
54 class RealtimeMonitoringService : Service() {
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"
61 // Define the class variables.
62 private var currentStatus = ""
63 private lateinit var phoneStateListener: PhoneStateListener
65 inner class ServiceBinder : Binder() {
66 // Get a copy of this service as a binder.
67 fun getService(): RealtimeMonitoringService = this@RealtimeMonitoringService
70 override fun onBind(intent: Intent?): IBinder {
71 // Return a copy of the service binder.
72 return ServiceBinder()
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
79 // Create a notification channel group.
80 notificationManager.createNotificationChannelGroup(NotificationChannelGroup(REALTIME_MONITORING, getString(R.string.realtime_monitoring)))
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)
87 // Set the notification channel group.
88 secureNetworkChannel.group = REALTIME_MONITORING
89 insecureNetworkChannel.group = REALTIME_MONITORING
90 unknownNetworkChannel.group = REALTIME_MONITORING
92 // Disable the notification dots.
93 secureNetworkChannel.setShowBadge(false)
94 insecureNetworkChannel.setShowBadge(false)
95 unknownNetworkChannel.setShowBadge(false)
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
101 // Create the notification channels.
102 notificationManager.createNotificationChannel(secureNetworkChannel)
103 notificationManager.createNotificationChannel(insecureNetworkChannel)
104 notificationManager.createNotificationChannel(unknownNetworkChannel)
106 // Create a notification builder.
107 val notificationBuilder = Notification.Builder(this, UNKNOWN_NETWORK)
109 // Create an intent to open Privacy Cell.
110 val privacyCellIntent = Intent(this, PrivacyCellActivity::class.java)
112 // Create a pending intent from the Privacy Cell intent.
113 val privacyCellPendingIntent = PendingIntent.getActivity(this, 0, privacyCellIntent, PendingIntent.FLAG_IMMUTABLE)
115 // Set the notification to open Privacy Cell.
116 notificationBuilder.setContentIntent(privacyCellPendingIntent)
118 // Set the notification text.
119 notificationBuilder.setContentText(getString(R.string.unknown_network))
121 // Set the notification icon.
122 notificationBuilder.setSmallIcon(R.drawable.insecure_notification)
125 notificationBuilder.setColor(getColor(R.color.red_text))
127 // Start the foreground notification.
128 startForeground(NOTIFICATION_ID, notificationBuilder.build())
130 // Define the phone state listener.
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)
140 // Set the notification to open Privacy Cell.
141 secureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
143 // Set the notification text.
144 secureNetworkNotificationBuilder.setContentText(getString(R.string.secure_network))
146 // Set the notification icon.
147 secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification)
150 secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_text))
152 // Update the notification.
153 notificationManager.notify(NOTIFICATION_ID, secureNetworkNotificationBuilder.build())
155 // Store the new network status.
156 currentStatus = SECURE_NETWORK
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)
164 // Set the notification to open Privacy Cell.
165 insecureNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent)
167 // Set the notification text.
168 insecureNetworkNotificationBuilder.setContentText(getString(R.string.insecure_network))
170 // Set the notification icon.
171 insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification)
174 insecureNetworkNotificationBuilder.setColor(getColor(R.color.red_text))
176 // Update the notification.
177 notificationManager.notify(NOTIFICATION_ID, insecureNetworkNotificationBuilder.build())
179 // Store the new network status.
180 currentStatus = INSECURE_NETWORK
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()
192 // Register the realtime listener work request.
193 WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest)
196 // Return a sticky service.
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
206 // Cancel the current listener if it exists.
207 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
209 // Listen for changes to the phone state.
210 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)