• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.virtualization.terminal
17 
18 import android.app.Notification
19 import android.app.NotificationManager
20 import android.app.PendingIntent
21 import android.content.BroadcastReceiver
22 import android.content.Context
23 import android.content.Intent
24 import android.content.IntentFilter
25 import android.graphics.drawable.Icon
26 import com.android.virtualization.terminal.MainActivity.Companion.TAG
27 import java.util.Locale
28 
29 /**
30  * PortNotifier is responsible for posting a notification when a new open port is detected. User can
31  * enable or disable forwarding of the port in notification panel.
32  */
33 internal class PortNotifier(val context: Context) {
34     private val notificationManager: NotificationManager =
35         context.getSystemService<NotificationManager>(NotificationManager::class.java)
36     private val receiver: BroadcastReceiver =
<lambda>null37         PortForwardingRequestReceiver().also {
38             val intentFilter = IntentFilter(ACTION_PORT_FORWARDING)
39             context.registerReceiver(it, intentFilter, Context.RECEIVER_NOT_EXPORTED)
40         }
41     private val portsStateListener: PortsStateManager.Listener =
42         object : PortsStateManager.Listener {
onPortsStateUpdatednull43             override fun onPortsStateUpdated(oldActivePorts: Set<Int>, newActivePorts: Set<Int>) {
44                 // added active ports
45                 (newActivePorts - oldActivePorts).forEach { showNotificationFor(it) }
46                 // removed active ports
47                 (oldActivePorts - newActivePorts).forEach { discardNotificationFor(it) }
48             }
49         }
50     private val portsStateManager: PortsStateManager =
<lambda>null51         PortsStateManager.getInstance(context).also { it.registerListener(portsStateListener) }
52 
stopnull53     fun stop() {
54         portsStateManager.unregisterListener(portsStateListener)
55         context.unregisterReceiver(receiver)
56     }
57 
getStringnull58     private fun getString(resId: Int): String {
59         return context.getString(resId)
60     }
61 
getPendingIntentFornull62     private fun getPendingIntentFor(port: Int, enabled: Boolean): PendingIntent {
63         val intent = Intent(ACTION_PORT_FORWARDING)
64         intent.setPackage(context.getPackageName())
65         intent.setIdentifier(String.format(Locale.ROOT, "%d_%b", port, enabled))
66         intent.putExtra(KEY_PORT, port)
67         intent.putExtra(KEY_ENABLED, enabled)
68         return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
69     }
70 
showNotificationFornull71     private fun showNotificationFor(port: Int) {
72         val tapIntent = Intent(context, SettingsPortForwardingActivity::class.java)
73         tapIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
74         val tapPendingIntent =
75             PendingIntent.getActivity(context, 0, tapIntent, PendingIntent.FLAG_IMMUTABLE)
76 
77         val title = getString(R.string.settings_port_forwarding_notification_title)
78         val content =
79             context.getString(
80                 R.string.settings_port_forwarding_notification_content,
81                 port,
82                 portsStateManager.getActivePortInfo(port)?.comm,
83             )
84         val acceptText = getString(R.string.settings_port_forwarding_notification_accept)
85         val denyText = getString(R.string.settings_port_forwarding_notification_deny)
86         val icon = Icon.createWithResource(context, R.drawable.ic_launcher_foreground)
87 
88         val acceptAction: Notification.Action =
89             Notification.Action.Builder(
90                     icon,
91                     acceptText,
92                     getPendingIntentFor(port, true /* enabled */),
93                 )
94                 .build()
95         val denyAction: Notification.Action =
96             Notification.Action.Builder(
97                     icon,
98                     denyText,
99                     getPendingIntentFor(port, false /* enabled */),
100                 )
101                 .build()
102         val notification: Notification =
103             Notification.Builder(context, Application.CHANNEL_SYSTEM_EVENTS_ID)
104                 .setSmallIcon(R.drawable.ic_launcher_foreground)
105                 .setContentTitle(title)
106                 .setContentText(content)
107                 .setFullScreenIntent(tapPendingIntent, true)
108                 .addAction(acceptAction)
109                 .addAction(denyAction)
110                 .setAutoCancel(true)
111                 .build()
112         notificationManager.notify(TAG, port, notification)
113     }
114 
discardNotificationFornull115     private fun discardNotificationFor(port: Int) {
116         notificationManager.cancel(TAG, port)
117     }
118 
119     private inner class PortForwardingRequestReceiver : BroadcastReceiver() {
onReceivenull120         override fun onReceive(context: Context?, intent: Intent) {
121             if (ACTION_PORT_FORWARDING == intent.action) {
122                 performActionPortForwarding(intent)
123             }
124         }
125 
performActionPortForwardingnull126         fun performActionPortForwarding(intent: Intent) {
127             val port = intent.getIntExtra(KEY_PORT, 0)
128             val enabled = intent.getBooleanExtra(KEY_ENABLED, false)
129             portsStateManager.updateEnabledPort(port, enabled)
130             discardNotificationFor(port)
131         }
132     }
133 
134     companion object {
135         private const val ACTION_PORT_FORWARDING = "android.virtualization.PORT_FORWARDING"
136         private const val KEY_PORT = "port"
137         private const val KEY_ENABLED = "enabled"
138     }
139 }
140