1 /* <lambda>null2 * 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.privacy 18 19 import android.provider.DeviceConfig 20 import com.android.internal.annotations.VisibleForTesting 21 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags 22 import com.android.systemui.Dumpable 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Main 25 import com.android.systemui.dump.DumpManager 26 import com.android.systemui.util.DeviceConfigProxy 27 import com.android.systemui.util.asIndenting 28 import com.android.systemui.util.annotations.WeaklyReferencedCallback 29 import com.android.systemui.util.concurrency.DelayableExecutor 30 import com.android.systemui.util.withIncreasedIndent 31 import java.io.PrintWriter 32 import java.lang.ref.WeakReference 33 import javax.inject.Inject 34 35 @SysUISingleton 36 class PrivacyConfig @Inject constructor( 37 @Main private val uiExecutor: DelayableExecutor, 38 private val deviceConfigProxy: DeviceConfigProxy, 39 dumpManager: DumpManager 40 ) : Dumpable { 41 42 @VisibleForTesting 43 internal companion object { 44 const val TAG = "PrivacyConfig" 45 private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED 46 private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED 47 private const val MEDIA_PROJECTION = 48 SystemUiDeviceConfigFlags.PROPERTY_MEDIA_PROJECTION_INDICATORS_ENABLED 49 private const val DEFAULT_MIC_CAMERA = true 50 private const val DEFAULT_LOCATION = false 51 private const val DEFAULT_MEDIA_PROJECTION = true 52 } 53 54 private val callbacks = mutableListOf<WeakReference<Callback>>() 55 56 var micCameraAvailable = isMicCameraEnabled() 57 private set 58 var locationAvailable = isLocationEnabled() 59 private set 60 var mediaProjectionAvailable = isMediaProjectionEnabled() 61 private set 62 63 private val devicePropertiesChangedListener = 64 DeviceConfig.OnPropertiesChangedListener { properties -> 65 if (DeviceConfig.NAMESPACE_PRIVACY == properties.namespace) { 66 // Running on the ui executor so can iterate on callbacks 67 if (properties.keyset.contains(MIC_CAMERA)) { 68 micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA) 69 callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } 70 } 71 72 if (properties.keyset.contains(LOCATION)) { 73 locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION) 74 callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) } 75 } 76 77 if (properties.keyset.contains(MEDIA_PROJECTION)) { 78 mediaProjectionAvailable = 79 properties.getBoolean(MEDIA_PROJECTION, DEFAULT_MEDIA_PROJECTION) 80 callbacks.forEach { 81 it.get()?.onFlagMediaProjectionChanged(mediaProjectionAvailable) 82 } 83 } 84 } 85 } 86 87 init { 88 dumpManager.registerDumpable(TAG, this) 89 deviceConfigProxy.addOnPropertiesChangedListener( 90 DeviceConfig.NAMESPACE_PRIVACY, 91 uiExecutor, 92 devicePropertiesChangedListener) 93 } 94 95 private fun isMicCameraEnabled(): Boolean { 96 return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, 97 MIC_CAMERA, DEFAULT_MIC_CAMERA) 98 } 99 100 private fun isLocationEnabled(): Boolean { 101 return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, 102 LOCATION, DEFAULT_LOCATION) 103 } 104 105 private fun isMediaProjectionEnabled(): Boolean { 106 return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, 107 MEDIA_PROJECTION, DEFAULT_MEDIA_PROJECTION) 108 } 109 110 fun addCallback(callback: Callback) { 111 addCallback(WeakReference(callback)) 112 } 113 114 fun removeCallback(callback: Callback) { 115 removeCallback(WeakReference(callback)) 116 } 117 118 private fun addCallback(callback: WeakReference<Callback>) { 119 uiExecutor.execute { 120 callbacks.add(callback) 121 } 122 } 123 124 private fun removeCallback(callback: WeakReference<Callback>) { 125 uiExecutor.execute { 126 // Removes also if the callback is null 127 callbacks.removeIf { it.get()?.equals(callback.get()) ?: true } 128 } 129 } 130 131 override fun dump(pw: PrintWriter, args: Array<out String>) { 132 val ipw = pw.asIndenting() 133 ipw.println("PrivacyConfig state:") 134 ipw.withIncreasedIndent { 135 ipw.println("micCameraAvailable: $micCameraAvailable") 136 ipw.println("locationAvailable: $locationAvailable") 137 ipw.println("mediaProjectionAvailable: $mediaProjectionAvailable") 138 ipw.println("Callbacks:") 139 ipw.withIncreasedIndent { 140 callbacks.forEach { callback -> 141 callback.get()?.let { ipw.println(it) } 142 } 143 } 144 } 145 ipw.flush() 146 } 147 148 @WeaklyReferencedCallback 149 interface Callback { 150 fun onFlagMicCameraChanged(flag: Boolean) {} 151 152 fun onFlagLocationChanged(flag: Boolean) {} 153 154 fun onFlagMediaProjectionChanged(flag: Boolean) {} 155 } 156 }