Merge "Add broadcast receiver to listen for safety center flag changes" into tm-dev

This commit is contained in:
Ganesh Olekar
2022-03-28 18:32:53 +00:00
committed by Android (Google) Code Review
5 changed files with 103 additions and 46 deletions

View File

@@ -79,5 +79,6 @@
<permission name="android.permission.LOG_COMPAT_CHANGE" /> <permission name="android.permission.LOG_COMPAT_CHANGE" />
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<permission name="android.permission.READ_DEVICE_CONFIG" /> <permission name="android.permission.READ_DEVICE_CONFIG" />
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
</privapp-permissions> </privapp-permissions>
</permissions> </permissions>

View File

@@ -319,6 +319,9 @@
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- To read safety center status -->
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />

View File

@@ -61,6 +61,7 @@ import android.os.ServiceManager;
import android.os.UserManager; import android.os.UserManager;
import android.os.Vibrator; import android.os.Vibrator;
import android.permission.PermissionManager; import android.permission.PermissionManager;
import android.safetycenter.SafetyCenterManager;
import android.service.dreams.DreamService; import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager; import android.service.dreams.IDreamManager;
import android.telecom.TelecomManager; import android.telecom.TelecomManager;
@@ -476,4 +477,11 @@ public class FrameworkServicesModule {
static SmartspaceManager provideSmartspaceManager(Context context) { static SmartspaceManager provideSmartspaceManager(Context context) {
return context.getSystemService(SmartspaceManager.class); return context.getSystemService(SmartspaceManager.class);
} }
@Provides
@Singleton
static SafetyCenterManager provideSafetyCenterManager(Context context) {
return context.getSystemService(SafetyCenterManager.class);
}
} }

View File

@@ -1,15 +1,19 @@
package com.android.systemui.qs package com.android.systemui.qs
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.permission.PermissionGroupUsage import android.permission.PermissionGroupUsage
import android.permission.PermissionManager import android.permission.PermissionManager
import android.provider.DeviceConfig import android.safetycenter.SafetyCenterManager
import android.view.View import android.view.View
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import com.android.internal.R import com.android.internal.R
import com.android.internal.logging.UiEventLogger import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.appops.AppOpsController import com.android.systemui.appops.AppOpsController
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyChipEvent import com.android.systemui.privacy.PrivacyChipEvent
@@ -18,7 +22,6 @@ import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.util.DeviceConfigProxy
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
@@ -50,15 +53,11 @@ class HeaderPrivacyIconsController @Inject constructor(
@Main private val uiExecutor: Executor, @Main private val uiExecutor: Executor,
private val activityStarter: ActivityStarter, private val activityStarter: ActivityStarter,
private val appOpsController: AppOpsController, private val appOpsController: AppOpsController,
private val deviceConfigProxy: DeviceConfigProxy private val broadcastDispatcher: BroadcastDispatcher,
private val safetyCenterManager: SafetyCenterManager
) { ) {
companion object {
const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
}
var chipVisibilityListener: ChipVisibilityListener? = null var chipVisibilityListener: ChipVisibilityListener? = null
private var listening = false private var listening = false
private var micCameraIndicatorsEnabled = false private var micCameraIndicatorsEnabled = false
private var locationIndicatorsEnabled = false private var locationIndicatorsEnabled = false
@@ -68,20 +67,40 @@ class HeaderPrivacyIconsController @Inject constructor(
private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone) private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location) private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
private val devicePropertiesChangedListener = private val safetyCenterReceiver = object : BroadcastReceiver() {
object : DeviceConfig.OnPropertiesChangedListener { override fun onReceive(context: Context, intent: Intent) {
override fun onPropertiesChanged(properties: DeviceConfig.Properties) { safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
safetyCenterEnabled = properties.getBoolean(SAFETY_CENTER_ENABLED, false) }
} }
val attachStateChangeListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
broadcastDispatcher.registerReceiver(
safetyCenterReceiver,
IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
executor = backgroundExecutor
)
} }
override fun onViewDetachedFromWindow(v: View) {
broadcastDispatcher.unregisterReceiver(safetyCenterReceiver)
}
}
init { init {
safetyCenterEnabled = deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, backgroundExecutor.execute {
SAFETY_CENTER_ENABLED, false) safetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled()
deviceConfigProxy.addOnPropertiesChangedListener( }
DeviceConfig.NAMESPACE_PRIVACY,
uiExecutor, if (privacyChip.isAttachedToWindow()) {
devicePropertiesChangedListener) broadcastDispatcher.registerReceiver(
safetyCenterReceiver,
IntentFilter(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
executor = backgroundExecutor
)
}
privacyChip.addOnAttachStateChangeListener(attachStateChangeListener)
} }
private val picCallback: PrivacyItemController.Callback = private val picCallback: PrivacyItemController.Callback =

View File

