Merge "Creating new RESTRICTED bucket."
This commit is contained in:
@@ -103,6 +103,8 @@ public interface AppStandbyInternal {
|
||||
/**
|
||||
* Changes an app's standby bucket to the provided value. The caller can only set the standby
|
||||
* bucket for a different app than itself.
|
||||
* If attempting to automatically place an app in the RESTRICTED bucket, use
|
||||
* {@link #restrictApp(String, int, int)} instead.
|
||||
*/
|
||||
void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid,
|
||||
int callingPid);
|
||||
@@ -113,6 +115,17 @@ public interface AppStandbyInternal {
|
||||
void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid,
|
||||
int callingPid);
|
||||
|
||||
/**
|
||||
* Put the specified app in the
|
||||
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
|
||||
* bucket. If it has been used by the user recently, the restriction will delayed until an
|
||||
* appropriate time.
|
||||
*
|
||||
* @param restrictReason The restrictReason for restricting the app. Should be one of the
|
||||
* UsageStatsManager.REASON_SUB_RESTRICT_* reasons.
|
||||
*/
|
||||
void restrictApp(@NonNull String packageName, int userId, int restrictReason);
|
||||
|
||||
void addActiveDeviceAdmin(String adminPkg, int userId);
|
||||
|
||||
void setActiveAdminApps(Set<String> adminPkgs, int userId);
|
||||
|
||||
@@ -277,6 +277,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
DeviceIdleInternal mLocalDeviceIdleController;
|
||||
AppStateTracker mAppStateTracker;
|
||||
final UsageStatsManagerInternal mUsageStats;
|
||||
private final AppStandbyInternal mAppStandbyInternal;
|
||||
|
||||
/**
|
||||
* Set to true once we are allowed to run third party apps.
|
||||
@@ -1062,7 +1063,8 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
packageName == null ? job.getService().getPackageName() : packageName;
|
||||
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
|
||||
Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
|
||||
// TODO(b/145551233): attempt to restrict app
|
||||
mAppStandbyInternal.restrictApp(
|
||||
pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
|
||||
if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION
|
||||
&& mPlatformCompat.isChangeEnabledByPackageName(
|
||||
CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) {
|
||||
@@ -1430,8 +1432,8 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
mConstants.API_QUOTA_SCHEDULE_COUNT,
|
||||
mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
|
||||
|
||||
AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
|
||||
appStandby.addListener(mStandbyTracker);
|
||||
mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
|
||||
mAppStandbyInternal.addListener(mStandbyTracker);
|
||||
|
||||
// The job store needs to call back
|
||||
publishLocalService(JobSchedulerInternal.class, new LocalService());
|
||||
|
||||
@@ -25,8 +25,11 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACT
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
|
||||
|
||||
import static com.android.server.usage.AppStandbyController.isUserUsage;
|
||||
|
||||
import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.os.SystemClock;
|
||||
@@ -81,6 +84,8 @@ public class AppIdleHistory {
|
||||
private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
|
||||
// Elapsed timebase time when app was last used
|
||||
private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
|
||||
// Elapsed timebase time when app was last used by the user
|
||||
private static final String ATTR_LAST_USED_BY_USER_ELAPSED = "lastUsedByUserElapsedTime";
|
||||
// Elapsed timebase time when the app bucket was last predicted externally
|
||||
private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
|
||||
// The standby bucket for the app
|
||||
@@ -93,6 +98,12 @@ public class AppIdleHistory {
|
||||
private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
|
||||
// The time when the forced working_set state can be overridden.
|
||||
private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
|
||||
// Elapsed timebase time when the app was last marked for restriction.
|
||||
private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED =
|
||||
"lastRestrictionAttemptElapsedTime";
|
||||
// Reason why the app was last marked for restriction.
|
||||
private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON =
|
||||
"lastRestrictionAttemptReason";
|
||||
|
||||
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
|
||||
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
|
||||
@@ -107,8 +118,10 @@ public class AppIdleHistory {
|
||||
private boolean mScreenOn;
|
||||
|
||||
static class AppUsageHistory {
|
||||
// Last used time using elapsed timebase
|
||||
// Last used time (including system usage), using elapsed timebase
|
||||
long lastUsedElapsedTime;
|
||||
// Last time the user used the app, using elapsed timebase
|
||||
long lastUsedByUserElapsedTime;
|
||||
// Last used time using screen_on timebase
|
||||
long lastUsedScreenTime;
|
||||
// Last predicted time using elapsed timebase
|
||||
@@ -136,6 +149,10 @@ public class AppIdleHistory {
|
||||
// under any active state timeout, so that it becomes applicable after the active state
|
||||
// timeout expires.
|
||||
long bucketWorkingSetTimeoutTime;
|
||||
// The last time an agent attempted to put the app into the RESTRICTED bucket.
|
||||
long lastRestrictAttemptElapsedTime;
|
||||
// The last reason the app was marked to be put into the RESTRICTED bucket.
|
||||
int lastRestrictReason;
|
||||
}
|
||||
|
||||
AppIdleHistory(File storageDir, long elapsedRealtime) {
|
||||
@@ -229,25 +246,37 @@ public class AppIdleHistory {
|
||||
*/
|
||||
public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
|
||||
int newBucket, int usageReason, long elapsedRealtime, long timeout) {
|
||||
// Set the timeout if applicable
|
||||
if (timeout > elapsedRealtime) {
|
||||
// Convert to elapsed timebase
|
||||
final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
|
||||
if (newBucket == STANDBY_BUCKET_ACTIVE) {
|
||||
appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
|
||||
appUsageHistory.bucketActiveTimeoutTime);
|
||||
} else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
|
||||
appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
|
||||
appUsageHistory.bucketWorkingSetTimeoutTime);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot set a timeout on bucket=" +
|
||||
newBucket);
|
||||
int bucketingReason = REASON_MAIN_USAGE | usageReason;
|
||||
final boolean isUserUsage = isUserUsage(bucketingReason);
|
||||
|
||||
if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) {
|
||||
// Only user usage should bring an app out of the RESTRICTED bucket.
|
||||
newBucket = STANDBY_BUCKET_RESTRICTED;
|
||||
bucketingReason = appUsageHistory.bucketingReason;
|
||||
} else {
|
||||
// Set the timeout if applicable
|
||||
if (timeout > elapsedRealtime) {
|
||||
// Convert to elapsed timebase
|
||||
final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
|
||||
if (newBucket == STANDBY_BUCKET_ACTIVE) {
|
||||
appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
|
||||
appUsageHistory.bucketActiveTimeoutTime);
|
||||
} else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
|
||||
appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
|
||||
appUsageHistory.bucketWorkingSetTimeoutTime);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot set a timeout on bucket="
|
||||
+ newBucket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elapsedRealtime != 0) {
|
||||
appUsageHistory.lastUsedElapsedTime = mElapsedDuration
|
||||
+ (elapsedRealtime - mElapsedSnapshot);
|
||||
if (isUserUsage) {
|
||||
appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime;
|
||||
}
|
||||
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
|
||||
}
|
||||
|
||||
@@ -259,7 +288,7 @@ public class AppIdleHistory {
|
||||
+ ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
|
||||
}
|
||||
}
|
||||
appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
|
||||
appUsageHistory.bucketingReason = bucketingReason;
|
||||
|
||||
return appUsageHistory;
|
||||
}
|
||||
@@ -385,6 +414,24 @@ public class AppIdleHistory {
|
||||
appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes an attempt to put the app in the {@link UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
|
||||
* bucket.
|
||||
*
|
||||
* @param packageName The package name of the app that is being restricted
|
||||
* @param userId The ID of the user in which the app is being restricted
|
||||
* @param elapsedRealtime The time the attempt was made, in the (unadjusted) elapsed realtime
|
||||
* timebase
|
||||
* @param reason The reason for the restriction attempt
|
||||
*/
|
||||
void noteRestrictionAttempt(String packageName, int userId, long elapsedRealtime, int reason) {
|
||||
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
|
||||
AppUsageHistory appUsageHistory =
|
||||
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
|
||||
appUsageHistory.lastRestrictAttemptElapsedTime = getElapsedTime(elapsedRealtime);
|
||||
appUsageHistory.lastRestrictReason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time since the last job was run for this app. This can be larger than the
|
||||
* current elapsedRealtime, in case it happened before boot or a really large value if no jobs
|
||||
@@ -547,6 +594,9 @@ public class AppIdleHistory {
|
||||
AppUsageHistory appUsageHistory = new AppUsageHistory();
|
||||
appUsageHistory.lastUsedElapsedTime =
|
||||
Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
|
||||
appUsageHistory.lastUsedByUserElapsedTime = getLongValue(parser,
|
||||
ATTR_LAST_USED_BY_USER_ELAPSED,
|
||||
appUsageHistory.lastUsedElapsedTime);
|
||||
appUsageHistory.lastUsedScreenTime =
|
||||
Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
|
||||
appUsageHistory.lastPredictedTime = getLongValue(parser,
|
||||
@@ -570,6 +620,19 @@ public class AppIdleHistory {
|
||||
appUsageHistory.bucketingReason =
|
||||
Integer.parseInt(bucketingReason, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
Slog.wtf(TAG, "Unable to read bucketing reason", nfe);
|
||||
}
|
||||
}
|
||||
appUsageHistory.lastRestrictAttemptElapsedTime =
|
||||
getLongValue(parser, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED, 0);
|
||||
String lastRestrictReason = parser.getAttributeValue(
|
||||
null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON);
|
||||
if (lastRestrictReason != null) {
|
||||
try {
|
||||
appUsageHistory.lastRestrictReason =
|
||||
Integer.parseInt(lastRestrictReason, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
Slog.wtf(TAG, "Unable to read last restrict reason", nfe);
|
||||
}
|
||||
}
|
||||
appUsageHistory.lastInformedBucket = -1;
|
||||
@@ -618,6 +681,8 @@ public class AppIdleHistory {
|
||||
xml.attribute(null, ATTR_NAME, packageName);
|
||||
xml.attribute(null, ATTR_ELAPSED_IDLE,
|
||||
Long.toString(history.lastUsedElapsedTime));
|
||||
xml.attribute(null, ATTR_LAST_USED_BY_USER_ELAPSED,
|
||||
Long.toString(history.lastUsedByUserElapsedTime));
|
||||
xml.attribute(null, ATTR_SCREEN_IDLE,
|
||||
Long.toString(history.lastUsedScreenTime));
|
||||
xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
|
||||
@@ -638,6 +703,12 @@ public class AppIdleHistory {
|
||||
xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
|
||||
.lastJobRunTime));
|
||||
}
|
||||
if (history.lastRestrictAttemptElapsedTime > 0) {
|
||||
xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED,
|
||||
Long.toString(history.lastRestrictAttemptElapsedTime));
|
||||
}
|
||||
xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON,
|
||||
Integer.toHexString(history.lastRestrictReason));
|
||||
xml.endTag(null, TAG_PACKAGE);
|
||||
}
|
||||
|
||||
@@ -672,6 +743,9 @@ public class AppIdleHistory {
|
||||
+ UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
|
||||
idpw.print(" used=");
|
||||
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
|
||||
idpw.print(" usedByUser=");
|
||||
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedByUserElapsedTime,
|
||||
idpw);
|
||||
idpw.print(" usedScr=");
|
||||
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
|
||||
idpw.print(" lastPred=");
|
||||
@@ -684,6 +758,13 @@ public class AppIdleHistory {
|
||||
idpw);
|
||||
idpw.print(" lastJob=");
|
||||
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
|
||||
if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) {
|
||||
idpw.print(" lastRestrictAttempt=");
|
||||
TimeUtils.formatDuration(
|
||||
totalElapsedTime - appUsageHistory.lastRestrictAttemptElapsedTime, idpw);
|
||||
idpw.print(" lastRestrictReason="
|
||||
+ UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason));
|
||||
}
|
||||
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
|
||||
idpw.println();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
|
||||
@@ -44,6 +45,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
|
||||
|
||||
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
|
||||
@@ -73,6 +75,7 @@ import android.net.Network;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.NetworkScoreManager;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.IDeviceIdleController;
|
||||
@@ -93,7 +96,9 @@ import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.TimeUtils;
|
||||
import android.view.Display;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
@@ -124,7 +129,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
public class AppStandbyController implements AppStandbyInternal {
|
||||
|
||||
private static final String TAG = "AppStandbyController";
|
||||
static final boolean DEBUG = false;
|
||||
static final boolean DEBUG = true;
|
||||
|
||||
static final boolean COMPRESS_TIME = false;
|
||||
private static final long ONE_MINUTE = 60 * 1000;
|
||||
@@ -615,6 +620,16 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
|
||||
}
|
||||
}
|
||||
|
||||
if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
|
||||
&& elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
|
||||
>= mInjector.getRestrictedBucketDelayMs()) {
|
||||
newBucket = STANDBY_BUCKET_RESTRICTED;
|
||||
reason = app.lastRestrictReason;
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, " Old bucket=" + oldBucket
|
||||
+ ", newBucket=" + newBucket);
|
||||
@@ -733,15 +748,16 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
|
||||
nextCheckTime = mStrongUsageTimeoutMillis;
|
||||
}
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage
|
||||
(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
|
||||
nextCheckTime);
|
||||
final boolean userStartedInteracting =
|
||||
appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
|
||||
prevBucket != appHistory.currentBucket &&
|
||||
(prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
|
||||
maybeInformListeners(pkg, userId, elapsedRealtime,
|
||||
appHistory.currentBucket, reason, userStartedInteracting);
|
||||
if (appHistory.currentBucket != prevBucket) {
|
||||
mHandler.sendMessageDelayed(
|
||||
mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
|
||||
nextCheckTime);
|
||||
final boolean userStartedInteracting =
|
||||
appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
|
||||
&& (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
|
||||
maybeInformListeners(pkg, userId, elapsedRealtime,
|
||||
appHistory.currentBucket, reason, userStartedInteracting);
|
||||
}
|
||||
|
||||
if (previouslyIdle) {
|
||||
notifyBatteryStats(pkg, userId, false);
|
||||
@@ -923,6 +939,15 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isUserUsage(int reason) {
|
||||
if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) {
|
||||
final int subReason = reason & REASON_SUB_MASK;
|
||||
return subReason == REASON_SUB_USAGE_USER_INTERACTION
|
||||
|| subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getIdleUidsForUser(int userId) {
|
||||
if (!mAppIdleEnabled) {
|
||||
@@ -1016,6 +1041,20 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restrictApp(@NonNull String packageName, int userId, int restrictReason) {
|
||||
// If the package is not installed, don't allow the bucket to be set.
|
||||
if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
|
||||
Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
|
||||
return;
|
||||
}
|
||||
|
||||
final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason);
|
||||
final long nowElapsed = mInjector.elapsedRealtime();
|
||||
setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RESTRICTED, reason,
|
||||
nowElapsed, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
|
||||
int callingUid, int callingPid) {
|
||||
@@ -1080,6 +1119,7 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
synchronized (mAppIdleLock) {
|
||||
// If the package is not installed, don't allow the bucket to be set.
|
||||
if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
|
||||
Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
|
||||
return;
|
||||
}
|
||||
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
|
||||
@@ -1089,8 +1129,9 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
// Don't allow changing bucket if higher than ACTIVE
|
||||
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
|
||||
|
||||
// Don't allow prediction to change from/to NEVER
|
||||
// Don't allow prediction to change from/to NEVER or from RESTRICTED.
|
||||
if ((app.currentBucket == STANDBY_BUCKET_NEVER
|
||||
|| app.currentBucket == STANDBY_BUCKET_RESTRICTED
|
||||
|| newBucket == STANDBY_BUCKET_NEVER)
|
||||
&& predicted) {
|
||||
return;
|
||||
@@ -1103,6 +1144,50 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isForcedByUser =
|
||||
(reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
|
||||
|
||||
// If the current bucket is RESTRICTED, only user force or usage should bring it out.
|
||||
if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
|
||||
&& !isForcedByUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newBucket == STANDBY_BUCKET_RESTRICTED) {
|
||||
mAppIdleHistory
|
||||
.noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
|
||||
|
||||
if (isForcedByUser) {
|
||||
// Only user force can bypass the delay restriction. If the user forced the
|
||||
// app into the RESTRICTED bucket, then a toast confirming the action
|
||||
// shouldn't be surprising.
|
||||
if (Build.IS_DEBUGGABLE) {
|
||||
Toast.makeText(mContext,
|
||||
// Since AppStandbyController sits low in the lock hierarchy,
|
||||
// make sure not to call out with the lock held.
|
||||
mHandler.getLooper(),
|
||||
mContext.getResources().getString(
|
||||
R.string.as_app_forced_to_restricted_bucket, packageName),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
Slog.i(TAG, packageName + " restricted by user");
|
||||
}
|
||||
} else {
|
||||
final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
|
||||
+ mInjector.getRestrictedBucketDelayMs() - elapsedRealtime;
|
||||
if (timeUntilRestrictPossibleMs > 0) {
|
||||
Slog.w(TAG, "Tried to restrict recently used app: " + packageName
|
||||
+ " due to " + reason);
|
||||
mHandler.sendMessageDelayed(
|
||||
mHandler.obtainMessage(
|
||||
MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
|
||||
timeUntilRestrictPossibleMs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the bucket is required to stay in a higher state for a specified duration, don't
|
||||
// override unless the duration has passed
|
||||
if (predicted) {
|
||||
@@ -1435,6 +1520,12 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
private DisplayManager mDisplayManager;
|
||||
private PowerManager mPowerManager;
|
||||
int mBootPhase;
|
||||
/**
|
||||
* The minimum amount of time required since the last user interaction before an app can be
|
||||
* placed in the RESTRICTED bucket.
|
||||
*/
|
||||
// TODO: make configurable via DeviceConfig
|
||||
private long mRestrictedBucketDelayMs = ONE_DAY;
|
||||
|
||||
Injector(Context context, Looper looper) {
|
||||
mContext = context;
|
||||
@@ -1459,6 +1550,12 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
mDisplayManager = (DisplayManager) mContext.getSystemService(
|
||||
Context.DISPLAY_SERVICE);
|
||||
mPowerManager = mContext.getSystemService(PowerManager.class);
|
||||
|
||||
final ActivityManager activityManager =
|
||||
(ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
|
||||
mRestrictedBucketDelayMs = 12 * ONE_HOUR;
|
||||
}
|
||||
}
|
||||
mBootPhase = phase;
|
||||
}
|
||||
@@ -1498,6 +1595,10 @@ public class AppStandbyController implements AppStandbyInternal {
|
||||
return Environment.getDataSystemDirectory();
|
||||
}
|
||||
|
||||
long getRestrictedBucketDelayMs() {
|
||||
return mRestrictedBucketDelayMs;
|
||||
}
|
||||
|
||||
void noteEvent(int event, String packageName, int uid) throws RemoteException {
|
||||
mBatteryStats.noteEvent(event, packageName, uid);
|
||||
}
|
||||
|
||||
@@ -8030,6 +8030,7 @@ package android.app.usage {
|
||||
field public static final int STANDBY_BUCKET_ACTIVE = 10; // 0xa
|
||||
field public static final int STANDBY_BUCKET_FREQUENT = 30; // 0x1e
|
||||
field public static final int STANDBY_BUCKET_RARE = 40; // 0x28
|
||||
field public static final int STANDBY_BUCKET_RESTRICTED = 45; // 0x2d
|
||||
field public static final int STANDBY_BUCKET_WORKING_SET = 20; // 0x14
|
||||
}
|
||||
|
||||
|
||||
@@ -142,13 +142,22 @@ public final class UsageStatsManager {
|
||||
|
||||
/**
|
||||
* The app has not be used for several days and/or is unlikely to be used for several days.
|
||||
* Apps in this bucket will have the most restrictions, including network restrictions, except
|
||||
* Apps in this bucket will have more restrictions, including network restrictions, except
|
||||
* during certain short periods (at a minimum, once a day) when they are allowed to execute
|
||||
* jobs, access the network, etc.
|
||||
* @see #getAppStandbyBucket()
|
||||
*/
|
||||
public static final int STANDBY_BUCKET_RARE = 40;
|
||||
|
||||
/**
|
||||
* The app has not be used for several days, is unlikely to be used for several days, and has
|
||||
* been misbehaving in some manner.
|
||||
* Apps in this bucket will have the most restrictions, including network restrictions and
|
||||
* additional restrictions on jobs.
|
||||
* @see #getAppStandbyBucket()
|
||||
*/
|
||||
public static final int STANDBY_BUCKET_RESTRICTED = 45;
|
||||
|
||||
/**
|
||||
* The app has never been used.
|
||||
* {@hide}
|
||||
@@ -278,6 +287,26 @@ public final class UsageStatsManager {
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
|
||||
/**
|
||||
* The reason for restricting the app is unknown or undefined.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000;
|
||||
/**
|
||||
* The app was unnecessarily using system resources (battery, memory, etc) in the background.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001;
|
||||
/**
|
||||
* The app was deemed to be intentionally abusive.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002;
|
||||
/**
|
||||
* The app was displaying buggy behavior.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003;
|
||||
|
||||
|
||||
/** @hide */
|
||||
@@ -287,6 +316,7 @@ public final class UsageStatsManager {
|
||||
STANDBY_BUCKET_WORKING_SET,
|
||||
STANDBY_BUCKET_FREQUENT,
|
||||
STANDBY_BUCKET_RARE,
|
||||
STANDBY_BUCKET_RESTRICTED,
|
||||
STANDBY_BUCKET_NEVER,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -598,7 +628,7 @@ public final class UsageStatsManager {
|
||||
* state of the app based on app usage patterns. Standby buckets determine how much an app will
|
||||
* be restricted from running background tasks such as jobs and alarms.
|
||||
* <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
|
||||
* {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
|
||||
* {@link #STANDBY_BUCKET_RESTRICTED}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
|
||||
* restrictive. The battery level of the device might also affect the restrictions.
|
||||
* <p>Apps in buckets ≤ {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed.
|
||||
* Apps in buckets > {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when
|
||||
@@ -642,7 +672,8 @@ public final class UsageStatsManager {
|
||||
/**
|
||||
* {@hide}
|
||||
* Changes an app's standby bucket to the provided value. The caller can only set the standby
|
||||
* bucket for a different app than itself.
|
||||
* bucket for a different app than itself. The caller will not be able to change an app's
|
||||
* standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket.
|
||||
* @param packageName the package name of the app to set the bucket for. A SecurityException
|
||||
* will be thrown if the package name is that of the caller.
|
||||
* @param bucket the standby bucket to set it to, which should be one of STANDBY_BUCKET_*.
|
||||
@@ -688,7 +719,8 @@ public final class UsageStatsManager {
|
||||
/**
|
||||
* {@hide}
|
||||
* Changes the app standby bucket for multiple apps at once. The Map is keyed by the package
|
||||
* name and the value is one of STANDBY_BUCKET_*.
|
||||
* name and the value is one of STANDBY_BUCKET_*. The caller will not be able to change an
|
||||
* app's standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket.
|
||||
* @param appBuckets a map of package name to bucket value.
|
||||
*/
|
||||
@SystemApi
|
||||
@@ -1027,6 +1059,20 @@ public final class UsageStatsManager {
|
||||
break;
|
||||
case REASON_MAIN_FORCED_BY_SYSTEM:
|
||||
sb.append("s");
|
||||
switch (standbyReason & REASON_SUB_MASK) {
|
||||
case REASON_SUB_RESTRICT_ABUSE:
|
||||
sb.append("-ra");
|
||||
break;
|
||||
case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE:
|
||||
sb.append("-rbru");
|
||||
break;
|
||||
case REASON_SUB_RESTRICT_BUGGY:
|
||||
sb.append("-rb");
|
||||
break;
|
||||
case REASON_SUB_RESTRICT_UNDEFINED:
|
||||
sb.append("-r");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case REASON_MAIN_FORCED_BY_USER:
|
||||
sb.append("f");
|
||||
|
||||
@@ -5315,4 +5315,8 @@
|
||||
|
||||
<!-- Accessibility description of caption view -->
|
||||
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
|
||||
|
||||
<!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] -->
|
||||
<string name="as_app_forced_to_restricted_bucket">
|
||||
<xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
|
||||
</resources>
|
||||
|
||||
@@ -3810,6 +3810,9 @@
|
||||
<java-symbol type="string" name="config_rawContactsLocalAccountName" />
|
||||
<java-symbol type="string" name="config_rawContactsLocalAccountType" />
|
||||
|
||||
<!-- For App Standby -->
|
||||
<java-symbol type="string" name="as_app_forced_to_restricted_bucket" />
|
||||
|
||||
<!-- Assistant handles -->
|
||||
<java-symbol type="dimen" name="assist_handle_shadow_radius" />
|
||||
|
||||
|
||||
@@ -2391,6 +2391,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
|
||||
} else if (lower.startsWith("ra")) {
|
||||
return UsageStatsManager.STANDBY_BUCKET_RARE;
|
||||
} else if (lower.startsWith("re")) {
|
||||
return UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||
} else if (lower.startsWith("ne")) {
|
||||
return UsageStatsManager.STANDBY_BUCKET_NEVER;
|
||||
} else {
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
|
||||
package com.android.server.usage;
|
||||
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.os.FileUtils;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
@@ -37,10 +37,12 @@ public class AppIdleHistoryTests extends AndroidTestCase {
|
||||
|
||||
File mStorageDir;
|
||||
|
||||
final static String PACKAGE_1 = "com.android.testpackage1";
|
||||
final static String PACKAGE_2 = "com.android.testpackage2";
|
||||
private static final String PACKAGE_1 = "com.android.testpackage1";
|
||||
private static final String PACKAGE_2 = "com.android.testpackage2";
|
||||
private static final String PACKAGE_3 = "com.android.testpackage3";
|
||||
private static final String PACKAGE_4 = "com.android.testpackage4";
|
||||
|
||||
final static int USER_ID = 0;
|
||||
private static final int USER_ID = 0;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
@@ -100,16 +102,27 @@ public class AppIdleHistoryTests extends AndroidTestCase {
|
||||
|
||||
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
|
||||
REASON_MAIN_USAGE);
|
||||
aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
|
||||
aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
|
||||
REASON_MAIN_TIMEOUT);
|
||||
|
||||
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
|
||||
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
|
||||
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
|
||||
assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
|
||||
assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
|
||||
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
|
||||
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
|
||||
// RARE is considered idle
|
||||
// RARE and RESTRICTED are considered idle
|
||||
assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
|
||||
assertFalse(aih.isIdle(PACKAGE_2, USER_ID, 3000));
|
||||
assertTrue(aih.isIdle(PACKAGE_3, USER_ID, 3000));
|
||||
assertTrue(aih.isIdle(PACKAGE_4, USER_ID, 3000));
|
||||
|
||||
// Check persistence
|
||||
aih.writeAppIdleDurations();
|
||||
@@ -118,6 +131,11 @@ public class AppIdleHistoryTests extends AndroidTestCase {
|
||||
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
|
||||
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
|
||||
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
|
||||
assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
|
||||
assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
|
||||
REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
|
||||
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
|
||||
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
|
||||
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
|
||||
|
||||
@@ -28,11 +28,17 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
|
||||
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -46,6 +52,8 @@ import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.usage.AppStandbyInfo;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
@@ -124,6 +132,13 @@ public class AppStandbyControllerTests {
|
||||
public PackageManager getPackageManager() {
|
||||
return mockPm;
|
||||
}
|
||||
|
||||
public Object getSystemService(@NonNull String name) {
|
||||
if (Context.ACTIVITY_SERVICE.equals(name)) {
|
||||
return mock(ActivityManager.class);
|
||||
}
|
||||
return super.getSystemService(name);
|
||||
}
|
||||
}
|
||||
|
||||
static class MyInjector extends AppStandbyController.Injector {
|
||||
@@ -253,7 +268,10 @@ public class AppStandbyControllerTests {
|
||||
|
||||
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
|
||||
try {
|
||||
doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt());
|
||||
doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
|
||||
doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
|
||||
anyInt());
|
||||
doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
|
||||
anyInt(), anyInt());
|
||||
doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
|
||||
@@ -468,7 +486,7 @@ public class AppStandbyControllerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPredictionTimedout() throws Exception {
|
||||
public void testPredictionTimedOut() throws Exception {
|
||||
// Set it to timeout or usage, so that prediction can override it
|
||||
mInjector.mElapsedRealtime = HOUR_MS;
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
|
||||
@@ -532,6 +550,79 @@ public class AppStandbyControllerTests {
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
|
||||
REASON_MAIN_PREDICTED);
|
||||
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
// Prediction can't remove from RESTRICTED
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_PREDICTED);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_PREDICTED);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
// Force from user can remove from RESTRICTED
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
// Force from system can remove from RESTRICTED if it was put it in due to system
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_PREDICTED);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
// Non-user usage can't remove from RESTRICTED
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_USAGE);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_USAGE | REASON_SUB_USAGE_SYSTEM_INTERACTION);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_USAGE | REASON_SUB_USAGE_SYNC_ADAPTER);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_USAGE | REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE);
|
||||
assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
|
||||
|
||||
// Explicit user usage can remove from RESTRICTED
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_USER);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
|
||||
REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION);
|
||||
assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
|
||||
REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND);
|
||||
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -556,6 +647,55 @@ public class AppStandbyControllerTests {
|
||||
assertBucket(STANDBY_BUCKET_RARE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that setAppStandbyBucket to RESTRICTED doesn't change the bucket until the usage
|
||||
* timeout has passed.
|
||||
*/
|
||||
@Test
|
||||
public void testTimeoutBeforeRestricted() throws Exception {
|
||||
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
// Bucket shouldn't change
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
// bucketing works after timeout
|
||||
mInjector.mElapsedRealtime += DAY_MS;
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
assertBucket(STANDBY_BUCKET_RESTRICTED);
|
||||
|
||||
// Way past all timeouts. Make sure timeout processing doesn't raise bucket.
|
||||
mInjector.mElapsedRealtime += RARE_THRESHOLD * 4;
|
||||
mController.checkIdleStates(USER_ID);
|
||||
assertBucket(STANDBY_BUCKET_RESTRICTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an app is put into the RESTRICTED bucket after enough time has passed.
|
||||
*/
|
||||
@Test
|
||||
public void testRestrictedDelay() throws Exception {
|
||||
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000;
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
|
||||
REASON_MAIN_FORCED_BY_SYSTEM);
|
||||
// Bucket shouldn't change
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
// bucketing works after timeout
|
||||
mInjector.mElapsedRealtime += 6000;
|
||||
|
||||
Thread.sleep(6000);
|
||||
// Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
|
||||
assertBucket(STANDBY_BUCKET_RESTRICTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCascadingTimeouts() throws Exception {
|
||||
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
|
||||
|
||||
Reference in New Issue
Block a user