diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto index cf87dc7b7b0..8512cb82506 100644 --- a/protos/fuelgauge_log.proto +++ b/protos/fuelgauge_log.proto @@ -19,10 +19,11 @@ message BatteryOptimizeHistoricalLogEntry { APPLY = 2; RESET = 3; RESTORE = 4; + BACKUP = 5; } optional string package_name = 1; optional Action action = 2; optional string action_description = 3; optional int64 timestamp = 4; -} \ No newline at end of file +} diff --git a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java index 0558d46c3b8..79df57ab08d 100644 --- a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java +++ b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java @@ -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); diff --git a/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java b/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java index a827e6df74a..f82b7031749 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java @@ -37,40 +37,40 @@ public final class BatteryHistoricalLogUtil { @VisibleForTesting static final int MAX_ENTRIES = 40; - /** - * Writes a log entry. - * - *

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 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 diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java index f3d6816c5b1..89707305add 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java @@ -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 captor = ArgumentCaptor.forClass(byte[].class); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java index 74f62ad936d..cb5de7d43a5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java @@ -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