Merge "Extreme battery saver: AlarmManager"
This commit is contained in:
committed by
Android (Google) Code Review
commit
2dedff18cc
@@ -20,6 +20,7 @@ import "frameworks/base/core/proto/android/app/alarmmanager.proto";
|
||||
import "frameworks/base/core/proto/android/app/pendingintent.proto";
|
||||
import "frameworks/base/core/proto/android/internal/locallog.proto";
|
||||
import "frameworks/base/core/proto/android/os/worksource.proto";
|
||||
import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
|
||||
|
||||
package com.android.server;
|
||||
|
||||
@@ -32,10 +33,9 @@ message AlarmManagerServiceProto {
|
||||
optional int64 last_time_change_realtime = 4;
|
||||
// Current settings
|
||||
optional ConstantsProto settings = 5;
|
||||
// UIDs currently in the foreground.
|
||||
repeated int32 foreground_uids = 6;
|
||||
// Packages forced into app standby.
|
||||
repeated string forced_app_standby_packages = 7;
|
||||
|
||||
// Dump from ForceAppStandbyTracker.
|
||||
optional ForceAppStandbyTrackerProto force_app_standby_tracker = 6;
|
||||
|
||||
optional bool is_interactive = 8;
|
||||
// Only valid if is_interactive is false.
|
||||
|
||||
44
core/proto/android/server/forceappstandbytracker.proto
Normal file
44
core/proto/android/server/forceappstandbytracker.proto
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package com.android.server;
|
||||
|
||||
option java_multiple_files = true;
|
||||
|
||||
// Dump from ForceAppStandbyTracker.
|
||||
message ForceAppStandbyTrackerProto {
|
||||
// Whether all apps are forced standby or not.
|
||||
optional bool force_all_apps_standby = 1;
|
||||
|
||||
// UIDs currently in the foreground.
|
||||
repeated int32 foreground_uids = 2;
|
||||
|
||||
// App ids that are in power-save whitelist.
|
||||
repeated int32 power_save_whitelist_app_ids = 3;
|
||||
|
||||
// App ids that are in temporary power-save whitelist.
|
||||
repeated int32 temp_power_save_whitelist_app_ids = 4;
|
||||
|
||||
message RunAnyInBackgroundRestrictedPackages {
|
||||
optional int32 uid = 1;
|
||||
optional string package_name = 2;
|
||||
}
|
||||
|
||||
// Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
|
||||
repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
|
||||
}
|
||||
@@ -46,7 +46,6 @@ import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
@@ -56,7 +55,6 @@ import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.KeyValueListParser;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
@@ -82,6 +80,7 @@ import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||
import static android.app.AlarmManager.RTC;
|
||||
@@ -89,11 +88,17 @@ import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.LocalLog;
|
||||
import com.android.server.ForceAppStandbyTracker.Listener;
|
||||
|
||||
/**
|
||||
* Alarm manager implementaion.
|
||||
*
|
||||
* Unit test:
|
||||
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
|
||||
*/
|
||||
class AlarmManagerService extends SystemService {
|
||||
private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
|
||||
private static final int RTC_MASK = 1 << RTC;
|
||||
@@ -132,13 +137,10 @@ class AlarmManagerService extends SystemService {
|
||||
final LocalLog mLog = new LocalLog(TAG);
|
||||
|
||||
AppOpsManager mAppOps;
|
||||
IAppOpsService mAppOpsService;
|
||||
DeviceIdleController.LocalService mLocalDeviceIdleController;
|
||||
|
||||
final Object mLock = new Object();
|
||||
|
||||
ArraySet<String> mForcedAppStandbyPackages = new ArraySet<>();
|
||||
SparseBooleanArray mForegroundUids = new SparseBooleanArray();
|
||||
// List of alarms per uid deferred due to user applied background restrictions on the source app
|
||||
SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>();
|
||||
long mNativeData;
|
||||
@@ -184,12 +186,6 @@ class AlarmManagerService extends SystemService {
|
||||
*/
|
||||
int mSystemUiUid;
|
||||
|
||||
/**
|
||||
* The current set of user whitelisted apps for device idle mode, meaning these are allowed
|
||||
* to freely schedule alarms.
|
||||
*/
|
||||
int[] mDeviceIdleUserWhitelist = new int[0];
|
||||
|
||||
/**
|
||||
* For each uid, this is the last time we dispatched an "allow while idle" alarm,
|
||||
* used to determine the earliest we can dispatch the next such alarm. Times are in the
|
||||
@@ -224,6 +220,8 @@ class AlarmManagerService extends SystemService {
|
||||
private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
|
||||
new SparseArray<>();
|
||||
|
||||
private final ForceAppStandbyTracker mForceAppStandbyTracker;
|
||||
|
||||
/**
|
||||
* All times are in milliseconds. These constants are kept synchronized with the system
|
||||
* global Settings. Any access to this class or its fields should be done while
|
||||
@@ -758,6 +756,9 @@ class AlarmManagerService extends SystemService {
|
||||
public AlarmManagerService(Context context) {
|
||||
super(context);
|
||||
mConstants = new Constants(mHandler);
|
||||
|
||||
mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
|
||||
mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
|
||||
}
|
||||
|
||||
static long convertToElapsed(long when, int type) {
|
||||
@@ -895,17 +896,48 @@ class AlarmManagerService extends SystemService {
|
||||
deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
void sendPendingBackgroundAlarmsForAppIdLocked(int appId) {
|
||||
/**
|
||||
* Check all alarms in {@link #mPendingBackgroundAlarms} and send the ones that are not
|
||||
* restricted.
|
||||
*
|
||||
* This is only called when the global "force all apps-standby" flag changes or when the
|
||||
* power save whitelist changes, so it's okay to be slow.
|
||||
*/
|
||||
void sendAllUnrestrictedPendingBackgroundAlarmsLocked() {
|
||||
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
|
||||
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
|
||||
final int uid = mPendingBackgroundAlarms.keyAt(i);
|
||||
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
|
||||
if (UserHandle.getAppId(uid) == appId) {
|
||||
alarmsToDeliver.addAll(alarmsForUid);
|
||||
mPendingBackgroundAlarms.removeAt(i);
|
||||
|
||||
findAllUnrestrictedPendingBackgroundAlarmsLockedInner(
|
||||
mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted);
|
||||
|
||||
if (alarmsToDeliver.size() > 0) {
|
||||
deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void findAllUnrestrictedPendingBackgroundAlarmsLockedInner(
|
||||
SparseArray<ArrayList<Alarm>> pendingAlarms, ArrayList<Alarm> unrestrictedAlarms,
|
||||
Predicate<Alarm> isBackgroundRestricted) {
|
||||
|
||||
for (int uidIndex = pendingAlarms.size() - 1; uidIndex >= 0; uidIndex--) {
|
||||
final int uid = pendingAlarms.keyAt(uidIndex);
|
||||
final ArrayList<Alarm> alarmsForUid = pendingAlarms.valueAt(uidIndex);
|
||||
|
||||
for (int alarmIndex = alarmsForUid.size() - 1; alarmIndex >= 0; alarmIndex--) {
|
||||
final Alarm alarm = alarmsForUid.get(alarmIndex);
|
||||
|
||||
if (isBackgroundRestricted.test(alarm)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unrestrictedAlarms.add(alarm);
|
||||
alarmsForUid.remove(alarmIndex);
|
||||
}
|
||||
|
||||
if (alarmsForUid.size() == 0) {
|
||||
pendingAlarms.removeAt(uidIndex);
|
||||
}
|
||||
}
|
||||
deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
private void deliverPendingBackgroundAlarmsLocked(ArrayList<Alarm> alarms, long nowELAPSED) {
|
||||
@@ -1235,10 +1267,8 @@ class AlarmManagerService extends SystemService {
|
||||
} catch (RemoteException e) {
|
||||
// ignored; both services live in system_server
|
||||
}
|
||||
mAppOpsService = IAppOpsService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||
publishBinderService(Context.ALARM_SERVICE, mService);
|
||||
publishLocalService(LocalService.class, new LocalService());
|
||||
mForceAppStandbyTracker.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1248,13 +1278,6 @@ class AlarmManagerService extends SystemService {
|
||||
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
|
||||
mLocalDeviceIdleController
|
||||
= LocalServices.getService(DeviceIdleController.LocalService.class);
|
||||
try {
|
||||
mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
|
||||
new AppOpsWatcher());
|
||||
} catch (RemoteException rexc) {
|
||||
// Shouldn't happen as they are in the same process.
|
||||
Slog.e(TAG, "AppOps service not reachable", rexc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1583,8 +1606,7 @@ class AlarmManagerService extends SystemService {
|
||||
// timing restrictions.
|
||||
} else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
|
||||
|| callingUid == mSystemUiUid
|
||||
|| Arrays.binarySearch(mDeviceIdleUserWhitelist,
|
||||
UserHandle.getAppId(callingUid)) >= 0)) {
|
||||
|| mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
|
||||
flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
|
||||
flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
|
||||
}
|
||||
@@ -1661,24 +1683,14 @@ class AlarmManagerService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
public final class LocalService {
|
||||
public void setDeviceIdleUserWhitelist(int[] appids) {
|
||||
setDeviceIdleUserWhitelistImpl(appids);
|
||||
}
|
||||
}
|
||||
|
||||
void dumpImpl(PrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
pw.println("Current Alarm Manager state:");
|
||||
mConstants.dump(pw);
|
||||
pw.println();
|
||||
|
||||
pw.print(" Foreground uids: [");
|
||||
for (int i = 0; i < mForegroundUids.size(); i++) {
|
||||
if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
|
||||
}
|
||||
pw.println("]");
|
||||
pw.println(" Forced app standby packages: " + mForcedAppStandbyPackages);
|
||||
mForceAppStandbyTracker.dump(pw, " ");
|
||||
|
||||
final long nowRTC = System.currentTimeMillis();
|
||||
final long nowELAPSED = SystemClock.elapsedRealtime();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
@@ -1718,7 +1730,6 @@ class AlarmManagerService extends SystemService {
|
||||
pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
|
||||
pw.println();
|
||||
pw.print(" Num time change events: "); pw.println(mNumTimeChanged);
|
||||
pw.println(" mDeviceIdleUserWhitelist=" + Arrays.toString(mDeviceIdleUserWhitelist));
|
||||
|
||||
pw.println();
|
||||
pw.println(" Next alarm clock information: ");
|
||||
@@ -1991,15 +2002,8 @@ class AlarmManagerService extends SystemService {
|
||||
|
||||
mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
|
||||
|
||||
final int foregroundUidsSize = mForegroundUids.size();
|
||||
for (int i = 0; i < foregroundUidsSize; i++) {
|
||||
if (mForegroundUids.valueAt(i)) {
|
||||
proto.write(AlarmManagerServiceProto.FOREGROUND_UIDS, mForegroundUids.keyAt(i));
|
||||
}
|
||||
}
|
||||
for (String pkg : mForcedAppStandbyPackages) {
|
||||
proto.write(AlarmManagerServiceProto.FORCED_APP_STANDBY_PACKAGES, pkg);
|
||||
}
|
||||
mForceAppStandbyTracker.dumpProto(proto,
|
||||
AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
|
||||
|
||||
proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
|
||||
if (!mInteractive) {
|
||||
@@ -2023,9 +2027,6 @@ class AlarmManagerService extends SystemService {
|
||||
proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
|
||||
nowElapsed - mLastWakeupSet);
|
||||
proto.write(AlarmManagerServiceProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
|
||||
for (int i : mDeviceIdleUserWhitelist) {
|
||||
proto.write(AlarmManagerServiceProto.DEVICE_IDLE_USER_WHITELIST_APP_IDS, i);
|
||||
}
|
||||
|
||||
final TreeSet<Integer> users = new TreeSet<>();
|
||||
final int nextAlarmClockForUserSize = mNextAlarmClockForUser.size();
|
||||
@@ -2267,28 +2268,6 @@ class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
void setDeviceIdleUserWhitelistImpl(int[] appids) {
|
||||
synchronized (mLock) {
|
||||
// appids are sorted, just send pending alarms for any new appids added to the whitelist
|
||||
int i = 0, j = 0;
|
||||
while (i < appids.length) {
|
||||
while (j < mDeviceIdleUserWhitelist.length
|
||||
&& mDeviceIdleUserWhitelist[j] < appids[i]) {
|
||||
j++;
|
||||
}
|
||||
if (j < mDeviceIdleUserWhitelist.length
|
||||
&& appids[i] != mDeviceIdleUserWhitelist[j]) {
|
||||
if (DEBUG_BG_LIMIT) {
|
||||
Slog.d(TAG, "Sending blocked alarms for whitelisted appid " + appids[j]);
|
||||
}
|
||||
sendPendingBackgroundAlarmsForAppIdLocked(appids[j]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
mDeviceIdleUserWhitelist = appids;
|
||||
}
|
||||
}
|
||||
|
||||
AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
|
||||
synchronized (mLock) {
|
||||
return mNextAlarmClockForUser.get(userId);
|
||||
@@ -2711,9 +2690,7 @@ class AlarmManagerService extends SystemService {
|
||||
final String sourcePackage =
|
||||
(alarm.operation != null) ? alarm.operation.getCreatorPackage() : alarm.packageName;
|
||||
final int sourceUid = alarm.creatorUid;
|
||||
return mForcedAppStandbyPackages.contains(sourcePackage) && !mForegroundUids.get(sourceUid)
|
||||
&& Arrays.binarySearch(mDeviceIdleUserWhitelist, UserHandle.getAppId(sourceUid))
|
||||
< 0;
|
||||
return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage);
|
||||
}
|
||||
|
||||
private native long init();
|
||||
@@ -2860,7 +2837,8 @@ class AlarmManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Alarm {
|
||||
@VisibleForTesting
|
||||
static class Alarm {
|
||||
public final int type;
|
||||
public final long origWhen;
|
||||
public final boolean wakeup;
|
||||
@@ -3483,17 +3461,10 @@ class AlarmManagerService extends SystemService {
|
||||
if (disabled) {
|
||||
removeForStoppedLocked(uid);
|
||||
}
|
||||
mForegroundUids.delete(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onUidActive(int uid) {
|
||||
synchronized (mLock) {
|
||||
if (!mForegroundUids.get(uid)) {
|
||||
mForegroundUids.put(uid, true);
|
||||
sendPendingBackgroundAlarmsLocked(uid, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onUidIdle(int uid, boolean disabled) {
|
||||
@@ -3501,7 +3472,6 @@ class AlarmManagerService extends SystemService {
|
||||
if (disabled) {
|
||||
removeForStoppedLocked(uid);
|
||||
}
|
||||
mForegroundUids.delete(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3509,27 +3479,29 @@ class AlarmManagerService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
private final class AppOpsWatcher extends IAppOpsCallback.Stub {
|
||||
|
||||
private final Listener mForceAppStandbyListener = new Listener() {
|
||||
@Override
|
||||
public void opChanged(int op, int uid, String packageName) throws RemoteException {
|
||||
public void unblockAllUnrestrictedAlarms() {
|
||||
synchronized (mLock) {
|
||||
final int mode = mAppOpsService.checkOperation(op, uid, packageName);
|
||||
if (DEBUG_BG_LIMIT) {
|
||||
Slog.d(TAG,
|
||||
"Appop changed for " + uid + ", " + packageName + " to " + mode);
|
||||
}
|
||||
final boolean changed;
|
||||
if (mode != AppOpsManager.MODE_ALLOWED) {
|
||||
changed = mForcedAppStandbyPackages.add(packageName);
|
||||
} else {
|
||||
changed = mForcedAppStandbyPackages.remove(packageName);
|
||||
}
|
||||
if (changed && mode == AppOpsManager.MODE_ALLOWED) {
|
||||
sendPendingBackgroundAlarmsLocked(uid, packageName);
|
||||
}
|
||||
sendAllUnrestrictedPendingBackgroundAlarmsLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unblockAlarmsForUid(int uid) {
|
||||
synchronized (mLock) {
|
||||
sendPendingBackgroundAlarmsLocked(uid, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unblockAlarmsForUidPackage(int uid, String packageName) {
|
||||
synchronized (mLock) {
|
||||
sendPendingBackgroundAlarmsLocked(uid, packageName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastStats getStatsLocked(PendingIntent pi) {
|
||||
String pkg = pi.getCreatorPackage();
|
||||
|
||||
@@ -119,7 +119,6 @@ public class DeviceIdleController extends SystemService
|
||||
private PowerManagerInternal mLocalPowerManager;
|
||||
private PowerManager mPowerManager;
|
||||
private ConnectivityService mConnectivityService;
|
||||
private AlarmManagerService.LocalService mLocalAlarmManager;
|
||||
private INetworkPolicyManager mNetworkPolicyManager;
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mMotionSensor;
|
||||
@@ -1435,7 +1434,6 @@ public class DeviceIdleController extends SystemService
|
||||
mGoingIdleWakeLock.setReferenceCounted(true);
|
||||
mConnectivityService = (ConnectivityService)ServiceManager.getService(
|
||||
Context.CONNECTIVITY_SERVICE);
|
||||
mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
|
||||
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
|
||||
mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
|
||||
@@ -1500,8 +1498,8 @@ public class DeviceIdleController extends SystemService
|
||||
|
||||
mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
|
||||
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
|
||||
mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
|
||||
|
||||
passWhiteListToForceAppStandbyTrackerLocked();
|
||||
updateInteractivityLocked();
|
||||
}
|
||||
updateConnectivityState(null);
|
||||
@@ -2477,13 +2475,7 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
|
||||
}
|
||||
if (mLocalAlarmManager != null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Setting alarm whitelist to "
|
||||
+ Arrays.toString(mPowerSaveWhitelistUserAppIdArray));
|
||||
}
|
||||
mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
|
||||
}
|
||||
passWhiteListToForceAppStandbyTrackerLocked();
|
||||
}
|
||||
|
||||
private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) {
|
||||
@@ -2509,6 +2501,7 @@ public class DeviceIdleController extends SystemService
|
||||
}
|
||||
mLocalPowerManager.setDeviceIdleTempWhitelist(mTempWhitelistAppIdArray);
|
||||
}
|
||||
passWhiteListToForceAppStandbyTrackerLocked();
|
||||
}
|
||||
|
||||
private void reportPowerSaveWhitelistChangedLocked() {
|
||||
@@ -2523,6 +2516,12 @@ public class DeviceIdleController extends SystemService
|
||||
getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
|
||||
}
|
||||
|
||||
private void passWhiteListToForceAppStandbyTrackerLocked() {
|
||||
ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
|
||||
mPowerSaveWhitelistAllAppIdArray,
|
||||
mTempWhitelistAppIdArray);
|
||||
}
|
||||
|
||||
void readConfigFileLocked() {
|
||||
if (DEBUG) Slog.d(TAG, "Reading config from " + mConfigFile.getBaseFile());
|
||||
mPowerSaveWhitelistUserApps.clear();
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
package com.android.server;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.PackageOps;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.IUidObserver;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager.ServiceType;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.RemoteException;
|
||||
@@ -30,21 +35,36 @@ import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class to track OP_RUN_ANY_IN_BACKGROUND, UID foreground state and "force all app standby".
|
||||
* Class to keep track of the information related to "force app standby", which includes:
|
||||
* - OP_RUN_ANY_IN_BACKGROUND for each package
|
||||
* - UID foreground state
|
||||
* - User+system power save whitelist
|
||||
* - Temporary power save whitelist
|
||||
* - Global "force all apps standby" mode enforced by battery saver.
|
||||
*
|
||||
* TODO Clean up cache when a user is deleted.
|
||||
* TODO Add unit tests. b/68769804.
|
||||
* TODO: In general, we can reduce the number of callbacks by checking all signals before sending
|
||||
* each callback. For example, even when an UID comes into the foreground, if it wasn't
|
||||
* originally restricted, then there's no need to send an event.
|
||||
* Doing this would be error-prone, so we punt it for now, but we should revisit it later.
|
||||
*
|
||||
* Test:
|
||||
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
|
||||
*/
|
||||
public class ForceAppStandbyTracker {
|
||||
private static final String TAG = "ForceAppStandbyTracker";
|
||||
@@ -55,21 +75,31 @@ public class ForceAppStandbyTracker {
|
||||
private final Object mLock = new Object();
|
||||
private final Context mContext;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int TARGET_OP = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
|
||||
|
||||
IActivityManager mIActivityManager;
|
||||
AppOpsManager mAppOpsManager;
|
||||
IAppOpsService mAppOpsService;
|
||||
PowerManagerInternal mPowerManagerInternal;
|
||||
|
||||
private final Handler mCallbackHandler;
|
||||
private final MyHandler mHandler;
|
||||
|
||||
/**
|
||||
* Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
final ArraySet<Pair<Integer, String>> mForcedAppStandbyUidPackages = new ArraySet<>();
|
||||
final ArraySet<Pair<Integer, String>> mRunAnyRestrictedPackages = new ArraySet<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int[] mPowerWhitelistedAllAppIds = new int[0];
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private int[] mTempWhitelistedAppIds = mPowerWhitelistedAllAppIds;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
final ArraySet<Listener> mListeners = new ArraySet<>();
|
||||
|
||||
@@ -80,16 +110,116 @@ public class ForceAppStandbyTracker {
|
||||
boolean mForceAllAppsStandby;
|
||||
|
||||
public static abstract class Listener {
|
||||
public void onRestrictionChanged(int uid, @Nullable String packageName) {
|
||||
/**
|
||||
* This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
|
||||
*/
|
||||
private void onRunAnyAppOpsChanged(ForceAppStandbyTracker sender,
|
||||
int uid, @NonNull String packageName) {
|
||||
updateJobsForUidPackage(uid, packageName);
|
||||
|
||||
if (!sender.areAlarmsRestricted(uid, packageName)) {
|
||||
unblockAlarmsForUidPackage(uid, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
public void onGlobalRestrictionChanged() {
|
||||
/**
|
||||
* This is called when the foreground state changed for a UID.
|
||||
*/
|
||||
private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
|
||||
updateJobsForUid(uid);
|
||||
|
||||
if (sender.isInForeground(uid)) {
|
||||
unblockAlarmsForUid(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when an app-id(s) is removed from the power save whitelist.
|
||||
*/
|
||||
private void onPowerSaveUnwhitelisted(ForceAppStandbyTracker sender) {
|
||||
updateAllJobs();
|
||||
unblockAllUnrestrictedAlarms();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the power save whitelist changes, excluding the
|
||||
* {@link #onPowerSaveUnwhitelisted} case.
|
||||
*/
|
||||
private void onPowerSaveWhitelistedChanged(ForceAppStandbyTracker sender) {
|
||||
updateAllJobs();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the temp whitelist changes.
|
||||
*/
|
||||
private void onTempPowerSaveWhitelistChanged(ForceAppStandbyTracker sender) {
|
||||
|
||||
// TODO This case happens rather frequently; consider optimizing and update jobs
|
||||
// only for affected app-ids.
|
||||
|
||||
updateAllJobs();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the global "force all apps standby" flag changes.
|
||||
*/
|
||||
private void onForceAllAppsStandbyChanged(ForceAppStandbyTracker sender) {
|
||||
updateAllJobs();
|
||||
|
||||
if (!sender.isForceAllAppsStandbyEnabled()) {
|
||||
unblockAllUnrestrictedAlarms();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the job restrictions for multiple UIDs might have changed, so the job
|
||||
* scheduler should re-evaluate all restrictions for all jobs.
|
||||
*/
|
||||
public void updateAllJobs() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the job restrictions for a UID might have changed, so the job
|
||||
* scheduler should re-evaluate all restrictions for all jobs.
|
||||
*/
|
||||
public void updateJobsForUid(int uid) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the job restrictions for a UID - package might have changed, so the job
|
||||
* scheduler should re-evaluate all restrictions for all jobs.
|
||||
*/
|
||||
public void updateJobsForUidPackage(int uid, String packageName) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the job restrictions for multiple UIDs might have changed, so the alarm
|
||||
* manager should re-evaluate all restrictions for all blocked jobs.
|
||||
*/
|
||||
public void unblockAllUnrestrictedAlarms() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all jobs for a specific UID are unblocked.
|
||||
*/
|
||||
public void unblockAlarmsForUid(int uid) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all alarms for a specific UID - package are unblocked.
|
||||
*/
|
||||
public void unblockAlarmsForUidPackage(int uid, String packageName) {
|
||||
}
|
||||
}
|
||||
|
||||
private ForceAppStandbyTracker(Context context) {
|
||||
@VisibleForTesting
|
||||
ForceAppStandbyTracker(Context context, Looper looper) {
|
||||
mContext = context;
|
||||
mCallbackHandler = FgThread.getHandler();
|
||||
mHandler = new MyHandler(looper);
|
||||
}
|
||||
|
||||
private ForceAppStandbyTracker(Context context) {
|
||||
this(context, FgThread.get().getLooper());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,45 +242,65 @@ public class ForceAppStandbyTracker {
|
||||
}
|
||||
mStarted = true;
|
||||
|
||||
mAppOpsManager = Preconditions.checkNotNull(
|
||||
mContext.getSystemService(AppOpsManager.class));
|
||||
mAppOpsService = Preconditions.checkNotNull(
|
||||
IAppOpsService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.APP_OPS_SERVICE)));
|
||||
mPowerManagerInternal = Preconditions.checkNotNull(
|
||||
LocalServices.getService(PowerManagerInternal.class));
|
||||
mIActivityManager = Preconditions.checkNotNull(injectIActivityManager());
|
||||
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
|
||||
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
|
||||
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
|
||||
|
||||
try {
|
||||
ActivityManager.getService().registerUidObserver(new UidObserver(),
|
||||
mIActivityManager.registerUidObserver(new UidObserver(),
|
||||
ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
|
||||
| ActivityManager.UID_OBSERVER_ACTIVE,
|
||||
ActivityManager.PROCESS_STATE_UNKNOWN, null);
|
||||
mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
|
||||
mAppOpsService.startWatchingMode(TARGET_OP, null,
|
||||
new AppOpsWatcher());
|
||||
} catch (RemoteException e) {
|
||||
// shouldn't happen.
|
||||
}
|
||||
|
||||
mPowerManagerInternal.registerLowPowerModeObserver(
|
||||
ServiceType.FORCE_ALL_APPS_STANDBY,
|
||||
state -> updateForceAllAppsStandby(state.batterySaverEnabled));
|
||||
|
||||
updateForceAllAppsStandby(
|
||||
mPowerManagerInternal.getLowPowerState(ServiceType.FORCE_ALL_APPS_STANDBY)
|
||||
.batterySaverEnabled);
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_USER_REMOVED);
|
||||
mContext.registerReceiver(new MyReceiver(), filter);
|
||||
|
||||
refreshForcedAppStandbyUidPackagesLocked();
|
||||
|
||||
mPowerManagerInternal.registerLowPowerModeObserver(
|
||||
ServiceType.FORCE_ALL_APPS_STANDBY,
|
||||
(state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
|
||||
|
||||
updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
|
||||
ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AppOpsManager injectAppOpsManager() {
|
||||
return mContext.getSystemService(AppOpsManager.class);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
IAppOpsService injectIAppOpsService() {
|
||||
return IAppOpsService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
IActivityManager injectIActivityManager() {
|
||||
return ActivityManager.getService();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PowerManagerInternal injectPowerManagerInternal() {
|
||||
return LocalServices.getService(PowerManagerInternal.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update {@link #mForcedAppStandbyUidPackages} with the current app ops state.
|
||||
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
|
||||
*/
|
||||
private void refreshForcedAppStandbyUidPackagesLocked() {
|
||||
final int op = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
|
||||
|
||||
mForcedAppStandbyUidPackages.clear();
|
||||
final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(new int[] {op});
|
||||
mRunAnyRestrictedPackages.clear();
|
||||
final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(
|
||||
new int[] {TARGET_OP});
|
||||
|
||||
if (ops == null) {
|
||||
return;
|
||||
@@ -162,31 +312,38 @@ public class ForceAppStandbyTracker {
|
||||
|
||||
for (int j = 0; j < entries.size(); j++) {
|
||||
AppOpsManager.OpEntry ent = entries.get(j);
|
||||
if (ent.getOp() != op) {
|
||||
if (ent.getOp() != TARGET_OP) {
|
||||
continue;
|
||||
}
|
||||
if (ent.getMode() != AppOpsManager.MODE_ALLOWED) {
|
||||
mForcedAppStandbyUidPackages.add(Pair.create(
|
||||
mRunAnyRestrictedPackages.add(Pair.create(
|
||||
pkg.getUid(), pkg.getPackageName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isRunAnyInBackgroundAppOpRestricted(int uid, @NonNull String packageName) {
|
||||
try {
|
||||
return mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
|
||||
uid, packageName) != AppOpsManager.MODE_ALLOWED;
|
||||
} catch (RemoteException e) {
|
||||
return false; // shouldn't happen.
|
||||
/**
|
||||
* Update {@link #mForceAllAppsStandby} and notifies the listeners.
|
||||
*/
|
||||
void updateForceAllAppsStandby(boolean enable) {
|
||||
synchronized (mLock) {
|
||||
if (enable == mForceAllAppsStandby) {
|
||||
return;
|
||||
}
|
||||
mForceAllAppsStandby = enable;
|
||||
|
||||
mHandler.notifyForceAllAppsStandbyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
|
||||
// TODO Maybe we should switch to indexOf(Pair.create()) if the array size is too big.
|
||||
final int size = mForcedAppStandbyUidPackages.size();
|
||||
final int size = mRunAnyRestrictedPackages.size();
|
||||
if (size > 8) {
|
||||
return mRunAnyRestrictedPackages.indexOf(Pair.create(uid, packageName));
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Pair<Integer, String> pair = mForcedAppStandbyUidPackages.valueAt(i);
|
||||
final Pair<Integer, String> pair = mRunAnyRestrictedPackages.valueAt(i);
|
||||
|
||||
if ((pair.first == uid) && packageName.equals(pair.second)) {
|
||||
return i;
|
||||
@@ -196,13 +353,16 @@ public class ForceAppStandbyTracker {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether a uid package-name pair is in mForcedAppStandbyUidPackages.
|
||||
* @return whether a uid package-name pair is in mRunAnyRestrictedPackages.
|
||||
*/
|
||||
boolean isUidPackageRestrictedLocked(int uid, @NonNull String packageName) {
|
||||
boolean isRunAnyRestrictedLocked(int uid, @NonNull String packageName) {
|
||||
return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
|
||||
}
|
||||
|
||||
boolean updateRestrictedUidPackageLocked(int uid, @NonNull String packageName,
|
||||
/**
|
||||
* Add to / remove from {@link #mRunAnyRestrictedPackages}.
|
||||
*/
|
||||
boolean updateForcedAppStandbyUidPackageLocked(int uid, @NonNull String packageName,
|
||||
boolean restricted) {
|
||||
final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
|
||||
final boolean wasRestricted = index >= 0;
|
||||
@@ -210,13 +370,16 @@ public class ForceAppStandbyTracker {
|
||||
return false;
|
||||
}
|
||||
if (restricted) {
|
||||
mForcedAppStandbyUidPackages.add(Pair.create(uid, packageName));
|
||||
mRunAnyRestrictedPackages.add(Pair.create(uid, packageName));
|
||||
} else {
|
||||
mForcedAppStandbyUidPackages.removeAt(index);
|
||||
mRunAnyRestrictedPackages.removeAt(index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a UID to {@link #mForegroundUids}.
|
||||
*/
|
||||
void uidToForeground(int uid) {
|
||||
synchronized (mLock) {
|
||||
if (!UserHandle.isApp(uid)) {
|
||||
@@ -228,10 +391,13 @@ public class ForceAppStandbyTracker {
|
||||
return;
|
||||
}
|
||||
mForegroundUids.put(uid, true);
|
||||
notifyForUidPackage(uid, null);
|
||||
mHandler.notifyUidForegroundStateChanged(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets false for a UID {@link #mForegroundUids}, or remove it when {@code remove} is true.
|
||||
*/
|
||||
void uidToBackground(int uid, boolean remove) {
|
||||
synchronized (mLock) {
|
||||
if (!UserHandle.isApp(uid)) {
|
||||
@@ -247,13 +413,11 @@ public class ForceAppStandbyTracker {
|
||||
} else {
|
||||
mForegroundUids.put(uid, false);
|
||||
}
|
||||
notifyForUidPackage(uid, null);
|
||||
mHandler.notifyUidForegroundStateChanged(uid);
|
||||
}
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
final class UidObserver extends IUidObserver.Stub {
|
||||
private final class UidObserver extends IUidObserver.Stub {
|
||||
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
|
||||
}
|
||||
|
||||
@@ -277,11 +441,28 @@ public class ForceAppStandbyTracker {
|
||||
private final class AppOpsWatcher extends IAppOpsCallback.Stub {
|
||||
@Override
|
||||
public void opChanged(int op, int uid, String packageName) throws RemoteException {
|
||||
boolean restricted = false;
|
||||
try {
|
||||
restricted = mAppOpsService.checkOperation(TARGET_OP,
|
||||
uid, packageName) != AppOpsManager.MODE_ALLOWED;
|
||||
} catch (RemoteException e) {
|
||||
// Shouldn't happen
|
||||
}
|
||||
synchronized (mLock) {
|
||||
final boolean restricted = isRunAnyInBackgroundAppOpRestricted(uid, packageName);
|
||||
if (updateForcedAppStandbyUidPackageLocked(uid, packageName, restricted)) {
|
||||
mHandler.notifyRunAnyAppOpsChanged(uid, packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateRestrictedUidPackageLocked(uid, packageName, restricted)) {
|
||||
notifyForUidPackage(uid, packageName);
|
||||
private final class MyReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
|
||||
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
|
||||
if (userId > 0) {
|
||||
mHandler.doUserRemoved(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,31 +474,183 @@ public class ForceAppStandbyTracker {
|
||||
}
|
||||
}
|
||||
|
||||
void notifyForUidPackage(int uid, String packageName) {
|
||||
mCallbackHandler.post(() -> {
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onRestrictionChanged(uid, packageName);
|
||||
}
|
||||
});
|
||||
}
|
||||
private class MyHandler extends Handler {
|
||||
private static final int MSG_UID_STATE_CHANGED = 1;
|
||||
private static final int MSG_RUN_ANY_CHANGED = 2;
|
||||
private static final int MSG_ALL_UNWHITELISTED = 3;
|
||||
private static final int MSG_ALL_WHITELIST_CHANGED = 4;
|
||||
private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
|
||||
private static final int MSG_FORCE_ALL_CHANGED = 6;
|
||||
|
||||
void notifyGlobal() {
|
||||
mCallbackHandler.post(() -> {
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onGlobalRestrictionChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
private static final int MSG_USER_REMOVED = 7;
|
||||
|
||||
void updateForceAllAppsStandby(boolean forceAllAppsStandby) {
|
||||
synchronized (mLock) {
|
||||
if (mForceAllAppsStandby == forceAllAppsStandby) {
|
||||
return;
|
||||
}
|
||||
mForceAllAppsStandby = forceAllAppsStandby;
|
||||
Slog.i(TAG, "Force all app standby: " + mForceAllAppsStandby);
|
||||
notifyGlobal();
|
||||
public MyHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
public void notifyUidForegroundStateChanged(int uid) {
|
||||
obtainMessage(MSG_UID_STATE_CHANGED, uid, 0).sendToTarget();
|
||||
}
|
||||
public void notifyRunAnyAppOpsChanged(int uid, @NonNull String packageName) {
|
||||
obtainMessage(MSG_RUN_ANY_CHANGED, uid, 0, packageName).sendToTarget();
|
||||
}
|
||||
|
||||
public void notifyAllUnwhitelisted() {
|
||||
obtainMessage(MSG_ALL_UNWHITELISTED).sendToTarget();
|
||||
}
|
||||
|
||||
public void notifyAllWhitelistChanged() {
|
||||
obtainMessage(MSG_ALL_WHITELIST_CHANGED).sendToTarget();
|
||||
}
|
||||
|
||||
public void notifyTempWhitelistChanged() {
|
||||
obtainMessage(MSG_TEMP_WHITELIST_CHANGED).sendToTarget();
|
||||
}
|
||||
|
||||
public void notifyForceAllAppsStandbyChanged() {
|
||||
obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
|
||||
}
|
||||
|
||||
public void doUserRemoved(int userId) {
|
||||
obtainMessage(MSG_USER_REMOVED, userId, 0).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_USER_REMOVED:
|
||||
handleUserRemoved(msg.arg1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only notify the listeners when started.
|
||||
synchronized (mLock) {
|
||||
if (!mStarted) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this;
|
||||
|
||||
switch (msg.what) {
|
||||
case MSG_UID_STATE_CHANGED:
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onUidForegroundStateChanged(sender, msg.arg1);
|
||||
}
|
||||
return;
|
||||
case MSG_RUN_ANY_CHANGED:
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onRunAnyAppOpsChanged(sender, msg.arg1, (String) msg.obj);
|
||||
}
|
||||
return;
|
||||
case MSG_ALL_UNWHITELISTED:
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onPowerSaveUnwhitelisted(sender);
|
||||
}
|
||||
return;
|
||||
case MSG_ALL_WHITELIST_CHANGED:
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onPowerSaveWhitelistedChanged(sender);
|
||||
}
|
||||
return;
|
||||
case MSG_TEMP_WHITELIST_CHANGED:
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onTempPowerSaveWhitelistChanged(sender);
|
||||
}
|
||||
return;
|
||||
case MSG_FORCE_ALL_CHANGED:
|
||||
for (Listener l : cloneListeners()) {
|
||||
l.onForceAllAppsStandbyChanged(sender);
|
||||
}
|
||||
return;
|
||||
case MSG_USER_REMOVED:
|
||||
handleUserRemoved(msg.arg1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleUserRemoved(int removedUserId) {
|
||||
synchronized (mLock) {
|
||||
for (int i = mRunAnyRestrictedPackages.size() - 1; i >= 0; i--) {
|
||||
final Pair<Integer, String> pair = mRunAnyRestrictedPackages.valueAt(i);
|
||||
final int uid = pair.first;
|
||||
final int userId = UserHandle.getUserId(uid);
|
||||
|
||||
if (userId == removedUserId) {
|
||||
mRunAnyRestrictedPackages.removeAt(i);
|
||||
}
|
||||
}
|
||||
for (int i = mForegroundUids.size() - 1; i >= 0; i--) {
|
||||
final int uid = mForegroundUids.keyAt(i);
|
||||
final int userId = UserHandle.getUserId(uid);
|
||||
|
||||
if (userId == removedUserId) {
|
||||
mForegroundUids.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by device idle controller to update the power save whitelists.
|
||||
*/
|
||||
public void setPowerSaveWhitelistAppIds(
|
||||
int[] powerSaveWhitelistAllAppIdArray, int[] tempWhitelistAppIdArray) {
|
||||
synchronized (mLock) {
|
||||
final int[] previousWhitelist = mPowerWhitelistedAllAppIds;
|
||||
final int[] previousTempWhitelist = mTempWhitelistedAppIds;
|
||||
|
||||
mPowerWhitelistedAllAppIds = powerSaveWhitelistAllAppIdArray;
|
||||
mTempWhitelistedAppIds = tempWhitelistAppIdArray;
|
||||
|
||||
if (isAnyAppIdUnwhitelisted(previousWhitelist, mPowerWhitelistedAllAppIds)) {
|
||||
mHandler.notifyAllUnwhitelisted();
|
||||
} else if (!Arrays.equals(previousWhitelist, mPowerWhitelistedAllAppIds)) {
|
||||
mHandler.notifyAllWhitelistChanged();
|
||||
}
|
||||
|
||||
if (!Arrays.equals(previousTempWhitelist, mTempWhitelistedAppIds)) {
|
||||
mHandler.notifyTempWhitelistChanged();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @retunr true if a sorted app-id array {@code prevArray} has at least one element
|
||||
* that's not in a sorted app-id array {@code newArray}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isAnyAppIdUnwhitelisted(int[] prevArray, int[] newArray) {
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
boolean prevFinished;
|
||||
boolean newFinished;
|
||||
|
||||
for (;;) {
|
||||
prevFinished = i1 >= prevArray.length;
|
||||
newFinished = i2 >= newArray.length;
|
||||
if (prevFinished || newFinished) {
|
||||
break;
|
||||
}
|
||||
int a1 = prevArray[i1];
|
||||
int a2 = newArray[i2];
|
||||
|
||||
if (a1 == a2) {
|
||||
i1++;
|
||||
i2++;
|
||||
continue;
|
||||
}
|
||||
if (a1 < a2) {
|
||||
// prevArray has an element that's not in a2.
|
||||
return true;
|
||||
}
|
||||
i2++;
|
||||
}
|
||||
if (prevFinished) {
|
||||
return false;
|
||||
}
|
||||
return newFinished;
|
||||
}
|
||||
|
||||
// Public interface.
|
||||
@@ -332,21 +665,51 @@ public class ForceAppStandbyTracker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether force-app-standby is effective for a UID package-name.
|
||||
* @return whether alarms should be restricted for a UID package-name.
|
||||
*/
|
||||
public boolean isRestricted(int uid, @NonNull String packageName) {
|
||||
public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
|
||||
return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether jobs should be restricted for a UID package-name.
|
||||
*/
|
||||
public boolean areJobsRestricted(int uid, @NonNull String packageName) {
|
||||
return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether force-app-standby is effective for a UID package-name.
|
||||
*/
|
||||
private boolean isRestricted(int uid, @NonNull String packageName,
|
||||
boolean useTempWhitelistToo) {
|
||||
if (isInForeground(uid)) {
|
||||
return false;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
// Whitelisted?
|
||||
final int appId = UserHandle.getAppId(uid);
|
||||
if (ArrayUtils.contains(mPowerWhitelistedAllAppIds, appId)) {
|
||||
return false;
|
||||
}
|
||||
if (useTempWhitelistToo &&
|
||||
ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mForceAllAppsStandby) {
|
||||
return true;
|
||||
}
|
||||
return isUidPackageRestrictedLocked(uid, packageName);
|
||||
|
||||
return isRunAnyRestrictedLocked(uid, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
/** For dumpsys -- otherwise the callers don't need to know it. */
|
||||
/**
|
||||
* @return whether a UID is in the foreground or not.
|
||||
*
|
||||
* Note clients normally shouldn't need to access it. It's only for dumpsys.
|
||||
*/
|
||||
public boolean isInForeground(int uid) {
|
||||
if (!UserHandle.isApp(uid)) {
|
||||
return true;
|
||||
@@ -356,31 +719,120 @@ public class ForceAppStandbyTracker {
|
||||
}
|
||||
}
|
||||
|
||||
/** For dumpsys -- otherwise the callers don't need to know it. */
|
||||
public boolean isForceAllAppsStandbyEnabled() {
|
||||
/**
|
||||
* @return whether force all apps standby is enabled or not.
|
||||
*
|
||||
* Note clients normally shouldn't need to access it.
|
||||
*/
|
||||
boolean isForceAllAppsStandbyEnabled() {
|
||||
synchronized (mLock) {
|
||||
return mForceAllAppsStandby;
|
||||
}
|
||||
}
|
||||
|
||||
/** For dumpsys -- otherwise the callers don't need to know it. */
|
||||
/**
|
||||
* @return whether a UID/package has {@code OP_RUN_ANY_IN_BACKGROUND} allowed or not.
|
||||
*
|
||||
* Note clients normally shouldn't need to access it. It's only for dumpsys.
|
||||
*/
|
||||
public boolean isRunAnyInBackgroundAppOpsAllowed(int uid, @NonNull String packageName) {
|
||||
synchronized (mLock) {
|
||||
return !isUidPackageRestrictedLocked(uid, packageName);
|
||||
return !isRunAnyRestrictedLocked(uid, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
/** For dumpsys -- otherwise the callers don't need to know it. */
|
||||
public SparseBooleanArray getForegroudUids() {
|
||||
/**
|
||||
* @return whether a UID is in the user / system defined power-save whitelist or not.
|
||||
*
|
||||
* Note clients normally shouldn't need to access it. It's only for dumpsys.
|
||||
*/
|
||||
public boolean isUidPowerSaveWhitelisted(int uid) {
|
||||
synchronized (mLock) {
|
||||
return mForegroundUids.clone();
|
||||
return ArrayUtils.contains(mPowerWhitelistedAllAppIds, UserHandle.getAppId(uid));
|
||||
}
|
||||
}
|
||||
|
||||
/** For dumpsys -- otherwise the callers don't need to know it. */
|
||||
public ArraySet<Pair<Integer, String>> getRestrictedUidPackages() {
|
||||
/**
|
||||
* @return whether a UID is in the temp power-save whitelist or not.
|
||||
*
|
||||
* Note clients normally shouldn't need to access it. It's only for dumpsys.
|
||||
*/
|
||||
public boolean isUidTempPowerSaveWhitelisted(int uid) {
|
||||
synchronized (mLock) {
|
||||
return new ArraySet(mForcedAppStandbyUidPackages);
|
||||
return ArrayUtils.contains(mTempWhitelistedAppIds, UserHandle.getAppId(uid));
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String indent) {
|
||||
synchronized (mLock) {
|
||||
pw.print(indent);
|
||||
pw.print("Force all apps standby: ");
|
||||
pw.println(isForceAllAppsStandbyEnabled());
|
||||
|
||||
pw.print(indent);
|
||||
pw.print("Foreground uids: [");
|
||||
|
||||
String sep = "";
|
||||
for (int i = 0; i < mForegroundUids.size(); i++) {
|
||||
if (mForegroundUids.valueAt(i)) {
|
||||
pw.print(sep);
|
||||
pw.print(UserHandle.formatUid(mForegroundUids.keyAt(i)));
|
||||
sep = " ";
|
||||
}
|
||||
}
|
||||
pw.println("]");
|
||||
|
||||
pw.print(indent);
|
||||
pw.print("Whitelist appids: ");
|
||||
pw.println(Arrays.toString(mPowerWhitelistedAllAppIds));
|
||||
|
||||
pw.print(indent);
|
||||
pw.print("Temp whitelist appids: ");
|
||||
pw.println(Arrays.toString(mTempWhitelistedAppIds));
|
||||
|
||||
pw.print(indent);
|
||||
pw.println("Restricted packages:");
|
||||
for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) {
|
||||
pw.print(indent);
|
||||
pw.print(" ");
|
||||
pw.print(UserHandle.formatUid(uidAndPackage.first));
|
||||
pw.print(" ");
|
||||
pw.print(uidAndPackage.second);
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpProto(ProtoOutputStream proto, long fieldId) {
|
||||
synchronized (mLock) {
|
||||
final long token = proto.start(fieldId);
|
||||
|
||||
proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
|
||||
|
||||
for (int i = 0; i < mForegroundUids.size(); i++) {
|
||||
if (mForegroundUids.valueAt(i)) {
|
||||
proto.write(ForceAppStandbyTrackerProto.FOREGROUND_UIDS,
|
||||
mForegroundUids.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int appId : mPowerWhitelistedAllAppIds) {
|
||||
proto.write(ForceAppStandbyTrackerProto.POWER_SAVE_WHITELIST_APP_IDS, appId);
|
||||
}
|
||||
|
||||
for (int appId : mTempWhitelistedAppIds) {
|
||||
proto.write(ForceAppStandbyTrackerProto.TEMP_POWER_SAVE_WHITELIST_APP_IDS, appId);
|
||||
}
|
||||
|
||||
for (Pair<Integer, String> uidAndPackage : mRunAnyRestrictedPackages) {
|
||||
final long token2 = proto.start(
|
||||
ForceAppStandbyTrackerProto.RUN_ANY_IN_BACKGROUND_RESTRICTED_PACKAGES);
|
||||
proto.write(RunAnyInBackgroundRestrictedPackages.UID, uidAndPackage.first);
|
||||
proto.write(RunAnyInBackgroundRestrictedPackages.PACKAGE_NAME,
|
||||
uidAndPackage.second);
|
||||
proto.end(token2);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,12 @@
|
||||
|
||||
package com.android.server.job.controllers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.IDeviceIdleController;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.server.ForceAppStandbyTracker;
|
||||
@@ -37,7 +30,6 @@ import com.android.server.job.JobSchedulerService;
|
||||
import com.android.server.job.JobStore;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class BackgroundJobsController extends StateController {
|
||||
|
||||
@@ -51,9 +43,6 @@ public final class BackgroundJobsController extends StateController {
|
||||
private final JobSchedulerService mJobSchedulerService;
|
||||
private final IDeviceIdleController mDeviceIdleController;
|
||||
|
||||
private int[] mPowerWhitelistedUserAppIds;
|
||||
private int[] mTempWhitelistedAppIds;
|
||||
|
||||
private final ForceAppStandbyTracker mForceAppStandbyTracker;
|
||||
|
||||
|
||||
@@ -67,28 +56,6 @@ public final class BackgroundJobsController extends StateController {
|
||||
}
|
||||
}
|
||||
|
||||
private BroadcastReceiver mDozeWhitelistReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
switch (intent.getAction()) {
|
||||
case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
|
||||
mPowerWhitelistedUserAppIds =
|
||||
mDeviceIdleController.getAppIdUserWhitelist();
|
||||
break;
|
||||
case PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED:
|
||||
mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
|
||||
break;
|
||||
}
|
||||
} catch (RemoteException rexc) {
|
||||
Slog.e(LOG_TAG, "Device idle controller not reachable");
|
||||
}
|
||||
updateAllJobRestrictionsLocked();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
|
||||
super(service, context, lock);
|
||||
mJobSchedulerService = service;
|
||||
@@ -97,19 +64,6 @@ public final class BackgroundJobsController extends StateController {
|
||||
|
||||
mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
|
||||
|
||||
try {
|
||||
mPowerWhitelistedUserAppIds = mDeviceIdleController.getAppIdUserWhitelist();
|
||||
mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
|
||||
} catch (RemoteException rexc) {
|
||||
// Shouldn't happen as they are in the same process.
|
||||
Slog.e(LOG_TAG, "AppOps or DeviceIdle service not reachable", rexc);
|
||||
}
|
||||
IntentFilter powerWhitelistFilter = new IntentFilter();
|
||||
powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
|
||||
powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
|
||||
context.registerReceiverAsUser(mDozeWhitelistReceiver, UserHandle.ALL, powerWhitelistFilter,
|
||||
null, null);
|
||||
|
||||
mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
|
||||
mForceAppStandbyTracker.start();
|
||||
}
|
||||
@@ -128,31 +82,7 @@ public final class BackgroundJobsController extends StateController {
|
||||
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
|
||||
pw.println("BackgroundJobsController");
|
||||
|
||||
pw.print("Force all apps standby: ");
|
||||
pw.println(mForceAppStandbyTracker.isForceAllAppsStandbyEnabled());
|
||||
|
||||
pw.print("Foreground uids: [");
|
||||
final SparseBooleanArray foregroundUids = mForceAppStandbyTracker.getForegroudUids();
|
||||
|
||||
String sep = "";
|
||||
for (int i = 0; i < foregroundUids.size(); i++) {
|
||||
if (foregroundUids.valueAt(i)) {
|
||||
pw.print(sep);
|
||||
pw.print(UserHandle.formatUid(foregroundUids.keyAt(i)));
|
||||
sep = " ";
|
||||
}
|
||||
}
|
||||
pw.println("]");
|
||||
|
||||
pw.println("Restricted packages:");
|
||||
for (Pair<Integer, String> uidAndPackage
|
||||
: mForceAppStandbyTracker.getRestrictedUidPackages()) {
|
||||
pw.print(" ");
|
||||
pw.print(UserHandle.formatUid(uidAndPackage.first));
|
||||
pw.print(" ");
|
||||
pw.print(uidAndPackage.second);
|
||||
pw.println();
|
||||
}
|
||||
mForceAppStandbyTracker.dump(pw, "");
|
||||
|
||||
pw.println("Job state:");
|
||||
mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
|
||||
@@ -165,15 +95,17 @@ public final class BackgroundJobsController extends StateController {
|
||||
pw.print(" from ");
|
||||
UserHandle.formatUid(pw, uid);
|
||||
pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
|
||||
if (isWhitelistedLocked(uid)) {
|
||||
if (mForceAppStandbyTracker.isUidPowerSaveWhitelisted(uid) ||
|
||||
mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(uid)) {
|
||||
pw.print(", whitelisted");
|
||||
}
|
||||
pw.print(": ");
|
||||
pw.print(jobStatus.getSourcePackageName());
|
||||
|
||||
pw.print(" [background restrictions ");
|
||||
pw.print(" [RUN_ANY_IN_BACKGROUND ");
|
||||
pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
|
||||
jobStatus.getSourceUid(), jobStatus.getSourcePackageName()) ? "off]" : "on]");
|
||||
jobStatus.getSourceUid(), jobStatus.getSourcePackageName())
|
||||
? "allowed]" : "disallowed]");
|
||||
|
||||
if ((jobStatus.satisfiedConstraints
|
||||
& JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
|
||||
@@ -218,19 +150,12 @@ public final class BackgroundJobsController extends StateController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWhitelistedLocked(int uid) {
|
||||
final int appId = UserHandle.getAppId(uid);
|
||||
return ArrayUtils.contains(mTempWhitelistedAppIds, appId)
|
||||
|| ArrayUtils.contains(mPowerWhitelistedUserAppIds, appId);
|
||||
}
|
||||
|
||||
boolean updateSingleJobRestrictionLocked(JobStatus jobStatus) {
|
||||
|
||||
final int uid = jobStatus.getSourceUid();
|
||||
final String packageName = jobStatus.getSourcePackageName();
|
||||
|
||||
final boolean canRun = isWhitelistedLocked(uid)
|
||||
|| !mForceAppStandbyTracker.isRestricted(uid, packageName);
|
||||
final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName);
|
||||
|
||||
return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
|
||||
}
|
||||
@@ -261,13 +186,18 @@ public final class BackgroundJobsController extends StateController {
|
||||
|
||||
private final Listener mForceAppStandbyListener = new Listener() {
|
||||
@Override
|
||||
public void onRestrictionChanged(int uid, String packageName) {
|
||||
public void updateAllJobs() {
|
||||
updateAllJobRestrictionsLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateJobsForUid(int uid) {
|
||||
updateJobRestrictionsForUidLocked(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGlobalRestrictionChanged() {
|
||||
updateAllJobRestrictionsLocked();
|
||||
public void updateJobsForUidPackage(int uid, String packageName) {
|
||||
updateJobRestrictionsForUidLocked(uid);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.server;
|
||||
|
||||
import static android.app.AlarmManager.RTC;
|
||||
import static android.app.AlarmManager.RTC_WAKEUP;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.util.ObjectUtils;
|
||||
import com.android.server.AlarmManagerService.Alarm;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AlarmManagerServiceTest {
|
||||
private SparseArray<ArrayList<Alarm>> addPendingAlarm(
|
||||
SparseArray<ArrayList<Alarm>> all, int uid, String name, boolean removeIt) {
|
||||
ArrayList<Alarm> uidAlarms = all.get(uid);
|
||||
if (uidAlarms == null) {
|
||||
all.put(uid, uidAlarms = new ArrayList<>());
|
||||
}
|
||||
// Details don't matter.
|
||||
uidAlarms.add(new Alarm(
|
||||
removeIt ? RTC : RTC_WAKEUP,
|
||||
0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
|
||||
return all;
|
||||
}
|
||||
|
||||
private static String toString(SparseArray<ArrayList<Alarm>> pendingAlarms) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
String sep = "";
|
||||
for (int i = 0; i < pendingAlarms.size(); i++) {
|
||||
sb.append(sep);
|
||||
sep = ", ";
|
||||
sb.append("[");
|
||||
sb.append(pendingAlarms.keyAt(i));
|
||||
sb.append(": ");
|
||||
sb.append(toString(pendingAlarms.valueAt(i)));
|
||||
sb.append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String toString(ArrayList<Alarm> alarms) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
alarms.sort((a, b) -> ObjectUtils.compare(a.packageName, b.packageName));
|
||||
|
||||
String sep = "";
|
||||
for (Alarm a : alarms) {
|
||||
sb.append(sep);
|
||||
sep = ", ";
|
||||
sb.append(a.packageName);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void runCheckAllPendingAlarms(
|
||||
SparseArray<ArrayList<Alarm>> pending, ArrayList<Alarm> alarmsToDeliver) {
|
||||
// RTC_WAKEUP alarms are restricted.
|
||||
AlarmManagerService.findAllUnrestrictedPendingBackgroundAlarmsLockedInner(pending,
|
||||
alarmsToDeliver, alarm -> alarm.type == RTC_WAKEUP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllUnrestrictedPendingBackgroundAlarmsLockedInner_empty() {
|
||||
SparseArray<ArrayList<Alarm>> pending = new SparseArray<>();
|
||||
|
||||
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
|
||||
|
||||
runCheckAllPendingAlarms(pending, alarmsToDeliver);
|
||||
|
||||
assertEquals("", toString(pending));
|
||||
assertEquals("", toString(alarmsToDeliver));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllUnrestrictedPendingBackgroundAlarmsLockedInner_single_remove() {
|
||||
SparseArray<ArrayList<Alarm>> pending = new SparseArray<>();
|
||||
|
||||
addPendingAlarm(pending, 100001, "a1", false);
|
||||
|
||||
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
|
||||
|
||||
runCheckAllPendingAlarms(pending, alarmsToDeliver);
|
||||
|
||||
assertEquals("[100001: a1]", toString(pending));
|
||||
assertEquals("", toString(alarmsToDeliver));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllUnrestrictedPendingBackgroundAlarmsLockedInner_single_nonremove() {
|
||||
SparseArray<ArrayList<Alarm>> pending = new SparseArray<>();
|
||||
|
||||
addPendingAlarm(pending, 100001, "a1", true);
|
||||
|
||||
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
|
||||
runCheckAllPendingAlarms(pending, alarmsToDeliver);
|
||||
|
||||
|
||||
assertEquals("", toString(pending));
|
||||
assertEquals("a1", toString(alarmsToDeliver));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllUnrestrictedPendingBackgroundAlarmsLockedInner_complex() {
|
||||
SparseArray<ArrayList<Alarm>> pending = new SparseArray<>();
|
||||
|
||||
addPendingAlarm(pending, 100001, "a11", false);
|
||||
addPendingAlarm(pending, 100001, "a12", true);
|
||||
addPendingAlarm(pending, 100001, "a13", false);
|
||||
addPendingAlarm(pending, 100001, "a14", true);
|
||||
|
||||
addPendingAlarm(pending, 100002, "a21", false);
|
||||
|
||||
addPendingAlarm(pending, 100003, "a31", true);
|
||||
|
||||
addPendingAlarm(pending, 100004, "a41", false);
|
||||
addPendingAlarm(pending, 100004, "a42", false);
|
||||
|
||||
addPendingAlarm(pending, 100005, "a51", true);
|
||||
addPendingAlarm(pending, 100005, "a52", true);
|
||||
|
||||
addPendingAlarm(pending, 100006, "a61", true);
|
||||
addPendingAlarm(pending, 100006, "a62", false);
|
||||
addPendingAlarm(pending, 100006, "a63", true);
|
||||
addPendingAlarm(pending, 100006, "a64", false);
|
||||
|
||||
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
|
||||
runCheckAllPendingAlarms(pending, alarmsToDeliver);
|
||||
|
||||
|
||||
assertEquals("[100001: a11, a13], [100002: a21], [100004: a41, a42], [100006: a62, a64]",
|
||||
toString(pending));
|
||||
assertEquals("a12, a14, a31, a51, a52, a61, a63", toString(alarmsToDeliver));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAllUnrestrictedPendingBackgroundAlarmsLockedInner_complex_allRemove() {
|
||||
SparseArray<ArrayList<Alarm>> pending = new SparseArray<>();
|
||||
|
||||
addPendingAlarm(pending, 100001, "a11", true);
|
||||
addPendingAlarm(pending, 100001, "a12", true);
|
||||
addPendingAlarm(pending, 100001, "a13", true);
|
||||
addPendingAlarm(pending, 100001, "a14", true);
|
||||
|
||||
addPendingAlarm(pending, 100002, "a21", true);
|
||||
|
||||
addPendingAlarm(pending, 100003, "a31", true);
|
||||
|
||||
addPendingAlarm(pending, 100004, "a41", true);
|
||||
addPendingAlarm(pending, 100004, "a42", true);
|
||||
|
||||
addPendingAlarm(pending, 100005, "a51", true);
|
||||
addPendingAlarm(pending, 100005, "a52", true);
|
||||
|
||||
addPendingAlarm(pending, 100006, "a61", true);
|
||||
addPendingAlarm(pending, 100006, "a62", true);
|
||||
addPendingAlarm(pending, 100006, "a63", true);
|
||||
addPendingAlarm(pending, 100006, "a64", true);
|
||||
|
||||
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
|
||||
runCheckAllPendingAlarms(pending, alarmsToDeliver);
|
||||
|
||||
|
||||
assertEquals("", toString(pending));
|
||||
assertEquals("a11, a12, a13, a14, a21, a31, a41, a42, a51, a52, a61, a62, a63, a64",
|
||||
toString(alarmsToDeliver));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,900 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.server;
|
||||
|
||||
import static com.android.server.ForceAppStandbyTracker.TARGET_OP;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.AppOpsManager.OpEntry;
|
||||
import android.app.AppOpsManager.PackageOps;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.IUidObserver;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager.ServiceType;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.PowerSaveState;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.server.ForceAppStandbyTracker.Listener;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ForceAppStandbyTrackerTest {
|
||||
|
||||
private class ForceAppStandbyTrackerTestable extends ForceAppStandbyTracker {
|
||||
ForceAppStandbyTrackerTestable() {
|
||||
super(mMockContext, Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
AppOpsManager injectAppOpsManager() {
|
||||
return mMockAppOpsManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
IAppOpsService injectIAppOpsService() {
|
||||
return mMockIAppOpsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
IActivityManager injectIActivityManager() {
|
||||
return mMockIActivityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
PowerManagerInternal injectPowerManagerInternal() {
|
||||
return mMockPowerManagerInternal;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
|
||||
private static final int UID_2 = Process.FIRST_APPLICATION_UID + 2;
|
||||
private static final int UID_3 = Process.FIRST_APPLICATION_UID + 3;
|
||||
private static final int UID_10_1 = UserHandle.getUid(10, UID_1);
|
||||
private static final int UID_10_2 = UserHandle.getUid(10, UID_2);
|
||||
private static final int UID_10_3 = UserHandle.getUid(10, UID_3);
|
||||
private static final String PACKAGE_1 = "package1";
|
||||
private static final String PACKAGE_2 = "package2";
|
||||
private static final String PACKAGE_3 = "package3";
|
||||
private static final String PACKAGE_SYSTEM = "android";
|
||||
|
||||
private Handler mMainHandler;
|
||||
|
||||
@Mock
|
||||
private Context mMockContext;
|
||||
|
||||
@Mock
|
||||
private IActivityManager mMockIActivityManager;
|
||||
|
||||
@Mock
|
||||
private AppOpsManager mMockAppOpsManager;
|
||||
|
||||
@Mock
|
||||
private IAppOpsService mMockIAppOpsService;
|
||||
|
||||
@Mock
|
||||
private PowerManagerInternal mMockPowerManagerInternal;
|
||||
|
||||
private IUidObserver mIUidObserver;
|
||||
private IAppOpsCallback.Stub mAppOpsCallback;
|
||||
private Consumer<PowerSaveState> mPowerSaveObserver;
|
||||
private BroadcastReceiver mReceiver;
|
||||
|
||||
private boolean mPowerSaveMode;
|
||||
|
||||
private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMainHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
private void waitUntilMainHandlerDrain() throws Exception {
|
||||
final CountDownLatch l = new CountDownLatch(1);
|
||||
mMainHandler.post(() -> {
|
||||
l.countDown();
|
||||
});
|
||||
assertTrue(l.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private PowerSaveState getPowerSaveState() {
|
||||
return new PowerSaveState.Builder().setBatterySaverEnabled(mPowerSaveMode).build();
|
||||
}
|
||||
|
||||
private ForceAppStandbyTrackerTestable newInstance() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mMockIAppOpsService.checkOperation(eq(TARGET_OP), anyInt(), anyString()))
|
||||
.thenAnswer(inv -> {
|
||||
return mRestrictedPackages.indexOf(
|
||||
Pair.create(inv.getArgument(1), inv.getArgument(2))) >= 0 ?
|
||||
AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
|
||||
});
|
||||
|
||||
final ForceAppStandbyTrackerTestable instance = new ForceAppStandbyTrackerTestable();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException {
|
||||
|
||||
// Set up functions that start() calls.
|
||||
when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY)))
|
||||
.thenAnswer(inv -> getPowerSaveState());
|
||||
when(mMockAppOpsManager.getPackagesForOps(
|
||||
any(int[].class)
|
||||
)).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
|
||||
|
||||
// Call start.
|
||||
instance.start();
|
||||
|
||||
// Capture the listeners.
|
||||
ArgumentCaptor<IUidObserver> uidObserverArgumentCaptor =
|
||||
ArgumentCaptor.forClass(IUidObserver.class);
|
||||
ArgumentCaptor<IAppOpsCallback.Stub> appOpsCallbackCaptor =
|
||||
ArgumentCaptor.forClass(IAppOpsCallback.Stub.class);
|
||||
ArgumentCaptor<Consumer<PowerSaveState>> powerSaveObserverCaptor =
|
||||
ArgumentCaptor.forClass(Consumer.class);
|
||||
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
|
||||
ArgumentCaptor.forClass(BroadcastReceiver.class);
|
||||
|
||||
verify(mMockIActivityManager).registerUidObserver(
|
||||
uidObserverArgumentCaptor.capture(),
|
||||
eq(ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
|
||||
| ActivityManager.UID_OBSERVER_ACTIVE),
|
||||
eq(ActivityManager.PROCESS_STATE_UNKNOWN),
|
||||
isNull());
|
||||
verify(mMockIAppOpsService).startWatchingMode(
|
||||
eq(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND),
|
||||
isNull(),
|
||||
appOpsCallbackCaptor.capture());
|
||||
verify(mMockPowerManagerInternal).registerLowPowerModeObserver(
|
||||
eq(ServiceType.FORCE_ALL_APPS_STANDBY),
|
||||
powerSaveObserverCaptor.capture());
|
||||
|
||||
verify(mMockContext).registerReceiver(
|
||||
receiverCaptor.capture(), any(IntentFilter.class));
|
||||
|
||||
mIUidObserver = uidObserverArgumentCaptor.getValue();
|
||||
mAppOpsCallback = appOpsCallbackCaptor.getValue();
|
||||
mPowerSaveObserver = powerSaveObserverCaptor.getValue();
|
||||
mReceiver = receiverCaptor.getValue();
|
||||
|
||||
assertNotNull(mIUidObserver);
|
||||
assertNotNull(mAppOpsCallback);
|
||||
assertNotNull(mPowerSaveObserver);
|
||||
assertNotNull(mReceiver);
|
||||
}
|
||||
|
||||
private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
|
||||
final Pair p = Pair.create(uid, packageName);
|
||||
if (restrict) {
|
||||
mRestrictedPackages.add(p);
|
||||
} else {
|
||||
mRestrictedPackages.remove(p);
|
||||
}
|
||||
if (mAppOpsCallback != null) {
|
||||
mAppOpsCallback.opChanged(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int NONE = 0;
|
||||
private static final int ALARMS_ONLY = 1 << 0;
|
||||
private static final int JOBS_ONLY = 1 << 1;
|
||||
private static final int JOBS_AND_ALARMS = ALARMS_ONLY | JOBS_ONLY;
|
||||
|
||||
private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
|
||||
int restrictionTypes) {
|
||||
assertEquals(((restrictionTypes & JOBS_ONLY) != 0),
|
||||
instance.areJobsRestricted(uid, packageName));
|
||||
assertEquals(((restrictionTypes & ALARMS_ONLY) != 0),
|
||||
instance.areAlarmsRestricted(uid, packageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAll() throws Exception {
|
||||
final ForceAppStandbyTrackerTestable instance = newInstance();
|
||||
callStart(instance);
|
||||
|
||||
assertFalse(instance.isForceAllAppsStandbyEnabled());
|
||||
areRestricted(instance, UID_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, NONE);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
assertTrue(instance.isForceAllAppsStandbyEnabled());
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
// Toggle the foreground state.
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
assertFalse(instance.isInForeground(UID_1));
|
||||
assertFalse(instance.isInForeground(UID_2));
|
||||
assertTrue(instance.isInForeground(Process.SYSTEM_UID));
|
||||
|
||||
mIUidObserver.onUidActive(UID_1);
|
||||
areRestricted(instance, UID_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
assertTrue(instance.isInForeground(UID_1));
|
||||
assertFalse(instance.isInForeground(UID_2));
|
||||
|
||||
mIUidObserver.onUidGone(UID_1, /*disable=*/ false);
|
||||
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
assertFalse(instance.isInForeground(UID_1));
|
||||
assertFalse(instance.isInForeground(UID_2));
|
||||
|
||||
mIUidObserver.onUidActive(UID_1);
|
||||
areRestricted(instance, UID_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
mIUidObserver.onUidIdle(UID_1, /*disable=*/ false);
|
||||
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
assertFalse(instance.isInForeground(UID_1));
|
||||
assertFalse(instance.isInForeground(UID_2));
|
||||
|
||||
// Toggle the app ops.
|
||||
mPowerSaveMode = false;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_1, PACKAGE_1));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_1, PACKAGE_1));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, NONE);
|
||||
areRestricted(instance, UID_10_2, PACKAGE_2, NONE);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
setAppOps(UID_1, PACKAGE_1, true);
|
||||
setAppOps(UID_10_2, PACKAGE_2, true);
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_1, PACKAGE_1));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_1, PACKAGE_1));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, NONE);
|
||||
areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
// Toggle power saver, should still be the same.
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
mPowerSaveMode = false;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, NONE);
|
||||
areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
// Clear the app ops and update the whitelist.
|
||||
setAppOps(UID_1, PACKAGE_1, false);
|
||||
setAppOps(UID_10_2, PACKAGE_2, false);
|
||||
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_1, PACKAGE_1, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_1}, new int[] {UID_2});
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, ALARMS_ONLY);
|
||||
areRestricted(instance, UID_10_2, PACKAGE_2, ALARMS_ONLY);
|
||||
areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
// Again, make sure toggling the global state doesn't change it.
|
||||
mPowerSaveMode = false;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
areRestricted(instance, UID_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
|
||||
areRestricted(instance, UID_2, PACKAGE_2, ALARMS_ONLY);
|
||||
areRestricted(instance, UID_10_2, PACKAGE_2, ALARMS_ONLY);
|
||||
areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS);
|
||||
areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
|
||||
|
||||
assertTrue(instance.isUidPowerSaveWhitelisted(UID_1));
|
||||
assertTrue(instance.isUidPowerSaveWhitelisted(UID_10_1));
|
||||
assertFalse(instance.isUidPowerSaveWhitelisted(UID_2));
|
||||
assertFalse(instance.isUidPowerSaveWhitelisted(UID_10_2));
|
||||
|
||||
assertFalse(instance.isUidTempPowerSaveWhitelisted(UID_1));
|
||||
assertFalse(instance.isUidTempPowerSaveWhitelisted(UID_10_1));
|
||||
assertTrue(instance.isUidTempPowerSaveWhitelisted(UID_2));
|
||||
assertTrue(instance.isUidTempPowerSaveWhitelisted(UID_10_2));
|
||||
}
|
||||
|
||||
public void loadPersistedAppOps() throws Exception {
|
||||
final ForceAppStandbyTrackerTestable instance = newInstance();
|
||||
|
||||
final List<PackageOps> ops = new ArrayList<>();
|
||||
|
||||
//--------------------------------------------------
|
||||
List<OpEntry> entries = new ArrayList<>();
|
||||
entries.add(new AppOpsManager.OpEntry(
|
||||
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
|
||||
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
|
||||
entries.add(new AppOpsManager.OpEntry(
|
||||
ForceAppStandbyTracker.TARGET_OP,
|
||||
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
|
||||
|
||||
ops.add(new PackageOps(PACKAGE_1, UID_1, entries));
|
||||
|
||||
//--------------------------------------------------
|
||||
entries = new ArrayList<>();
|
||||
entries.add(new AppOpsManager.OpEntry(
|
||||
ForceAppStandbyTracker.TARGET_OP,
|
||||
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
|
||||
|
||||
ops.add(new PackageOps(PACKAGE_2, UID_2, entries));
|
||||
|
||||
//--------------------------------------------------
|
||||
entries = new ArrayList<>();
|
||||
entries.add(new AppOpsManager.OpEntry(
|
||||
ForceAppStandbyTracker.TARGET_OP,
|
||||
AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
|
||||
|
||||
ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries));
|
||||
|
||||
//--------------------------------------------------
|
||||
entries = new ArrayList<>();
|
||||
entries.add(new AppOpsManager.OpEntry(
|
||||
ForceAppStandbyTracker.TARGET_OP,
|
||||
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
|
||||
entries.add(new AppOpsManager.OpEntry(
|
||||
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
|
||||
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
|
||||
|
||||
ops.add(new PackageOps(PACKAGE_3, UID_10_3, entries));
|
||||
|
||||
callStart(instance);
|
||||
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_1, PACKAGE_1));
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_3, PACKAGE_3));
|
||||
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_1, PACKAGE_1));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_3, PACKAGE_3));
|
||||
}
|
||||
|
||||
private void assertNoCallbacks(Listener l) throws Exception {
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPowerSaveListener() throws Exception {
|
||||
final ForceAppStandbyTrackerTestable instance = newInstance();
|
||||
callStart(instance);
|
||||
|
||||
ForceAppStandbyTracker.Listener l = mock(ForceAppStandbyTracker.Listener.class);
|
||||
instance.addListener(l);
|
||||
|
||||
// Power save on.
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Power save off.
|
||||
mPowerSaveMode = false;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(1)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Power save on.
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
assertNoCallbacks(l);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllListeners() throws Exception {
|
||||
final ForceAppStandbyTrackerTestable instance = newInstance();
|
||||
callStart(instance);
|
||||
|
||||
ForceAppStandbyTracker.Listener l = mock(ForceAppStandbyTracker.Listener.class);
|
||||
instance.addListener(l);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Test with apppops.
|
||||
|
||||
setAppOps(UID_10_2, PACKAGE_2, true);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
setAppOps(UID_10_2, PACKAGE_2, false);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
|
||||
reset(l);
|
||||
|
||||
setAppOps(UID_10_2, PACKAGE_2, false);
|
||||
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
|
||||
// Unrestrict while battery saver is on. Shouldn't fire.
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
// Note toggling appops while BS is on will suppress unblockAlarmsForUidPackage().
|
||||
setAppOps(UID_10_2, PACKAGE_2, true);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Battery saver off.
|
||||
mPowerSaveMode = false;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(1)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests with system/user/temp whitelist.
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_1, UID_2}, new int[] {});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_2}, new int[] {});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(1)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Update temp whitelist.
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_2}, new int[] {UID_1, UID_3});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_2}, new int[] {UID_3});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Do the same thing with battery saver on. (Currently same callbacks are called.)
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_1, UID_2}, new int[] {});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_2}, new int[] {});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(1)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Update temp whitelist.
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_2}, new int[] {UID_1, UID_3});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
instance.setPowerSaveWhitelistAppIds(new int[] {UID_2}, new int[] {UID_3});
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(1)).updateAllJobs();
|
||||
verify(l, times(0)).updateJobsForUid(anyInt());
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests with proc state changes.
|
||||
|
||||
// With battery save.
|
||||
mPowerSaveMode = true;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
mIUidObserver.onUidActive(UID_10_1);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
mIUidObserver.onUidGone(UID_10_1, true);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
mIUidObserver.onUidActive(UID_10_1);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
mIUidObserver.onUidIdle(UID_10_1, true);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
// Without battery save.
|
||||
mPowerSaveMode = false;
|
||||
mPowerSaveObserver.accept(getPowerSaveState());
|
||||
|
||||
mIUidObserver.onUidActive(UID_10_1);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
mIUidObserver.onUidGone(UID_10_1, true);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
mIUidObserver.onUidActive(UID_10_1);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
|
||||
mIUidObserver.onUidIdle(UID_10_1, true);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
verify(l, times(0)).updateAllJobs();
|
||||
verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
|
||||
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
|
||||
|
||||
verify(l, times(0)).unblockAllUnrestrictedAlarms();
|
||||
verify(l, times(0)).unblockAlarmsForUid(anyInt());
|
||||
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
|
||||
reset(l);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserRemoved() throws Exception {
|
||||
final ForceAppStandbyTrackerTestable instance = newInstance();
|
||||
callStart(instance);
|
||||
|
||||
mIUidObserver.onUidActive(UID_1);
|
||||
mIUidObserver.onUidActive(UID_10_1);
|
||||
|
||||
setAppOps(UID_2, PACKAGE_2, true);
|
||||
setAppOps(UID_10_2, PACKAGE_2, true);
|
||||
|
||||
assertTrue(instance.isInForeground(UID_1));
|
||||
assertTrue(instance.isInForeground(UID_10_1));
|
||||
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
|
||||
|
||||
final Intent intent = new Intent(Intent.ACTION_USER_REMOVED);
|
||||
intent.putExtra(Intent.EXTRA_USER_HANDLE, 10);
|
||||
mReceiver.onReceive(mMockContext, intent);
|
||||
|
||||
waitUntilMainHandlerDrain();
|
||||
|
||||
assertTrue(instance.isInForeground(UID_1));
|
||||
assertFalse(instance.isInForeground(UID_10_1));
|
||||
|
||||
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
|
||||
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
|
||||
}
|
||||
|
||||
static int[] array(int... appIds) {
|
||||
Arrays.sort(appIds);
|
||||
return appIds;
|
||||
}
|
||||
|
||||
private final Random mRandom = new Random();
|
||||
|
||||
int[] makeRandomArray() {
|
||||
final ArrayList<Integer> list = new ArrayList<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (mRandom.nextDouble() < 0.5) {
|
||||
list.add(i);
|
||||
}
|
||||
}
|
||||
return Arrays.stream(list.toArray(new Integer[list.size()]))
|
||||
.mapToInt(Integer::intValue).toArray();
|
||||
}
|
||||
|
||||
static boolean isAnyAppIdUnwhitelistedSlow(int[] prevArray, int[] newArray) {
|
||||
Arrays.sort(newArray); // Just in case...
|
||||
for (int p : prevArray) {
|
||||
if (Arrays.binarySearch(newArray, p) < 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkAnyAppIdUnwhitelisted(int[] prevArray, int[] newArray, boolean expected) {
|
||||
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
|
||||
expected, ForceAppStandbyTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
|
||||
|
||||
// Also test isAnyAppIdUnwhitelistedSlow.
|
||||
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
|
||||
expected, isAnyAppIdUnwhitelistedSlow(prevArray, newArray));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAnyAppIdUnwhitelisted() {
|
||||
checkAnyAppIdUnwhitelisted(array(), array(), false);
|
||||
|
||||
checkAnyAppIdUnwhitelisted(array(1), array(), true);
|
||||
checkAnyAppIdUnwhitelisted(array(1), array(1), false);
|
||||
checkAnyAppIdUnwhitelisted(array(1), array(0, 1), false);
|
||||
checkAnyAppIdUnwhitelisted(array(1), array(0, 1, 2), false);
|
||||
checkAnyAppIdUnwhitelisted(array(1), array(0, 1, 2), false);
|
||||
|
||||
checkAnyAppIdUnwhitelisted(array(1, 2, 10), array(), true);
|
||||
checkAnyAppIdUnwhitelisted(array(1, 2, 10), array(1, 2), true);
|
||||
checkAnyAppIdUnwhitelisted(array(1, 2, 10), array(1, 2, 10), false);
|
||||
checkAnyAppIdUnwhitelisted(array(1, 2, 10), array(2, 10), true);
|
||||
checkAnyAppIdUnwhitelisted(array(1, 2, 10), array(0, 1, 2, 4, 3, 10), false);
|
||||
checkAnyAppIdUnwhitelisted(array(1, 2, 10), array(0, 0, 1, 2, 10), false);
|
||||
|
||||
// Random test
|
||||
int trueCount = 0;
|
||||
final int count = 10000;
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int[] array1 = makeRandomArray();
|
||||
final int[] array2 = makeRandomArray();
|
||||
|
||||
final boolean expected = isAnyAppIdUnwhitelistedSlow(array1, array2);
|
||||
final boolean actual = ForceAppStandbyTracker.isAnyAppIdUnwhitelisted(array1, array2);
|
||||
|
||||
assertEquals("Input: " + Arrays.toString(array1) + " " + Arrays.toString(array2),
|
||||
expected, actual);
|
||||
if (expected) {
|
||||
trueCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure makeRandomArray() didn't generate all same arrays by accident.
|
||||
assertTrue(trueCount > 0);
|
||||
assertTrue(trueCount < count);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user