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

Commit 1b91f616 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Record app optimization mode backup into BatteryHistoricalLog" into udc-dev

parents 59fa9de0 8f561144
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ message BatteryOptimizeHistoricalLogEntry {
    APPLY = 2;
    RESET = 3;
    RESTORE = 4;
    BACKUP = 5;
  }

  optional string package_name = 1;
+21 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.app.backup.BackupDataInputStream;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupHelper;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.IDeviceIdleController;
@@ -34,9 +35,11 @@ import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -47,6 +50,8 @@ public final class BatteryBackupHelper implements BackupHelper {
    /** An inditifier for {@link BackupHelper}. */
    public static final String TAG = "BatteryBackupHelper";
    private static final String DEVICE_IDLE_SERVICE = "deviceidle";
    private static final String BATTERY_OPTIMIZE_BACKUP_FILE_NAME =
            "battery_optimize_backup_historical_logs";

    static final String DELIMITER = ",";
    static final String DELIMITER_MODE = ":";
@@ -141,6 +146,7 @@ public final class BatteryBackupHelper implements BackupHelper {
        int backupCount = 0;
        final StringBuilder builder = new StringBuilder();
        final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
        final SharedPreferences sharedPreferences = getSharedPreferences(mContext);
        // Converts application into the AppUsageState.
        for (ApplicationInfo info : applications) {
            final int mode = BatteryOptimizeUtils.getMode(appOps, info.uid, info.packageName);
@@ -157,6 +163,9 @@ public final class BatteryBackupHelper implements BackupHelper {
                    info.packageName + DELIMITER_MODE + optimizationMode;
            builder.append(packageOptimizeMode + DELIMITER);
            Log.d(TAG, "backupOptimizationMode: " + packageOptimizeMode);
            BatteryHistoricalLogUtil.writeLog(
                    sharedPreferences, Action.BACKUP, info.packageName,
                    /* actionDescription */ "mode: " + optimizationMode);
            backupCount++;
        }

@@ -210,6 +219,18 @@ public final class BatteryBackupHelper implements BackupHelper {
                restoreCount, (System.currentTimeMillis() - timestamp)));
    }

    /** Dump the app optimization mode backup history data. */
    public static void dumpHistoricalData(Context context, PrintWriter writer) {
        BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(
                getSharedPreferences(context), writer);
    }

    @VisibleForTesting
    static SharedPreferences getSharedPreferences(Context context) {
        return context.getSharedPreferences(
                BATTERY_OPTIMIZE_BACKUP_FILE_NAME, Context.MODE_PRIVATE);
    }

    private void restoreOptimizationMode(
            String packageName, @BatteryOptimizeUtils.OptimizationMode int mode) {
        final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName);
+32 −30
Original line number Diff line number Diff line
@@ -37,40 +37,40 @@ public final class BatteryHistoricalLogUtil {
    @VisibleForTesting
    static final int MAX_ENTRIES = 40;

    /**
     * Writes a log entry.
     *
     * <p>Keeps up to {@link #MAX_ENTRIES} in the log, once that number is exceeded, it prunes the
     * oldest one.
     */
    static void writeLog(Context context, Action action, String pkg, String actionDescription) {
    /** Writes a log entry for battery optimization mode. */
    static void writeLog(
            Context context, Action action, String packageName, String actionDescription) {
        writeLog(getSharedPreferences(context), action, packageName, actionDescription);
    }

    static void writeLog(SharedPreferences sharedPreferences, Action action,
            String packageName, String actionDescription) {
        writeLog(
                context,
                sharedPreferences,
                BatteryOptimizeHistoricalLogEntry.newBuilder()
                        .setPackageName(pkg)
                        .setPackageName(packageName)
                        .setAction(action)
                        .setActionDescription(actionDescription)
                        .setTimestamp(System.currentTimeMillis())
                        .build());
    }

    private static void writeLog(Context context, BatteryOptimizeHistoricalLogEntry logEntry) {
        SharedPreferences sharedPreferences = getSharedPreferences(context);

    private static void writeLog(
            SharedPreferences sharedPreferences, BatteryOptimizeHistoricalLogEntry logEntry) {
        BatteryOptimizeHistoricalLog existingLog =
                parseLogFromString(sharedPreferences.getString(LOGS_KEY, ""));
        BatteryOptimizeHistoricalLog.Builder newLogBuilder = existingLog.toBuilder();
        // Prune old entries
        // Prune old entries to limit the max logging data count.
        if (existingLog.getLogEntryCount() >= MAX_ENTRIES) {
            newLogBuilder.removeLogEntry(0);
        }
        newLogBuilder.addLogEntry(logEntry);

        String loggingContent =
            Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT);
        sharedPreferences
                .edit()
                .putString(
                        LOGS_KEY,
                        Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT))
                .putString(LOGS_KEY, loggingContent)
                .apply();
    }

@@ -79,34 +79,36 @@ public final class BatteryHistoricalLogUtil {
                storedLogs, BatteryOptimizeHistoricalLog.getDefaultInstance());
    }

    /**
     * Prints the historical log that has previously been stored by this utility.
     */
    /** Prints the historical log that has previously been stored by this utility. */
    public static void printBatteryOptimizeHistoricalLog(Context context, PrintWriter writer) {
        printBatteryOptimizeHistoricalLog(getSharedPreferences(context), writer);
    }

    /** Prints the historical log that has previously been stored by this utility. */
    public static void printBatteryOptimizeHistoricalLog(
            SharedPreferences sharedPreferences, PrintWriter writer) {
        writer.println("Battery optimize state history:");
        SharedPreferences sharedPreferences = getSharedPreferences(context);
        BatteryOptimizeHistoricalLog existingLog =
                parseLogFromString(sharedPreferences.getString(LOGS_KEY, ""));
        List<BatteryOptimizeHistoricalLogEntry> logEntryList = existingLog.getLogEntryList();
        if (logEntryList.isEmpty()) {
            writer.println("\tNo past logs.");
            writer.println("\tnothing to dump");
        } else {
            writer.println("0:RESTRICTED  1:UNRESTRICTED  2:OPTIMIZED  3:UNKNOWN");
            writer.println("0:UNKNOWN 1:RESTRICTED  2:UNRESTRICTED 3:OPTIMIZED");
            logEntryList.forEach(entry -> writer.println(toString(entry)));
        }
    }

    /**
     * Gets the unique key for logging, combined with package name, delimiter and user id.
     */
    static String getPackageNameWithUserId(String pkgName, int userId) {
        return pkgName + ":" + userId;
    /** Gets the unique key for logging. */
    static String getPackageNameWithUserId(String packageName, int userId) {
        return packageName + ":" + userId;
    }

    private static String toString(BatteryOptimizeHistoricalLogEntry entry) {
        return String.format("%s\tAction:%s\tEvent:%s\tTimestamp:%s", entry.getPackageName(),
                entry.getAction(), entry.getActionDescription(),
                ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()));
        return String.format("%s\t%s\taction:%s\tevent:%s",
                ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()),
                entry.getPackageName(), entry.getAction(),
                entry.getActionDescription());
    }

    @VisibleForTesting
