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