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

Commit 998d84a0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "App Bucketing for Standby"

parents 9e739fdf 17fffee4
Loading
Loading
Loading
Loading
+83 −0
Original line number Diff line number Diff line
/*
 * 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 android.app.usage;

import android.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Set of constants for app standby buckets and reasons. Apps will be moved into different buckets
 * that affect how frequently they can run in the background or perform other battery-consuming
 * actions. Buckets will be assigned based on how frequently or when the system thinks the user
 * is likely to use the app.
 * @hide
 */
public class AppStandby {

    /** The app was used very recently, currently in use or likely to be used very soon. */
    public static final int STANDBY_BUCKET_ACTIVE = 0;

    // Leave some gap in case we want to increase the number of buckets

    /** The app was used recently and/or likely to be used in the next few hours  */
    public static final int STANDBY_BUCKET_WORKING_SET = 3;

    // Leave some gap in case we want to increase the number of buckets

    /** The app was used in the last few days and/or likely to be used in the next few days */
    public static final int STANDBY_BUCKET_FREQUENT = 6;

    // Leave some gap in case we want to increase the number of buckets

    /** The app has not be used for several days and/or is unlikely to be used for several days */
    public static final int STANDBY_BUCKET_RARE = 9;

    // Leave some gap in case we want to increase the number of buckets

    /** The app has never been used. */
    public static final int STANDBY_BUCKET_NEVER = 12;

    /** Reason for bucketing -- default initial state */
    public static final String REASON_DEFAULT = "default";

    /** Reason for bucketing -- timeout */
    public static final String REASON_TIMEOUT = "timeout";

    /** Reason for bucketing -- usage */
    public static final String REASON_USAGE = "usage";

    /** Reason for bucketing -- forced by user / shell command */
    public static final String REASON_FORCED = "forced";

    /**
     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
     * be appended.
     */
    public static final String REASON_PREDICTED = "predicted";

    @IntDef(flag = false, value = {
            STANDBY_BUCKET_ACTIVE,
            STANDBY_BUCKET_WORKING_SET,
            STANDBY_BUCKET_FREQUENT,
            STANDBY_BUCKET_RARE,
            STANDBY_BUCKET_NEVER,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StandbyBuckets {}
}
+2 −0
Original line number Diff line number Diff line
@@ -36,4 +36,6 @@ interface IUsageStatsManager {
    void onCarrierPrivilegedAppsChanged();
    void reportChooserSelection(String packageName, int userId, String contentType,
            in String[] annotations, String action);
    int getAppStandbyBucket(String packageName, String callingPackage, int userId);
    void setAppStandbyBucket(String packageName, int bucket, int userId);
}
+24 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.app.usage;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.usage.AppStandby.StandbyBuckets;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
@@ -246,6 +247,29 @@ public final class UsageStatsManager {
        }
    }

    /**
     * @hide
     */
    public @StandbyBuckets int getAppStandbyBucket(String packageName) {
        try {
            return mService.getAppStandbyBucket(packageName, mContext.getOpPackageName(),
                    mContext.getUserId());
        } catch (RemoteException e) {
        }
        return AppStandby.STANDBY_BUCKET_ACTIVE;
    }

    /**
     * @hide
     */
    public void setAppStandbyBucket(String packageName, @StandbyBuckets int bucket) {
        try {
            mService.setAppStandbyBucket(packageName, bucket, mContext.getUserId());
        } catch (RemoteException e) {
            // Nothing to do
        }
    }

