Make AutoTileManager lisen for component enable/disable

This will allow the Safety Tile to be shown/hidden.

Test: atest AutoTileManagerTest,SafetyControllerTest
Bug: 222127376
Change-Id: Iad666a9aafe74092871ca18fc8b1973a68e1312b
This commit is contained in:
Nate Myren
2022-03-15 11:04:36 -07:00
parent 19412da261
commit 65084a4822
6 changed files with 348 additions and 7 deletions

View File

@@ -490,5 +490,4 @@ public class FrameworkServicesModule {
static SafetyCenterManager provideSafetyCenterManager(Context context) {
return context.getSystemService(SafetyCenterManager.class);
}
}

View File

@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.SafetyController;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
@@ -66,6 +67,7 @@ public interface QSModule {
ReduceBrightColorsController reduceBrightColorsController,
DeviceControlsController deviceControlsController,
WalletController walletController,
SafetyController safetyController,
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
AutoTileManager manager = new AutoTileManager(
context,
@@ -81,6 +83,7 @@ public interface QSModule {
reduceBrightColorsController,
deviceControlsController,
walletController,
safetyController,
isReduceBrightColorsAvailable
);
manager.init();

View File

@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
import com.android.systemui.statusbar.policy.SafetyController;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.UserAwareController;
import com.android.systemui.util.settings.SecureSettings;
@@ -83,6 +84,7 @@ public class AutoTileManager implements UserAwareController {
private final DeviceControlsController mDeviceControlsController;
private final WalletController mWalletController;
private final ReduceBrightColorsController mReduceBrightColorsController;
private final SafetyController mSafetyController;
private final boolean mIsReduceBrightColorsAvailable;
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
@@ -98,6 +100,7 @@ public class AutoTileManager implements UserAwareController {
ReduceBrightColorsController reduceBrightColorsController,
DeviceControlsController deviceControlsController,
WalletController walletController,
SafetyController safetyController,
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
mContext = context;
mHost = host;
@@ -114,6 +117,7 @@ public class AutoTileManager implements UserAwareController {
mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
mDeviceControlsController = deviceControlsController;
mWalletController = walletController;
mSafetyController = safetyController;
String safetySpecRes;
try {
safetySpecRes = context.getResources().getString(R.string.safety_quick_settings_tile);
@@ -163,8 +167,11 @@ public class AutoTileManager implements UserAwareController {
if (!mAutoTracker.isAdded(WALLET)) {
initWalletController();
}
if (mSafetySpec != null && !mAutoTracker.isAdded(mSafetySpec)) {
initSafetyTile();
if (mSafetySpec != null) {
if (!mAutoTracker.isAdded(mSafetySpec)) {
initSafetyTile();
}
mSafetyController.addCallback(mSafetyCallback);
}
int settingsN = mAutoAddSettingList.size();
@@ -187,6 +194,9 @@ public class AutoTileManager implements UserAwareController {
}
mCastController.removeCallback(mCastCallback);
mDeviceControlsController.removeCallback();
if (mSafetySpec != null) {
mSafetyController.removeCallback(mSafetyCallback);
}
int settingsN = mAutoAddSettingList.size();
for (int i = 0; i < settingsN; i++) {
mAutoAddSettingList.get(i).setListening(false);
@@ -327,10 +337,9 @@ public class AutoTileManager implements UserAwareController {
}
private void initSafetyTile() {
if (mSafetySpec == null) {
if (mSafetySpec == null || mAutoTracker.isAdded(mSafetySpec)) {
return;
}
if (mAutoTracker.isAdded(mSafetySpec)) return;
mHost.addTile(CustomTile.getComponentFromSpec(mSafetySpec), true);
mAutoTracker.setTileAdded(mSafetySpec);
}
@@ -402,6 +411,23 @@ public class AutoTileManager implements UserAwareController {
}
};
@VisibleForTesting
final SafetyController.Listener mSafetyCallback = new SafetyController.Listener() {
@Override
public void onSafetyCenterEnableChanged(boolean isSafetyCenterEnabled) {
if (mSafetySpec == null) {
return;
}
if (isSafetyCenterEnabled && !mAutoTracker.isAdded(mSafetySpec)) {
initSafetyTile();
} else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
mHost.removeTile(CustomTile.getComponentFromSpec(mSafetySpec));
mHost.unmarkTileAsAutoAdded(mSafetySpec);
}
}
};
@VisibleForTesting
protected SettingObserver getSecureSettingForKey(String key) {
for (SettingObserver s : mAutoAddSettingList) {

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.policy;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.safetycenter.SafetyCenterManager;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Background;
import java.util.ArrayList;
import java.util.Objects;
import javax.inject.Inject;
/**
* Controller which calls listeners when a PACKAGE_CHANGED broadcast is sent for the
* PermissionController. These broadcasts may be because the PermissionController enabled or
* disabled its TileService, and the tile should be added if the component was enabled, or removed
* if disabled.
*/
public class SafetyController implements
CallbackController<SafetyController.Listener> {
private boolean mSafetyCenterEnabled;
private final Handler mBgHandler;
private final ArrayList<Listener> mListeners = new ArrayList<>();
private static final IntentFilter PKG_CHANGE_INTENT_FILTER;
private final Context mContext;
private final SafetyCenterManager mSafetyCenterManager;
private final PackageManager mPackageManager;
static {
PKG_CHANGE_INTENT_FILTER = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
PKG_CHANGE_INTENT_FILTER.addDataScheme("package");
}
@Inject
public SafetyController(Context context, PackageManager pm, SafetyCenterManager scm,
@Background Handler bgHandler) {
mContext = context;
mSafetyCenterManager = scm;
mPackageManager = pm;
mBgHandler = bgHandler;
mSafetyCenterEnabled = mSafetyCenterManager.isSafetyCenterEnabled();
}
public boolean isSafetyCenterEnabled() {
return mSafetyCenterEnabled;
}
/**
* Adds a listener, registering the broadcast receiver if need be. Immediately calls the
* provided listener on the calling thread.
*/
@Override
public void addCallback(@NonNull Listener listener) {
synchronized (mListeners) {
mListeners.add(listener);
if (mListeners.size() == 1) {
mContext.registerReceiver(mPermControllerChangeReceiver, PKG_CHANGE_INTENT_FILTER);
mBgHandler.post(() -> {
mSafetyCenterEnabled = mSafetyCenterManager.isSafetyCenterEnabled();
listener.onSafetyCenterEnableChanged(isSafetyCenterEnabled());
});
} else {
listener.onSafetyCenterEnableChanged(isSafetyCenterEnabled());
}
}
}
@Override
public void removeCallback(@NonNull Listener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
if (mListeners.isEmpty()) {
mContext.unregisterReceiver(mPermControllerChangeReceiver);
}
}
}
private void handleSafetyCenterEnableChange() {
synchronized (mListeners) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onSafetyCenterEnableChanged(mSafetyCenterEnabled);
}
}
}
/**
* Upon receiving a package changed broadcast for the PermissionController, checks if the
* safety center is enabled or disabled, and sends an update on the main thread if the state
* changed.
*/
@VisibleForTesting
final BroadcastReceiver mPermControllerChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String packageName = intent.getData() != null ? intent.getData().getSchemeSpecificPart()
: null;
if (!Objects.equals(packageName,
mPackageManager.getPermissionControllerPackageName())) {
return;
}
boolean wasSafetyCenterEnabled = mSafetyCenterEnabled;
mSafetyCenterEnabled = mSafetyCenterManager.isSafetyCenterEnabled();
if (wasSafetyCenterEnabled == mSafetyCenterEnabled) {
return;
}
mBgHandler.post(() -> handleSafetyCenterEnableChange());
}
};
/**
* Listener for safety center enabled changes
*/
public interface Listener {
/**
* Callback to be notified when the safety center is enabled or disabled
* @param isSafetyCenterEnabled If the safety center is enabled
*/
void onSafetyCenterEnableChanged(boolean isSafetyCenterEnabled);
}
}

View File

@@ -58,6 +58,7 @@ import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.SafetyController;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -101,6 +102,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
@Mock private ReduceBrightColorsController mReduceBrightColorsController;
@Mock private DeviceControlsController mDeviceControlsController;
@Mock private WalletController mWalletController;
@Mock private SafetyController mSafetyController;
@Mock(answer = Answers.RETURNS_SELF)
private AutoAddTracker.Builder mAutoAddTrackerBuilder;
@Mock private Context mUserContext;
@@ -150,6 +152,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
ReduceBrightColorsController reduceBrightColorsController,
DeviceControlsController deviceControlsController,
WalletController walletController,
SafetyController safetyController,
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost,
Handler.createAsync(TestableLooper.get(this).getLooper()),
@@ -162,6 +165,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
reduceBrightColorsController,
deviceControlsController,
walletController,
safetyController,
isReduceBrightColorsAvailable);
}
@@ -169,7 +173,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
mDataSaverController, mManagedProfileController, mNightDisplayListener,
mCastController, mReduceBrightColorsController, mDeviceControlsController,
mWalletController, mIsReduceBrightColorsAvailable);
mWalletController, mSafetyController, mIsReduceBrightColorsAvailable);
}
@Test
@@ -185,10 +189,11 @@ public class AutoTileManagerTest extends SysuiTestCase {
ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
DeviceControlsController dCC = mock(DeviceControlsController.class);
WalletController wC = mock(WalletController.class);
SafetyController sC = mock(SafetyController.class);
AutoTileManager manager =
createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC,
dCC, wC, true);
dCC, wC, sC, true);
verify(tracker, never()).initialize();
verify(hC, never()).addCallback(any());
@@ -199,6 +204,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(rBC, never()).addCallback(any());
verify(dCC, never()).setCallback(any());
verify(wC, never()).getWalletPosition();
verify(sC, never()).addCallback(any());
assertNull(manager.getSecureSettingForKey(TEST_SETTING));
assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
}
@@ -253,6 +259,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(mWalletController, times(2)).getWalletPosition();
InOrder inOrderSafety = inOrder(mSafetyController);
inOrderSafety.verify(mSafetyController).removeCallback(any());
inOrderSafety.verify(mSafetyController).addCallback(any());
SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser());
assertTrue(setting.isListening());
@@ -303,6 +313,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(mWalletController, times(2)).getWalletPosition();
InOrder inOrderSafety = inOrder(mSafetyController);
inOrderSafety.verify(mSafetyController).removeCallback(any());
inOrderSafety.verify(mSafetyController).addCallback(any());
SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser());
assertFalse(setting.isListening());
@@ -457,6 +471,26 @@ public class AutoTileManagerTest extends SysuiTestCase {
mAutoTileManager.changeUser(UserHandle.of(USER + 1));
verify(mQsTileHost, times(2)).addTile(safetyComponent, true);
}
@Test
public void testSafetyTileRemoved_onSafetyCenterDisable() {
ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
mAutoTileManager.init();
when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
verify(mQsTileHost, times(1)).removeTile(safetyComponent);
}
@Test
public void testSafetyTileAdded_onSafetyCenterEnable() {
ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
mAutoTileManager.init();
verify(mQsTileHost, times(1)).addTile(safetyComponent, true);
mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(true);
verify(mQsTileHost, times(2)).addTile(safetyComponent, true);
}
@Test
public void testEmptyArray_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.policy
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Handler
import android.safetycenter.SafetyCenterManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.same
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
@SmallTest
@RunWith(AndroidTestingRunner::class)
class SafetyControllerTest : SysuiTestCase() {
private val TEST_PC_PKG = "testPermissionControllerPackageName"
private val OTHER_PKG = "otherPackageName"
@Mock
private lateinit var context: Context
@Mock
private lateinit var scm: SafetyCenterManager
@Mock
private lateinit var pm: PackageManager
@Mock
private lateinit var handler: Handler
@Mock
private lateinit var listener: SafetyController.Listener
private val packageDataScheme = "package"
private lateinit var controller: SafetyController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(pm.permissionControllerPackageName).thenReturn(TEST_PC_PKG)
`when`(scm.isSafetyCenterEnabled).thenReturn(false)
`when`(handler.post(any(Runnable::class.java))).thenAnswer {
(it.arguments[0] as Runnable).run()
true
}
controller = SafetyController(context, pm, scm, handler)
}
@Test
fun addingFirstListenerRegistersReceiver() {
`when`(scm.isSafetyCenterEnabled).thenReturn(true)
controller.addCallback(listener)
verify(listener, times(1)).onSafetyCenterEnableChanged(true)
val filter = ArgumentCaptor.forClass(IntentFilter::class.java)
verify(context, times(1)).registerReceiver(
same(controller.mPermControllerChangeReceiver), filter.capture())
assertEquals(Intent.ACTION_PACKAGE_CHANGED, filter.value.getAction(0))
assertEquals(packageDataScheme, filter.value.getDataScheme(0))
}
@Test
fun removingLastListenerDeregistersReceiver() {
controller.addCallback(listener)
controller.removeCallback(listener)
verify(context, times(1)).unregisterReceiver(
eq(controller.mPermControllerChangeReceiver))
}
@Test
fun listenersCalledWhenBroadcastReceivedWithPCPackageAndStateChange() {
`when`(scm.isSafetyCenterEnabled).thenReturn(false)
controller.addCallback(listener)
reset(listener)
`when`(scm.isSafetyCenterEnabled).thenReturn(true)
val testIntent = Intent(Intent.ACTION_PACKAGE_CHANGED)
testIntent.data = Uri.parse("package:$TEST_PC_PKG")
controller.mPermControllerChangeReceiver.onReceive(context, testIntent)
verify(listener, times(1)).onSafetyCenterEnableChanged(true)
}
@Test
fun listenersNotCalledWhenBroadcastReceivedWithOtherPackage() {
`when`(scm.isSafetyCenterEnabled).thenReturn(true)
controller.addCallback(listener)
reset(listener)
val testIntent = Intent(Intent.ACTION_PACKAGE_CHANGED)
testIntent.data = Uri.parse("package:$OTHER_PKG")
controller.mPermControllerChangeReceiver.onReceive(context, testIntent)
verify(listener, never()).onSafetyCenterEnableChanged(true)
}
@Test
fun listenersNotCalledWhenBroadcastReceivedWithNoStateChange() {
`when`(scm.isSafetyCenterEnabled).thenReturn(false)
controller.addCallback(listener)
reset(listener)
val testIntent = Intent(Intent.ACTION_PACKAGE_CHANGED)
testIntent.data = Uri.parse("package:$TEST_PC_PKG")
controller.mPermControllerChangeReceiver.onReceive(context, testIntent)
verify(listener, never()).onSafetyCenterEnableChanged(true)
}
}