Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff11a6e2 authored by Ganesh Olekar's avatar Ganesh Olekar
Browse files

Add broadcast receiver to listen for safety center flag changes

Change-Id: I0a22edd85d10bdd1ccb6f26a48615e88a30e7cb3
Bug: 225294614
Test: atest com.android.systemui.qs.HeaderPrivacyIconsControllerTest -c
parent 4f5a02e5
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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>
+3 −0
Original line number Original line Diff line number Diff line
@@ -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" />
+8 −0
Original line number Original line Diff line number Diff line
@@ -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);
    }

}
}
+38 −19
Original line number Original line Diff line number Diff line
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 =
+53 −27
Original line number Original line Diff line number Diff line
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()
    }
}
}
 No newline at end of file