Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75e1da7b authored by Kweku Adams's avatar Kweku Adams
Browse files

Unrestrict updated buggy apps.

If an app is put into the RESTRICTED bucket because the system thinks
it's buggy, we can bring it out of the bucket when the app is updated,
assuming that the bug is fixed in the new version. If there are other
reasons that the app is restricted, then we don't bring it out.

Bug: 149507105
Test: atest FrameworksServicesTests:AppIdleHistoryTests
Test: atest FrameworksServicesTests:AppStandbyControllerTests
Change-Id: Ie4538dde3b367d32f4260e8301afa0556b845b2a
parent 7d528431
Loading
Loading
Loading
Loading
+42 −9
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ 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_DEFAULT_APP_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
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;
@@ -73,7 +75,6 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -304,10 +305,7 @@ public class AppStandbyController implements AppStandbyInternal {
    private final AppStandbyHandler mHandler;
    private final Context mContext;

    // TODO: Provide a mechanism to set an external bucketing service

    private AppWidgetManager mAppWidgetManager;
    private ConnectivityManager mConnectivityManager;
    private PackageManager mPackageManager;
    Injector mInjector;

@@ -411,7 +409,6 @@ public class AppStandbyController implements AppStandbyInternal {
            settingsObserver.updateSettings();

            mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
            mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);

            mInjector.registerDisplayListener(mDisplayListener, mHandler);
            synchronized (mAppIdleLock) {
@@ -1519,6 +1516,38 @@ public class AppStandbyController implements AppStandbyInternal {
        }
    }

    /**
     * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
     * bucket if it was forced into the bucket by the system because it was buggy.
     */
    @VisibleForTesting
    void maybeUnrestrictBuggyApp(String packageName, int userId) {
        synchronized (mAppIdleLock) {
            final long elapsedRealtime = mInjector.elapsedRealtime();
            final AppIdleHistory.AppUsageHistory app =
                    mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
            if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
                    || (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_FORCED_BY_SYSTEM) {
                return;
            }

            final int newBucket;
            final int newReason;
            if ((app.bucketingReason & REASON_SUB_MASK) == REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY) {
                // If bugginess was the only reason the app should be restricted, then lift it out.
                newBucket = STANDBY_BUCKET_RARE;
                newReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_UPDATE;
            } else {
                // There's another reason the app was restricted. Remove the buggy bit and call
                // it a day.
                newBucket = STANDBY_BUCKET_RESTRICTED;
                newReason = app.bucketingReason & ~REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
            }
            mAppIdleHistory.setAppStandbyBucket(
                    packageName, userId, elapsedRealtime, newBucket, newReason);
        }
    }

    private class PackageReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -1528,10 +1557,14 @@ public class AppStandbyController implements AppStandbyInternal {
                clearCarrierPrivilegedApps();
            }
            if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
                    Intent.ACTION_PACKAGE_ADDED.equals(action))
                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
                        getSendingUserId());
                    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 {
                    clearAppIdleForPackage(pkgName, userId);
                }
            }
        }
    }
+18 −0
Original line number Diff line number Diff line
@@ -203,6 +203,16 @@ public final class UsageStatsManager {

    /** @hide */
    public static final int REASON_SUB_MASK = 0x00FF;
    /**
     * The reason for using the default main reason is unknown or undefined.
     * @hide
     */
    public static final int REASON_SUB_DEFAULT_UNDEFINED = 0x0000;
    /**
     * The app was updated.
     * @hide
     */
    public static final int REASON_SUB_DEFAULT_APP_UPDATE = 0x0001;
    /**
     * The app was interacted with in some way by the system.
     * @hide
@@ -1069,6 +1079,14 @@ public final class UsageStatsManager {
        switch (standbyReason & REASON_MAIN_MASK) {
            case REASON_MAIN_DEFAULT:
                sb.append("d");
                switch (subReason) {
                    case REASON_SUB_DEFAULT_UNDEFINED:
                        // Historically, undefined didn't have a string, so don't add anything here.
                        break;
                    case REASON_SUB_DEFAULT_APP_UPDATE:
                        sb.append("-au");
                        break;
                }
                break;
            case REASON_MAIN_FORCED_BY_SYSTEM:
                sb.append("s");
+74 −0
Original line number Diff line number Diff line
@@ -1208,6 +1208,80 @@ public class AppStandbyControllerTests {
                STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
    }

    public void testAppUpdateOnRestrictedBucketStatus() {
        // Updates shouldn't change bucket if the app timed out.
        // Way past all timeouts. App times out into RESTRICTED bucket.
        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
        mController.checkIdleStates(USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);

        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);

        // Updates shouldn't change bucket if the app was forced by the system for a non-buggy
        // reason.
        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
                REASON_MAIN_FORCED_BY_SYSTEM
                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);

        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);

        // Updates should change bucket if the app was forced by the system for a buggy reason.
        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);

        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
        assertNotEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));

        // Updates shouldn't change bucket if the app was forced by the system for more than just
        // a buggy reason.
        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE
                        | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);

        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
                getStandbyBucketReason(PACKAGE_1));
        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);

        // Updates shouldn't change bucket if the app was forced by the user.
        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
                REASON_MAIN_FORCED_BY_USER);

        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
        mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
        assertBucket(STANDBY_BUCKET_RESTRICTED);
    }

    private String getAdminAppsStr(int userId) {
        return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
    }