Implement package exemptions for Low Power Standby
Packages listed in the Low Power Standby policy are now subject to Low Power Standby restrictions. Bug: 234002812 Test: atest LowPowerStandbyControllerTest LowPowerStandbyTest Change-Id: Iab20d7bfe6a6f4a36eb4d63a1bab18b275832513
This commit is contained in:
@@ -41,6 +41,7 @@ import android.os.PowerManager.LowPowerStandbyPolicy;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
@@ -133,7 +134,7 @@ public class LowPowerStandbyController {
|
||||
@GuardedBy("mLock")
|
||||
private boolean mEnableCustomPolicy;
|
||||
|
||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
private final BroadcastReceiver mIdleBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
switch (intent.getAction()) {
|
||||
@@ -150,6 +151,41 @@ public class LowPowerStandbyController {
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mPackageBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Received package intent: action=" + intent.getAction() + ", data="
|
||||
+ intent.getData());
|
||||
}
|
||||
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
|
||||
if (replacing) {
|
||||
return;
|
||||
}
|
||||
final Uri intentUri = intent.getData();
|
||||
final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
|
||||
: null;
|
||||
synchronized (mLock) {
|
||||
final LowPowerStandbyPolicy policy = getPolicy();
|
||||
if (policy.getExemptPackages().contains(packageName)) {
|
||||
enqueueNotifyAllowlistChangedLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Received user intent: action=" + intent.getAction());
|
||||
}
|
||||
synchronized (mLock) {
|
||||
enqueueNotifyAllowlistChangedLocked();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private AlarmManager mAlarmManager;
|
||||
@GuardedBy("mLock")
|
||||
@@ -599,11 +635,25 @@ public class LowPowerStandbyController {
|
||||
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
|
||||
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
|
||||
mContext.registerReceiver(mIdleBroadcastReceiver, intentFilter);
|
||||
|
||||
IntentFilter packageFilter = new IntentFilter();
|
||||
packageFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||
mContext.registerReceiver(mPackageBroadcastReceiver, packageFilter);
|
||||
|
||||
final IntentFilter userFilter = new IntentFilter();
|
||||
userFilter.addAction(Intent.ACTION_USER_ADDED);
|
||||
userFilter.addAction(Intent.ACTION_USER_REMOVED);
|
||||
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
|
||||
}
|
||||
|
||||
private void unregisterBroadcastReceiver() {
|
||||
mContext.unregisterReceiver(mBroadcastReceiver);
|
||||
mContext.unregisterReceiver(mIdleBroadcastReceiver);
|
||||
mContext.unregisterReceiver(mPackageBroadcastReceiver);
|
||||
mContext.unregisterReceiver(mUserReceiver);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@@ -814,7 +864,10 @@ public class LowPowerStandbyController {
|
||||
|
||||
int policyAllowedReasonsChanged = policyA.getAllowedReasons() ^ policyB.getAllowedReasons();
|
||||
|
||||
return (policyAllowedReasonsChanged & allowedReasonsInUse) != 0;
|
||||
boolean exemptPackagesChanged = !policyA.getExemptPackages().equals(
|
||||
policyB.getExemptPackages());
|
||||
|
||||
return (policyAllowedReasonsChanged & allowedReasonsInUse) != 0 || exemptPackagesChanged;
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw) {
|
||||
@@ -1033,9 +1086,12 @@ public class LowPowerStandbyController {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int[] getAllowlistUidsLocked() {
|
||||
final UserManager userManager = mContext.getSystemService(UserManager.class);
|
||||
final List<UserHandle> userHandles = userManager.getUserHandles(true);
|
||||
final ArraySet<Integer> uids = new ArraySet<>(mUidAllowedReasons.size());
|
||||
final LowPowerStandbyPolicy policy = getPolicy();
|
||||
|
||||
final int policyAllowedReasons = getPolicy().getAllowedReasons();
|
||||
final int policyAllowedReasons = policy.getAllowedReasons();
|
||||
for (int i = 0; i < mUidAllowedReasons.size(); i++) {
|
||||
Integer uid = mUidAllowedReasons.keyAt(i);
|
||||
if ((mUidAllowedReasons.valueAt(i) & policyAllowedReasons) != 0) {
|
||||
@@ -1043,6 +1099,12 @@ public class LowPowerStandbyController {
|
||||
}
|
||||
}
|
||||
|
||||
for (int appId : getExemptPackageAppIdsLocked()) {
|
||||
for (int uid : uidsForAppId(appId, userHandles)) {
|
||||
uids.add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
int[] allowlistUids = new int[uids.size()];
|
||||
for (int i = 0; i < uids.size(); i++) {
|
||||
allowlistUids[i] = uids.valueAt(i);
|
||||
@@ -1051,6 +1113,15 @@ public class LowPowerStandbyController {
|
||||
return allowlistUids;
|
||||
}
|
||||
|
||||
private int[] uidsForAppId(int appUid, List<UserHandle> userHandles) {
|
||||
final int appId = UserHandle.getAppId(appUid);
|
||||
final int[] uids = new int[userHandles.size()];
|
||||
for (int i = 0; i < userHandles.size(); i++) {
|
||||
uids[i] = userHandles.get(i).getUid(appId);
|
||||
}
|
||||
return uids;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void enqueueNotifyAllowlistChangedLocked() {
|
||||
final long now = mClock.elapsedRealtime();
|
||||
|
||||
@@ -33,18 +33,25 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.LowPowerStandbyPolicy;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.test.TestLooper;
|
||||
import android.provider.Settings;
|
||||
import android.test.mock.MockContentResolver;
|
||||
@@ -69,6 +76,7 @@ import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -79,7 +87,12 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class LowPowerStandbyControllerTest {
|
||||
private static final int STANDBY_TIMEOUT = 5000;
|
||||
|
||||
private static final String TEST_PKG1 = "PKG1";
|
||||
private static final String TEST_PKG2 = "PKG2";
|
||||
private static final int TEST_PKG1_APP_ID = 123;
|
||||
private static final int TEST_PKG2_APP_ID = 456;
|
||||
private static final int USER_ID_1 = 0;
|
||||
private static final int USER_ID_2 = 10;
|
||||
private static final LowPowerStandbyPolicy EMPTY_POLICY = new LowPowerStandbyPolicy(
|
||||
"Test policy", Collections.emptySet(), 0, Collections.emptySet());
|
||||
|
||||
@@ -95,6 +108,10 @@ public class LowPowerStandbyControllerTest {
|
||||
@Mock
|
||||
private AlarmManager mAlarmManagerMock;
|
||||
@Mock
|
||||
private PackageManager mPackageManagerMock;
|
||||
@Mock
|
||||
private UserManager mUserManagerMock;
|
||||
@Mock
|
||||
private IPowerManager mIPowerManagerMock;
|
||||
@Mock
|
||||
private PowerManagerInternal mPowerManagerInternalMock;
|
||||
@@ -106,7 +123,9 @@ public class LowPowerStandbyControllerTest {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContextSpy = spy(new BroadcastInterceptingContext(InstrumentationRegistry.getContext()));
|
||||
when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock);
|
||||
when(mContextSpy.getSystemService(AlarmManager.class)).thenReturn(mAlarmManagerMock);
|
||||
when(mContextSpy.getSystemService(UserManager.class)).thenReturn(mUserManagerMock);
|
||||
PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null, null);
|
||||
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
|
||||
addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
|
||||
@@ -132,6 +151,11 @@ public class LowPowerStandbyControllerTest {
|
||||
cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
||||
when(mContextSpy.getContentResolver()).thenReturn(cr);
|
||||
|
||||
when(mUserManagerMock.getUserHandles(true)).thenReturn(List.of(
|
||||
UserHandle.of(USER_ID_1), UserHandle.of(USER_ID_2)));
|
||||
when(mPackageManagerMock.getPackageUid(eq(TEST_PKG1), any())).thenReturn(TEST_PKG1_APP_ID);
|
||||
when(mPackageManagerMock.getPackageUid(eq(TEST_PKG2), any())).thenReturn(TEST_PKG2_APP_ID);
|
||||
|
||||
mClock = new OffsettableClock.Stopped();
|
||||
mTestLooper = new TestLooper(mClock::now);
|
||||
|
||||
@@ -535,6 +559,110 @@ public class LowPowerStandbyControllerTest {
|
||||
assertFalse(mController.isAllowed(LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetExemptPackages_uidPerUserIsExempt() throws Exception {
|
||||
mController.systemReady();
|
||||
mController.setEnabled(true);
|
||||
mController.setPolicy(policyWithExemptPackages(TEST_PKG1, TEST_PKG2));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
int[] expectedUidAllowlist = {
|
||||
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
|
||||
UserHandle.getUid(USER_ID_1, TEST_PKG2_APP_ID),
|
||||
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
|
||||
UserHandle.getUid(USER_ID_2, TEST_PKG2_APP_ID)
|
||||
};
|
||||
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
|
||||
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExemptPackageIsRemoved_servicesAreNotified() throws Exception {
|
||||
mController.systemReady();
|
||||
mController.setEnabled(true);
|
||||
mController.setPolicy(policyWithExemptPackages(TEST_PKG1));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
int[] expectedUidAllowlist = {
|
||||
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
|
||||
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
|
||||
};
|
||||
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
|
||||
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
|
||||
verifyNoMoreInteractions(mPowerManagerInternalMock, mNetworkPolicyManagerInternalMock);
|
||||
|
||||
reset(mPackageManagerMock);
|
||||
when(mPackageManagerMock.getPackageUid(eq(TEST_PKG1), any()))
|
||||
.thenThrow(PackageManager.NameNotFoundException.class);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
|
||||
intent.setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, TEST_PKG1, null));
|
||||
intent.putExtra(Intent.EXTRA_REPLACING, false);
|
||||
mContextSpy.sendBroadcast(intent);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[0]);
|
||||
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsersChanged_packagesExemptForNewUser() throws Exception {
|
||||
mController.systemReady();
|
||||
mController.setEnabled(true);
|
||||
mController.setPolicy(policyWithExemptPackages(TEST_PKG1));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
InOrder inOrder = inOrder(mPowerManagerInternalMock);
|
||||
|
||||
inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
|
||||
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
|
||||
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
|
||||
});
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
when(mUserManagerMock.getUserHandles(true)).thenReturn(List.of(UserHandle.of(USER_ID_1)));
|
||||
Intent intent = new Intent(Intent.ACTION_USER_REMOVED);
|
||||
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(USER_ID_2));
|
||||
mContextSpy.sendBroadcast(intent);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
|
||||
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID)
|
||||
});
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
when(mUserManagerMock.getUserHandles(true)).thenReturn(
|
||||
List.of(UserHandle.of(USER_ID_1), UserHandle.of(USER_ID_2)));
|
||||
intent = new Intent(Intent.ACTION_USER_ADDED);
|
||||
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(USER_ID_2));
|
||||
mContextSpy.sendBroadcast(intent);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{
|
||||
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
|
||||
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID)
|
||||
});
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsExempt_exemptIfDisabled() throws Exception {
|
||||
mController.systemReady();
|
||||
mController.setEnabled(false);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertTrue(mController.isPackageExempt(TEST_PKG1_APP_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsExempt_notExemptIfEnabled() throws Exception {
|
||||
mController.systemReady();
|
||||
mController.setEnabled(true);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
assertFalse(mController.isPackageExempt(TEST_PKG1_APP_ID));
|
||||
}
|
||||
|
||||
private void setInteractive() throws Exception {
|
||||
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
|
||||
mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
|
||||
@@ -568,6 +696,15 @@ public class LowPowerStandbyControllerTest {
|
||||
);
|
||||
}
|
||||
|
||||
private LowPowerStandbyPolicy policyWithExemptPackages(String... exemptPackages) {
|
||||
return new LowPowerStandbyPolicy(
|
||||
"Test policy",
|
||||
new ArraySet<>(exemptPackages),
|
||||
0,
|
||||
Collections.emptySet()
|
||||
);
|
||||
}
|
||||
|
||||
private void advanceTime(long timeMs) {
|
||||
mClock.fastForward(timeMs);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
Reference in New Issue
Block a user