summaryrefslogtreecommitdiff
path: root/apex/jobscheduler
diff options
context:
space:
mode:
authorKweku Adams <kwekua@google.com>2020-05-14 14:47:36 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-05-14 14:47:36 +0000
commit84e2f0c5821a6bb6d0b945673323879ec67e9cd2 (patch)
treec59ff0b58e0b99b0b23f4dcb5929d1a45cc9e13d /apex/jobscheduler
parentfc48f7e52a9e3f0ad0943db383f33d6f9bbb8899 (diff)
parent95cd952b470e2369749ab9cc98c85714157344cd (diff)
Merge "Partially exempt headless system apps from app standby." into rvc-dev
Diffstat (limited to 'apex/jobscheduler')
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java121
2 files changed, 107 insertions, 24 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 46d449a9257c..372ec981df02 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -79,6 +79,12 @@ public class AppIdleHistory {
private static final int STANDBY_BUCKET_UNKNOWN = -1;
+ /**
+ * The bucket beyond which apps are considered idle. Any apps in this bucket or lower are
+ * considered idle while those in higher buckets are not considered idle.
+ */
+ static final int IDLE_BUCKET_CUTOFF = STANDBY_BUCKET_RARE;
+
@VisibleForTesting
static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
private static final String TAG_PACKAGES = "packages";
@@ -350,7 +356,7 @@ public class AppIdleHistory {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
- return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
+ return appUsageHistory.currentBucket >= IDLE_BUCKET_CUTOFF;
}
public AppUsageHistory getAppUsageHistory(String packageName, int userId,
@@ -487,7 +493,7 @@ public class AppIdleHistory {
final int newBucket;
final int reason;
if (idle) {
- newBucket = STANDBY_BUCKET_RARE;
+ newBucket = IDLE_BUCKET_CUTOFF;
reason = REASON_MAIN_FORCED_BY_USER;
} else {
newBucket = STANDBY_BUCKET_ACTIVE;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 980372d58f33..2834ab14f28d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -54,6 +54,7 @@ import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -92,6 +93,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -227,6 +229,13 @@ public class AppStandbyController implements AppStandbyInternal {
@GuardedBy("mActiveAdminApps")
private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
+ /**
+ * Set of system apps that are headless (don't have any declared activities, enabled or
+ * disabled). Presence in this map indicates that the app is a headless system app.
+ */
+ @GuardedBy("mAppIdleLock")
+ private final ArrayMap<String, Boolean> mHeadlessSystemApps = new ArrayMap<>();
+
private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
// Messages for the handler
@@ -667,20 +676,22 @@ public class AppStandbyController implements AppStandbyInternal {
return;
}
}
- final boolean isSpecial = isAppSpecial(packageName,
+ final int minBucket = getAppMinBucket(packageName,
UserHandle.getAppId(uid),
userId);
if (DEBUG) {
- Slog.d(TAG, " Checking idle state for " + packageName + " special=" +
- isSpecial);
+ Slog.d(TAG, " Checking idle state for " + packageName
+ + " minBucket=" + minBucket);
}
- if (isSpecial) {
+ if (minBucket <= STANDBY_BUCKET_ACTIVE) {
+ // No extra processing needed for ACTIVE or higher since apps can't drop into lower
+ // buckets.
synchronized (mAppIdleLock) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
+ minBucket, REASON_MAIN_DEFAULT);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
+ minBucket, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
final AppIdleHistory.AppUsageHistory app =
@@ -761,6 +772,14 @@ public class AppStandbyController implements AppStandbyInternal {
Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
}
}
+ if (newBucket > minBucket) {
+ newBucket = minBucket;
+ // Leave the reason alone.
+ if (DEBUG) {
+ Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket
+ + " due to min bucketing");
+ }
+ }
if (DEBUG) {
Slog.d(TAG, " Old bucket=" + oldBucket
+ ", newBucket=" + newBucket);
@@ -1027,20 +1046,35 @@ public class AppStandbyController implements AppStandbyInternal {
return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
}
- private boolean isAppSpecial(String packageName, int appId, int userId) {
- if (packageName == null) return false;
+ @StandbyBuckets
+ private int getAppMinBucket(String packageName, int userId) {
+ try {
+ final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ return getAppMinBucket(packageName, UserHandle.getAppId(uid), userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Not a valid package for this user, nothing to do
+ return STANDBY_BUCKET_NEVER;
+ }
+ }
+
+ /**
+ * Return the lowest bucket this app should ever enter.
+ */
+ @StandbyBuckets
+ private int getAppMinBucket(String packageName, int appId, int userId) {
+ if (packageName == null) return STANDBY_BUCKET_NEVER;
// If not enabled at all, of course nobody is ever idle.
if (!mAppIdleEnabled) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (appId < Process.FIRST_APPLICATION_UID) {
// System uids never go idle.
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (packageName.equals("android")) {
// Nor does the framework (which should be redundant with the above, but for MR1 we will
// retain this for safety).
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (mSystemServicesReady) {
try {
@@ -1048,42 +1082,51 @@ public class AppStandbyController implements AppStandbyInternal {
// for idle mode, because app idle (aka app standby) is really not as big an issue
// for controlling who participates vs. doze mode.
if (mInjector.isNonIdleWhitelisted(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
if (isActiveDeviceAdmin(packageName, userId)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (isActiveNetworkScorer(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
if (mAppWidgetManager != null
&& mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
- return true;
+ // TODO: consider lowering to ACTIVE
+ return STANDBY_BUCKET_EXEMPTED;
}
if (isDeviceProvisioningPackage(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
}
// Check this last, as it can be the most expensive check
if (isCarrierApp(packageName)) {
- return true;
+ return STANDBY_BUCKET_EXEMPTED;
}
- return false;
+ if (isHeadlessSystemApp(packageName)) {
+ return STANDBY_BUCKET_ACTIVE;
+ }
+
+ return STANDBY_BUCKET_NEVER;
+ }
+
+ private boolean isHeadlessSystemApp(String packageName) {
+ return mHeadlessSystemApps.containsKey(packageName);
}
@Override
public boolean isAppIdleFiltered(String packageName, int appId, int userId,
long elapsedRealtime) {
- if (isAppSpecial(packageName, appId, userId)) {
+ if (getAppMinBucket(packageName, appId, userId) < AppIdleHistory.IDLE_BUCKET_CUTOFF) {
return false;
} else {
synchronized (mAppIdleLock) {
@@ -1423,6 +1466,8 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ // Make sure we don't put the app in a lower bucket than it's supposed to be in.
+ newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason, resetTimeout);
}
@@ -1617,14 +1662,16 @@ public class AppStandbyController implements AppStandbyInternal {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+ final int userId = getSendingUserId();
if (Intent.ACTION_PACKAGE_ADDED.equals(action)
|| Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
clearCarrierPrivilegedApps();
+ // ACTION_PACKAGE_ADDED is called even for system app downgrades.
+ evaluateSystemAppException(pkgName, userId);
}
if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
Intent.ACTION_PACKAGE_ADDED.equals(action))) {
- final String pkgName = intent.getData().getSchemeSpecificPart();
- final int userId = getSendingUserId();
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
maybeUnrestrictBuggyApp(pkgName, userId);
} else {
@@ -1634,6 +1681,34 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ private void evaluateSystemAppException(String packageName, int userId) {
+ if (!mSystemServicesReady) {
+ // The app will be evaluated in initializeDefaultsForSystemApps() when possible.
+ return;
+ }
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ evaluateSystemAppException(pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ mHeadlessSystemApps.remove(packageName);
+ }
+ }
+
+ private void evaluateSystemAppException(@Nullable PackageInfo pkgInfo) {
+ if (pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.isSystemApp()) {
+ synchronized (mAppIdleLock) {
+ if (pkgInfo.activities == null || pkgInfo.activities.length == 0) {
+ // Headless system app.
+ mHeadlessSystemApps.put(pkgInfo.packageName, true);
+ } else {
+ mHeadlessSystemApps.remove(pkgInfo.packageName);
+ }
+ }
+ }
+ }
+
@Override
public void initializeDefaultsForSystemApps(int userId) {
if (!mSystemServicesReady) {
@@ -1645,7 +1720,7 @@ public class AppStandbyController implements AppStandbyInternal {
+ "appIdleEnabled=" + mAppIdleEnabled);
final long elapsedRealtime = mInjector.elapsedRealtime();
List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
+ PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS,
userId);
final int packageCount = packages.size();
synchronized (mAppIdleLock) {
@@ -1658,6 +1733,8 @@ public class AppStandbyController implements AppStandbyInternal {
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
+
+ evaluateSystemAppException(pi);
}
}
// Immediately persist defaults to disk