    /**
     * {@hide}
     * Temporarily whitelist the specified app for a short duration. This is to allow an app
+25 −0
Original line number Diff line number Diff line
@@ -222,6 +222,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
                    return runSetInactive(pw);
                case "get-inactive":
                    return runGetInactive(pw);
                case "set-standby-bucket":
                    return runSetStandbyBucket(pw);
                case "send-trim-memory":
                    return runSendTrimMemory(pw);
                case "display":
@@ -1824,6 +1826,27 @@ final class ActivityManagerShellCommand extends ShellCommand {
        return 0;
    }

    int runSetStandbyBucket(PrintWriter pw) throws RemoteException {
        int userId = UserHandle.USER_CURRENT;

        String opt;
        while ((opt=getNextOption()) != null) {
            if (opt.equals("--user")) {
                userId = UserHandle.parseUserArg(getNextArgRequired());
            } else {
                getErrPrintWriter().println("Error: Unknown option: " + opt);
                return -1;
            }
        }
        String packageName = getNextArgRequired();
        String value = getNextArgRequired();

        IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
                Context.USAGE_STATS_SERVICE));
        usm.setAppStandbyBucket(packageName, Integer.parseInt(value), userId);
        return 0;
    }

    int runGetInactive(PrintWriter pw) throws RemoteException {
        int userId = UserHandle.USER_CURRENT;

@@ -2571,6 +2594,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
            pw.println("      Sets the inactive state of an app.");
            pw.println("  get-inactive [--user <USER_ID>] <PACKAGE>");
            pw.println("      Returns the inactive state of an app.");
            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> <BUCKET>");
            pw.println("      Puts an app in the standby bucket.");
            pw.println("  send-trim-memory [--user <USER_ID>] <PROCESS>");
            pw.println("          [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]");
            pw.println("      Send a memory trim event to a <PROCESS>.  May also supply a raw trim int level.");
+36 −21
Original line number Diff line number Diff line
@@ -16,6 +16,11 @@

package com.android.server.usage;

import static android.app.usage.AppStandby.REASON_TIMEOUT;
import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;

import android.app.usage.AppStandby;
import android.os.FileUtils;
import android.test.AndroidTestCase;

@@ -28,6 +33,8 @@ public class AppIdleHistoryTests extends AndroidTestCase {
    final static String PACKAGE_1 = "com.android.testpackage1";
    final static String PACKAGE_2 = "com.android.testpackage2";

    final static int USER_ID = 0;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
@@ -42,7 +49,6 @@ public class AppIdleHistoryTests extends AndroidTestCase {
    }

    public void testFilesCreation() {
        final int userId = 0;
        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 0);

        aih.updateDisplay(true, /* elapsedRealtime= */ 1000);
@@ -50,9 +56,9 @@ public class AppIdleHistoryTests extends AndroidTestCase {
        // Screen On time file should be written right away
        assertTrue(aih.getScreenOnTimeFile().exists());

        aih.writeAppIdleTimes(userId);
        aih.writeAppIdleTimes(USER_ID);
        // stats file should be written now
        assertTrue(new File(new File(mStorageDir, "users/" + userId),
        assertTrue(new File(new File(mStorageDir, "users/" + USER_ID),
                AppIdleHistory.APP_IDLE_FILENAME).exists());
    }

@@ -77,24 +83,33 @@ public class AppIdleHistoryTests extends AndroidTestCase {
        assertEquals(aih2.getScreenOnTime(13000), 4000);
    }

    public void testPackageEvents() {
    public void testBuckets() {
        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
        aih.setThresholds(4000, 1000);
        aih.updateDisplay(true, 1000);
        // App is not-idle by default
        assertFalse(aih.isIdle(PACKAGE_1, 0, 1500));
        // Still not idle
        assertFalse(aih.isIdle(PACKAGE_1, 0, 3000));
        // Idle now
        assertTrue(aih.isIdle(PACKAGE_1, 0, 8000));
        // Not idle
        assertFalse(aih.isIdle(PACKAGE_2, 0, 9000));

        // Screen off
        aih.updateDisplay(false, 9100);
        // Still idle after 10 seconds because screen hasn't been on long enough
        assertFalse(aih.isIdle(PACKAGE_2, 0, 20000));
        aih.updateDisplay(true, 21000);
        assertTrue(aih.isIdle(PACKAGE_2, 0, 23000));

        aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
                AppStandby.REASON_USAGE);
        // ACTIVE means not idle
        assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));

        aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
                AppStandby.REASON_USAGE);
        aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
                REASON_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_TIMEOUT);

        // RARE is considered idle
        assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
        assertFalse(aih.isIdle(PACKAGE_2, USER_ID, 3000));

        // Check persistence
        aih.writeAppIdleDurations();
        aih.writeAppIdleTimes(USER_ID);
        aih = new AppIdleHistory(mStorageDir, 4000);
        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_TIMEOUT);
    }
}
 No newline at end of file
Loading