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

Commit 04f83eb5 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Convert PrivacyItemController to Dependency

This makes sure that PIC is a Singleton and prevents memory leak.

Bug: 121388507
Test: atest PrivacyItemControllerTest

Change-Id: Ib5c2a8790157034e1937c8037650ac047478d005
parent 3f7e6408
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.AmbientPulseManager;
@@ -278,6 +279,7 @@ public class Dependency extends SystemUI {
    @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
    @Inject Lazy<AutoHideController> mAutoHideController;
    @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
    @Inject Lazy<PrivacyItemController> mPrivacyItemController;
    @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
    @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
    @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
@@ -452,6 +454,8 @@ public class Dependency extends SystemUI {
        mProviders.put(ForegroundServiceNotificationListener.class,
                mForegroundServiceNotificationListener::get);
        mProviders.put(ClockManager.class, mClockManager::get);
        mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);


        // TODO(b/118592525): to support multi-display , we start to add something which is
        //                    per-display, while others may be global. I think it's time to add
+43 −5
Original line number Diff line number Diff line
@@ -30,8 +30,12 @@ import com.android.systemui.Dependency
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
import com.android.systemui.R
import java.lang.ref.WeakReference
import javax.inject.Inject
import javax.inject.Singleton

class PrivacyItemController(val context: Context, val callback: Callback) {
@Singleton
class PrivacyItemController @Inject constructor(val context: Context) {

    companion object {
        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
@@ -56,9 +60,10 @@ class PrivacyItemController(val context: Context, val callback: Callback) {
    private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
    private var listening = false
    val systemApp = PrivacyApplication(context.getString(R.string.device_services), context)
    private val callbacks = mutableListOf<WeakReference<Callback>>()

    private val notifyChanges = Runnable {
        callback.privacyChanged(privacyList)
        callbacks.forEach { it.get()?.privacyChanged(privacyList) }
    }

    private val updateListAndNotifyChanges = Runnable {
@@ -88,8 +93,8 @@ class PrivacyItemController(val context: Context, val callback: Callback) {
            registerReceiver()
        }

    init {
        registerReceiver()
    private fun unregisterReceiver() {
        context.unregisterReceiver(userSwitcherReceiver)
    }

    private fun registerReceiver() {
@@ -108,15 +113,39 @@ class PrivacyItemController(val context: Context, val callback: Callback) {
        bgHandler.post(updateListAndNotifyChanges)
    }

    fun setListening(listen: Boolean) {
    @VisibleForTesting
    internal fun setListening(listen: Boolean) {
        if (listening == listen) return
        listening = listen
        if (listening) {
            appOpsController.addCallback(OPS, cb)
            registerReceiver()
            update(true)
        } else {
            appOpsController.removeCallback(OPS, cb)
            unregisterReceiver()
        }
    }

    private fun addCallback(callback: WeakReference<Callback>) {
        callbacks.add(callback)
        if (callbacks.isNotEmpty() && !listening) setListening(true)
        // Notify this callback if we didn't set to listening
        else uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
    }

    private fun removeCallback(callback: WeakReference<Callback>) {
        // Removes also if the callback is null
        callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
        if (callbacks.isEmpty()) setListening(false)
    }

    fun addCallback(callback: Callback) {
        addCallback(WeakReference(callback))
    }

    fun removeCallback(callback: Callback) {
        removeCallback(WeakReference(callback))
    }

    private fun updatePrivacyList() {
@@ -149,4 +178,13 @@ class PrivacyItemController(val context: Context, val callback: Callback) {
            }
        }
    }

    private class NotifyChangesToCallback(
        private val callback: Callback?,
        private val list: List<PrivacyItem>
    ) : Runnable {
        override fun run() {
            callback?.privacyChanged(list)
        }
    }
}
 No newline at end of file
+4 −3
Original line number Diff line number Diff line
@@ -180,14 +180,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
            NextAlarmController nextAlarmController, ZenModeController zenModeController,
            BatteryController batteryController, StatusBarIconController statusBarIconController,
            ActivityStarter activityStarter) {
            ActivityStarter activityStarter, PrivacyItemController privacyItemController) {
        super(context, attrs);
        mAlarmController = nextAlarmController;
        mZenController = zenModeController;
        mBatteryController = batteryController;
        mStatusBarIconController = statusBarIconController;
        mActivityStarter = activityStarter;
        mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
        mPrivacyItemController = privacyItemController;
        mShownCount = getStoredShownCount();
    }

@@ -512,7 +512,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            return;
        }
        mHeaderQsPanel.setListening(listening);
        mPrivacyItemController.setListening(listening);
        mListening = listening;

        if (listening) {
@@ -520,9 +519,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            mAlarmController.addCallback(this);
            mContext.registerReceiver(mRingerReceiver,
                    new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
            mPrivacyItemController.addCallback(mPICCallback);
        } else {
            mZenController.removeCallback(this);
            mAlarmController.removeCallback(this);
            mPrivacyItemController.removeCallback(mPICCallback);
            mContext.unregisterReceiver(mRingerReceiver);
        }
    }
+3 −3
Original line number Diff line number Diff line
@@ -173,7 +173,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
        mProvisionedController = Dependency.get(DeviceProvisionedController.class);
        mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
        mLocationController = Dependency.get(LocationController.class);
        mPrivacyItemController = new PrivacyItemController(mContext, this);
        mPrivacyItemController = Dependency.get(PrivacyItemController.class);

        mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
        mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -266,7 +266,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
        mNextAlarmController.addCallback(mNextAlarmCallback);
        mDataSaver.addCallback(this);
        mKeyguardMonitor.addCallback(this);
        mPrivacyItemController.setListening(true);
        mPrivacyItemController.addCallback(this);

        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
@@ -294,7 +294,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks,
        mNextAlarmController.removeCallback(mNextAlarmCallback);
        mDataSaver.removeCallback(this);
        mKeyguardMonitor.removeCallback(this);
        mPrivacyItemController.setListening(false);
        mPrivacyItemController.removeCallback(this);
        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallback(this);
        mContext.unregisterReceiver(mIntentReceiver);

+69 −7
Original line number Diff line number Diff line
@@ -45,9 +45,12 @@ import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations

@RunWith(AndroidTestingRunner::class)
@@ -73,6 +76,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
    private lateinit var userManager: UserManager
    @Captor
    private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
    @Captor
    private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>

    private lateinit var testableLooper: TestableLooper
    private lateinit var privacyItemController: PrivacyItemController
@@ -95,18 +100,25 @@ class PrivacyItemControllerTest : SysuiTestCase() {
            }
        })).`when`(userManager).getProfiles(anyInt())

        privacyItemController = PrivacyItemController(mContext, callback)
        privacyItemController = PrivacyItemController(mContext)
    }

    @Test
    fun testSetListeningTrue() {
        privacyItemController.setListening(true)
    fun testSetListeningTrueByAddingCallback() {
        privacyItemController.addCallback(callback)
        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
                any(AppOpsController.Callback::class.java))
        testableLooper.processAllMessages()
        verify(callback).privacyChanged(anyList())
    }

    @Test
    fun testSetListeningTrue() {
        privacyItemController.setListening(true)
        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
                any(AppOpsController.Callback::class.java))
    }

    @Test
    fun testSetListeningFalse() {
        privacyItemController.setListening(true)
@@ -121,7 +133,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())

        privacyItemController.setListening(true)
        privacyItemController.addCallback(callback)
        testableLooper.processAllMessages()
        verify(callback).privacyChanged(capture(argCaptor))
        assertEquals(1, argCaptor.value.size)