+16 −0
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -84,6 +86,8 @@ public final class BatteryBackupHelperTest {
    private static final int UID1 = 1;

    private Context mContext;
    private PrintWriter mPrintWriter;
    private StringWriter mStringWriter;
    private BatteryBackupHelper mBatteryBackupHelper;

    @Mock
@@ -109,6 +113,8 @@ public final class BatteryBackupHelperTest {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mContext = spy(RuntimeEnvironment.application);
        mStringWriter = new StringWriter();
        mPrintWriter = new PrintWriter(mStringWriter);
        doReturn(mContext).when(mContext).getApplicationContext();
        doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
        doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
@@ -126,6 +132,7 @@ public final class BatteryBackupHelperTest {
    @After
    public void resetShadows() {
        ShadowUserHandle.reset();
        BatteryBackupHelper.getSharedPreferences(mContext).edit().clear().apply();
    }

    @Test
@@ -216,6 +223,8 @@ public final class BatteryBackupHelperTest {
        // 2 for UNRESTRICTED mode and 1 for RESTRICTED mode.
        final String expectedResult = PACKAGE_NAME1 + ":2," + PACKAGE_NAME2 + ":1,";
        verifyBackupData(expectedResult);
        verifyDumpHistoryData("com.android.testing.1\taction:BACKUP\tevent:mode: 2");
        verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1");
    }

    @Test
@@ -232,6 +241,7 @@ public final class BatteryBackupHelperTest {
        // "com.android.testing.2" for RESTRICTED mode.
        final String expectedResult = PACKAGE_NAME2 + ":1,";
        verifyBackupData(expectedResult);
        verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1");
    }

    @Test
@@ -248,6 +258,7 @@ public final class BatteryBackupHelperTest {
        // "com.android.testing.2" for RESTRICTED mode.
        final String expectedResult = PACKAGE_NAME2 + ":1,";
        verifyBackupData(expectedResult);
        verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1");
    }

    @Test
@@ -357,6 +368,11 @@ public final class BatteryBackupHelperTest {
        doReturn(dataKey).when(mBackupDataInputStream).getKey();
    }

    private void verifyDumpHistoryData(String expectedResult) {
        BatteryBackupHelper.dumpHistoricalData(mContext, mPrintWriter);
        assertThat(mStringWriter.toString().contains(expectedResult)).isTrue();
    }

    private void verifyBackupData(String expectedResult) throws Exception {
        final byte[] expectedBytes = expectedResult.getBytes();
        final ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+2 −2
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ public final class BatteryHistoricalLogUtilTest {
    @Test
    public void printHistoricalLog_withDefaultLogs() {
        BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
        assertThat(mTestStringWriter.toString()).contains("No past logs");
        assertThat(mTestStringWriter.toString()).contains("nothing to dump");
    }

    @Test
@@ -58,7 +58,7 @@ public final class BatteryHistoricalLogUtilTest {
        BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);

        assertThat(mTestStringWriter.toString()).contains(
                "pkg1\tAction:APPLY\tEvent:logs\tTimestamp:");
                "pkg1\taction:APPLY\tevent:logs");
    }

    @Test