1 /*
2  * Copyright (C) 2022 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 
17 package com.android.systemui.flags
18 
19 import android.provider.DeviceConfig
20 import android.util.Log
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Background
23 import com.android.systemui.dagger.qualifiers.TestHarness
24 import com.android.systemui.util.DeviceConfigProxy
25 import dagger.Module
26 import dagger.Provides
27 import java.util.concurrent.Executor
28 import javax.inject.Inject
29 
30 interface ServerFlagReader {
31     /** Returns true if there is a server-side setting stored. */
hasOverridenull32     fun hasOverride(namespace: String, name: String): Boolean
33 
34     /** Returns any stored server-side setting or the default if not set. */
35     fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean
36     /** Register a listener for changes to any of the passed in flags. */
37     fun listenForChanges(values: Collection<Flag<*>>, listener: ChangeListener)
38 
39     interface ChangeListener {
40         fun onChange(flag: Flag<*>, value: String?)
41     }
42 }
43 
44 class ServerFlagReaderImpl @Inject constructor(
45     private val namespace: String,
46     private val deviceConfig: DeviceConfigProxy,
47     @Background private val executor: Executor,
48     @TestHarness private val isTestHarness: Boolean
49 ) : ServerFlagReader {
50 
51     private val TAG = "ServerFlagReader"
52 
53     private val listeners =
54         mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
55 
56     private val onPropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener {
onPropertiesChangednull57         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
58             if (isTestHarness) {
59                 Log.w(TAG, "Ignore server flag changes in Test Harness mode.")
60                 return
61             }
62             if (properties.namespace != namespace) {
63                 return
64             }
65 
66             for ((listener, flags) in listeners) {
67                 propLoop@ for (propName in properties.keyset) {
68                     for (flag in flags) {
69                         if (propName == flag.name) {
70                             listener.onChange(flag, properties.getString(propName, null))
71                             break@propLoop
72                         }
73                     }
74                 }
75             }
76         }
77     }
78 
hasOverridenull79     override fun hasOverride(namespace: String, name: String): Boolean =
80         !namespace.isBlank() && !name.isBlank() && deviceConfig.getProperty(
81             namespace,
82             name
83         ) != null
84 
85 
86     override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean =
87         !namespace.isBlank() && !name.isBlank() && deviceConfig.getBoolean(
88             namespace,
89             name,
90             default
91         )
92 
93     override fun listenForChanges(
94         flags: Collection<Flag<*>>,
95         listener: ServerFlagReader.ChangeListener
96     ) {
97         if (listeners.isEmpty()) {
98             deviceConfig.addOnPropertiesChangedListener(
99                 namespace,
100                 executor,
101                 onPropertiesChangedListener
102             )
103         }
104         listeners.add(Pair(listener, flags))
105     }
106 }
107 
108 @Module
109 interface ServerFlagReaderModule {
110     companion object {
111         private val SYSUI_NAMESPACE = "systemui"
112 
113         @JvmStatic
114         @Provides
115         @SysUISingleton
bindsReadernull116         fun bindsReader(
117             deviceConfig: DeviceConfigProxy,
118             @Background executor: Executor,
119             @TestHarness isTestHarness: Boolean
120         ): ServerFlagReader {
121             return ServerFlagReaderImpl(
122                 SYSUI_NAMESPACE, deviceConfig, executor, isTestHarness
123             )
124         }
125     }
126 }
127 
128 class ServerFlagReaderFake : ServerFlagReader {
129     private val flagMap: MutableMap<String, Boolean> = mutableMapOf()
130     private val listeners =
131         mutableListOf<Pair<ServerFlagReader.ChangeListener, Collection<Flag<*>>>>()
132 
hasOverridenull133     override fun hasOverride(namespace: String, name: String): Boolean {
134         return flagMap.containsKey(name)
135     }
136 
readServerOverridenull137     override fun readServerOverride(namespace: String, name: String, default: Boolean): Boolean {
138         return flagMap.getOrDefault(name, default)
139     }
140 
setFlagValuenull141     fun setFlagValue(namespace: String, name: String, value: Boolean) {
142         flagMap.put(name, value)
143 
144         for ((listener, flags) in listeners) {
145             flagLoop@ for (flag in flags) {
146                 if (name == flag.name) {
147                     listener.onChange(flag, if (value) "true" else "false")
148                     break@flagLoop
149                 }
150             }
151         }
152     }
153 
eraseFlagnull154     fun eraseFlag(namespace: String, name: String) {
155         flagMap.remove(name)
156     }
157 
listenForChangesnull158     override fun listenForChanges(
159         flags: Collection<Flag<*>>,
160         listener: ServerFlagReader.ChangeListener
161     ) {
162         listeners.add(Pair(listener, flags))
163     }
164 }
165