@@ -1,14 +1,18 @@
package com.android.systemui.qs package com.android.systemui.qs
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.permission.PermissionManager import android.permission.PermissionManager
import android.provider.DeviceConfig import android.safetycenter.SafetyCenterManager
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.view.View import android.view.View
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpsController import com.android.systemui.appops.AppOpsController
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyDialogController import com.android.systemui.privacy.PrivacyDialogController
@@ -16,30 +20,30 @@ import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
private fun <T> any(): T = Mockito.any<T>()
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
class HeaderPrivacyIconsControllerTest : SysuiTestCase() { class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
companion object {
const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
}
@Mock @Mock
private lateinit var privacyItemController: PrivacyItemController private lateinit var privacyItemController: PrivacyItemController
@Mock @Mock
@@ -55,14 +59,16 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var permissionManager: PermissionManager private lateinit var permissionManager: PermissionManager
@Mock @Mock
private lateinit var backgroundExecutor: Executor
@Mock
private lateinit var activityStarter: ActivityStarter private lateinit var activityStarter: ActivityStarter
@Mock @Mock
private lateinit var appOpsController: AppOpsController private lateinit var appOpsController: AppOpsController
@Mock
private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock
private lateinit var safetyCenterManager: SafetyCenterManager
private val uiExecutor = FakeExecutor(FakeSystemClock()) private val uiExecutor = FakeExecutor(FakeSystemClock())
private lateinit var deviceConfigProxy: DeviceConfigProxy private val backgroundExecutor = FakeExecutor(FakeSystemClock())
private lateinit var cameraSlotName: String private lateinit var cameraSlotName: String
private lateinit var microphoneSlotName: String private lateinit var microphoneSlotName: String
private lateinit var locationSlotName: String private lateinit var locationSlotName: String
@@ -73,11 +79,11 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
whenever(privacyChip.context).thenReturn(context) whenever(privacyChip.context).thenReturn(context)
whenever(privacyChip.resources).thenReturn(context.resources) whenever(privacyChip.resources).thenReturn(context.resources)
whenever(privacyChip.isAttachedToWindow).thenReturn(true)
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera) cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone) microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
locationSlotName = context.getString(com.android.internal.R.string.status_bar_location) locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
deviceConfigProxy = DeviceConfigProxyFake()
controller = HeaderPrivacyIconsController( controller = HeaderPrivacyIconsController(
privacyItemController, privacyItemController,
@@ -91,8 +97,11 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
uiExecutor, uiExecutor,
activityStarter, activityStarter,
appOpsController, appOpsController,
deviceConfigProxy broadcastDispatcher,
safetyCenterManager
) )
backgroundExecutor.runAllReady()
} }
@Test @Test
@@ -141,19 +150,25 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
@Test @Test
fun testPrivacyChipClicked() { fun testPrivacyChipClicked() {
changeProperty(SAFETY_CENTER_ENABLED, false) whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
controller.onParentVisible() controller.onParentVisible()
val captor = argumentCaptor<View.OnClickListener>() val captor = argumentCaptor<View.OnClickListener>()
verify(privacyChip).setOnClickListener(capture(captor)) verify(privacyChip).setOnClickListener(capture(captor))
captor.value.onClick(privacyChip) captor.value.onClick(privacyChip)
verify(privacyDialogController).showDialog(any(Context::class.java)) verify(privacyDialogController).showDialog(any(Context::class.java))
} }
@Test @Test
fun testSafetyCenterFlag() { fun testSafetyCenterFlag() {
changeProperty(SAFETY_CENTER_ENABLED, true) val receiverCaptor = argumentCaptor<BroadcastReceiver>()
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
verify(broadcastDispatcher).registerReceiver(capture(receiverCaptor),
any(), any(), nullable(), anyInt())
receiverCaptor.value.onReceive(
context,
Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED)
)
backgroundExecutor.runAllReady()
controller.onParentVisible() controller.onParentVisible()
val captor = argumentCaptor<View.OnClickListener>() val captor = argumentCaptor<View.OnClickListener>()
verify(privacyChip).setOnClickListener(capture(captor)) verify(privacyChip).setOnClickListener(capture(captor))
@@ -161,18 +176,29 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
verify(privacyDialogController, never()).showDialog(any(Context::class.java)) verify(privacyDialogController, never()).showDialog(any(Context::class.java))
} }
@Test
fun testBroadcastReceiverUnregisteredWhenChipDetached() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
controller.attachStateChangeListener.onViewDetachedFromWindow(privacyChip)
backgroundExecutor.runAllReady()
val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>()
verify(broadcastDispatcher).unregisterReceiver(capture(broadcastReceiverCaptor))
}
@Test
fun testBroadcastReceiverRegisteredWhenChipAttached() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
controller.attachStateChangeListener.onViewAttachedToWindow(privacyChip)
backgroundExecutor.runAllReady()
val broadcastReceiverCaptor = argumentCaptor<BroadcastReceiver>()
val intentFilterCaptor = argumentCaptor<IntentFilter>()
// Broadcast receiver is registered on init and when privacy chip is attached
verify(broadcastDispatcher, times(2)).registerReceiver(capture(broadcastReceiverCaptor),
capture(intentFilterCaptor), any(), nullable(), anyInt())
}
private fun setPrivacyController(micCamera: Boolean, location: Boolean) { private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera) whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
whenever(privacyItemController.locationAvailable).thenReturn(location) whenever(privacyItemController.locationAvailable).thenReturn(location)
} }
private fun changeProperty(name: String, value: Boolean?) {
deviceConfigProxy.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
name,
value?.toString(),
false
)
uiExecutor.runAllReady()
}
} }