@@ -131,7 +143,7 @@ class PrivacyItemControllerTest : SysuiTestCase() {
    fun testSystemApps() {
        doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
                0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
        privacyItemController.setListening(true)
        privacyItemController.addCallback(callback)
        testableLooper.processAllMessages()
        verify(callback).privacyChanged(capture(argCaptor))
        assertEquals(1, argCaptor.value.size)
@@ -142,8 +154,8 @@ class PrivacyItemControllerTest : SysuiTestCase() {
    @Test
    fun testRegisterReceiver_allUsers() {
        val spiedContext = spy(mContext)
        val itemController = PrivacyItemController(spiedContext, callback)

        val itemController = PrivacyItemController(spiedContext)
        itemController.setListening(true)
        verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
                eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
                eq(null))
@@ -170,4 +182,54 @@ class PrivacyItemControllerTest : SysuiTestCase() {
                Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
        verify(userManager).getProfiles(anyInt())
    }

    @Test
    fun testAddMultipleCallbacks() {
        val otherCallback = mock(PrivacyItemController.Callback::class.java)
        privacyItemController.addCallback(callback)
        testableLooper.processAllMessages()
        verify(callback).privacyChanged(anyList())

        privacyItemController.addCallback(otherCallback)
        testableLooper.processAllMessages()
        verify(otherCallback).privacyChanged(anyList())
        // Adding a callback should not unnecessarily call previous ones
        verifyNoMoreInteractions(callback)
    }

    @Test
    fun testMultipleCallbacksAreUpdated() {
        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())

        val otherCallback = mock(PrivacyItemController.Callback::class.java)
        privacyItemController.addCallback(callback)
        privacyItemController.addCallback(otherCallback)
        testableLooper.processAllMessages()
        reset(callback)
        reset(otherCallback)

        verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
        testableLooper.processAllMessages()
        verify(callback).privacyChanged(anyList())
        verify(otherCallback).privacyChanged(anyList())
    }

    @Test
    fun testRemoveCallback() {
        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
        val otherCallback = mock(PrivacyItemController.Callback::class.java)
        privacyItemController.addCallback(callback)
        privacyItemController.addCallback(otherCallback)
        testableLooper.processAllMessages()
        reset(callback)
        reset(otherCallback)

        verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
        privacyItemController.removeCallback(callback)
        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
        testableLooper.processAllMessages()
        verify(callback, never()).privacyChanged(anyList())
        verify(otherCallback).privacyChanged(anyList())
    }
}
 No newline at end of file