• 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.content.Context
19 import android.content.SharedPreferences
20 import com.android.internal.annotations.GuardedBy
21 import com.android.virtualization.terminal.proto.ActivePort
22 import java.util.HashSet
23 
24 /**
25  * PortsStateManager is responsible for communicating with shared preferences and managing state of
26  * ports.
27  */
28 class PortsStateManager private constructor(private val sharedPref: SharedPreferences) {
29     private val lock = Any()
30 
31     @GuardedBy("lock") private val activePorts: MutableMap<Int, ActivePort> = hashMapOf()
32 
33     @GuardedBy("lock")
34     private val enabledPorts: MutableSet<Int> =
35         sharedPref
36             .getAll()
37             .entries
38             .filterIsInstance<MutableMap.MutableEntry<String, Int>>()
<lambda>null39             .filter { it.value and FLAG_ENABLED == FLAG_ENABLED }
<lambda>null40             .map { it.key.toIntOrNull() }
41             .filterNotNull()
42             .toMutableSet()
43 
44     @GuardedBy("lock") private val listeners: MutableSet<Listener> = hashSetOf()
45 
getActivePortsnull46     fun getActivePorts(): Set<Int> {
47         synchronized(lock) {
48             return HashSet<Int>(activePorts.keys)
49         }
50     }
51 
getActivePortInfonull52     fun getActivePortInfo(port: Int): ActivePort? {
53         synchronized(lock) {
54             return activePorts[port]
55         }
56     }
57 
getEnabledPortsnull58     fun getEnabledPorts(): Set<Int> {
59         synchronized(lock) {
60             return HashSet<Int>(enabledPorts)
61         }
62     }
63 
updateActivePortsnull64     fun updateActivePorts(ports: List<ActivePort>) {
65         val oldPorts = getActivePorts()
66         synchronized(lock) {
67             activePorts.clear()
68             activePorts.putAll(ports.associateBy { it.port })
69         }
70         notifyPortsStateUpdated(oldPorts, getActivePorts())
71     }
72 
updateEnabledPortnull73     fun updateEnabledPort(port: Int, enabled: Boolean) {
74         synchronized(lock) {
75             val editor = sharedPref.edit()
76             editor.putInt(port.toString(), if (enabled) FLAG_ENABLED else 0)
77             editor.apply()
78             if (enabled) {
79                 enabledPorts.add(port)
80             } else {
81                 enabledPorts.remove(port)
82             }
83         }
84         notifyPortsStateUpdated(getActivePorts(), getActivePorts())
85     }
86 
clearEnabledPortsnull87     fun clearEnabledPorts() {
88         synchronized(lock) {
89             val editor = sharedPref.edit()
90             editor.clear()
91             editor.apply()
92             enabledPorts.clear()
93         }
94         notifyPortsStateUpdated(getActivePorts(), getActivePorts())
95     }
96 
registerListenernull97     fun registerListener(listener: Listener) {
98         synchronized(lock) { listeners.add(listener) }
99     }
100 
unregisterListenernull101     fun unregisterListener(listener: Listener) {
102         synchronized(lock) { listeners.remove(listener) }
103     }
104 
105     // TODO: it notifies when both enabledPort and activePort are changed, but doesn't provide
106     // enabledPort's value change. Make this callback provide that information as well.
notifyPortsStateUpdatednull107     private fun notifyPortsStateUpdated(oldActivePorts: Set<Int>, newActivePorts: Set<Int>) {
108         synchronized(lock) { HashSet<Listener>(this@PortsStateManager.listeners) }
109             .forEach {
110                 it.onPortsStateUpdated(HashSet<Int>(oldActivePorts), HashSet<Int>(newActivePorts))
111             }
112     }
113 
114     interface Listener {
onPortsStateUpdatednull115         fun onPortsStateUpdated(oldActivePorts: Set<Int>, newActivePorts: Set<Int>) {}
116     }
117 
118     companion object {
119         private const val PREFS_NAME = ".PORTS"
120         private const val FLAG_ENABLED = 1
121 
122         private var instance: PortsStateManager? = null
123 
124         @Synchronized
getInstancenull125         fun getInstance(context: Context): PortsStateManager {
126             if (instance == null) {
127                 val sharedPref =
128                     context.getSharedPreferences(
129                         context.getPackageName() + PREFS_NAME,
130                         Context.MODE_PRIVATE,
131                     )
132                 instance = PortsStateManager(sharedPref)
133             }
134             return instance!!
135         }
136     }
137 }
138