Merge "Record app optimization mode backup into BatteryHistoricalLog" into udc-dev am: 1b91f61633

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23124522

Change-Id: I019a6b64bd482cbdfc15648a1dcdfd3648708519
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot
2023-05-11 11:37:52 +00:00
committed by Automerger Merge Worker
5 changed files with 73 additions and 33 deletions

View File

@@ -19,6 +19,7 @@ message BatteryOptimizeHistoricalLogEntry {
APPLY = 2; APPLY = 2;
RESET = 3; RESET = 3;
RESTORE = 4; RESTORE = 4;
BACKUP = 5;
} }
optional string package_name = 1; optional string package_name = 1;

View File

@@ -22,6 +22,7 @@ import android.app.backup.BackupDataInputStream;
import android.app.backup.BackupDataOutput; import android.app.backup.BackupDataOutput;
import android.app.backup.BackupHelper; import android.app.backup.BackupHelper;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager; import android.content.pm.IPackageManager;
import android.os.IDeviceIdleController; import android.os.IDeviceIdleController;
@@ -34,9 +35,11 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -47,6 +50,8 @@ public final class BatteryBackupHelper implements BackupHelper {
/** An inditifier for {@link BackupHelper}. */ /** An inditifier for {@link BackupHelper}. */
public static final String TAG = "BatteryBackupHelper"; public static final String TAG = "BatteryBackupHelper";
private static final String DEVICE_IDLE_SERVICE = "deviceidle"; 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 = ",";
static final String DELIMITER_MODE = ":"; static final String DELIMITER_MODE = ":";
@@ -141,6 +146,7 @@ public final class BatteryBackupHelper implements BackupHelper {
int backupCount = 0; int backupCount = 0;
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
final SharedPreferences sharedPreferences = getSharedPreferences(mContext);
// Converts application into the AppUsageState. // Converts application into the AppUsageState.
for (ApplicationInfo info : applications) { for (ApplicationInfo info : applications) {
final int mode = BatteryOptimizeUtils.getMode(appOps, info.uid, info.packageName); 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; info.packageName + DELIMITER_MODE + optimizationMode;
builder.append(packageOptimizeMode + DELIMITER); builder.append(packageOptimizeMode + DELIMITER);
Log.d(TAG, "backupOptimizationMode: " + packageOptimizeMode); Log.d(TAG, "backupOptimizationMode: " + packageOptimizeMode);
BatteryHistoricalLogUtil.writeLog(
sharedPreferences, Action.BACKUP, info.packageName,
/* actionDescription */ "mode: " + optimizationMode);
backupCount++; backupCount++;
} }
@@ -210,6 +219,18 @@ public final class BatteryBackupHelper implements BackupHelper {
restoreCount, (System.currentTimeMillis() - timestamp))); 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( private void restoreOptimizationMode(
String packageName, @BatteryOptimizeUtils.OptimizationMode int mode) { String packageName, @BatteryOptimizeUtils.OptimizationMode int mode) {
final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName); final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName);

View File

@@ -37,40 +37,40 @@ public final class BatteryHistoricalLogUtil {
@VisibleForTesting @VisibleForTesting
static final int MAX_ENTRIES = 40; static final int MAX_ENTRIES = 40;
/** /** Writes a log entry for battery optimization mode. */
* Writes a log entry. static void writeLog(
* Context context, Action action, String packageName, String actionDescription) {
* <p>Keeps up to {@link #MAX_ENTRIES} in the log, once that number is exceeded, it prunes the writeLog(getSharedPreferences(context), action, packageName, actionDescription);
* oldest one. }
*/
static void writeLog(Context context, Action action, String pkg, String actionDescription) { static void writeLog(SharedPreferences sharedPreferences, Action action,
String packageName, String actionDescription) {
writeLog( writeLog(
context, sharedPreferences,
BatteryOptimizeHistoricalLogEntry.newBuilder() BatteryOptimizeHistoricalLogEntry.newBuilder()
.setPackageName(pkg) .setPackageName(packageName)
.setAction(action) .setAction(action)
.setActionDescription(actionDescription) .setActionDescription(actionDescription)
.setTimestamp(System.currentTimeMillis()) .setTimestamp(System.currentTimeMillis())
.build()); .build());
} }
private static void writeLog(Context context, BatteryOptimizeHistoricalLogEntry logEntry) { private static void writeLog(
SharedPreferences sharedPreferences = getSharedPreferences(context); SharedPreferences sharedPreferences, BatteryOptimizeHistoricalLogEntry logEntry) {
BatteryOptimizeHistoricalLog existingLog = BatteryOptimizeHistoricalLog existingLog =
parseLogFromString(sharedPreferences.getString(LOGS_KEY, "")); parseLogFromString(sharedPreferences.getString(LOGS_KEY, ""));
BatteryOptimizeHistoricalLog.Builder newLogBuilder = existingLog.toBuilder(); BatteryOptimizeHistoricalLog.Builder newLogBuilder = existingLog.toBuilder();
// Prune old entries // Prune old entries to limit the max logging data count.
if (existingLog.getLogEntryCount() >= MAX_ENTRIES) { if (existingLog.getLogEntryCount() >= MAX_ENTRIES) {
newLogBuilder.removeLogEntry(0); newLogBuilder.removeLogEntry(0);
} }
newLogBuilder.addLogEntry(logEntry); newLogBuilder.addLogEntry(logEntry);
String loggingContent =
Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT);
sharedPreferences sharedPreferences
.edit() .edit()
.putString( .putString(LOGS_KEY, loggingContent)
LOGS_KEY,
Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT))
.apply(); .apply();
} }
@@ -79,34 +79,36 @@ public final class BatteryHistoricalLogUtil {
storedLogs, BatteryOptimizeHistoricalLog.getDefaultInstance()); 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) { 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:"); writer.println("Battery optimize state history:");
SharedPreferences sharedPreferences = getSharedPreferences(context);
BatteryOptimizeHistoricalLog existingLog = BatteryOptimizeHistoricalLog existingLog =
parseLogFromString(sharedPreferences.getString(LOGS_KEY, "")); parseLogFromString(sharedPreferences.getString(LOGS_KEY, ""));
List<BatteryOptimizeHistoricalLogEntry> logEntryList = existingLog.getLogEntryList(); List<BatteryOptimizeHistoricalLogEntry> logEntryList = existingLog.getLogEntryList();
if (logEntryList.isEmpty()) { if (logEntryList.isEmpty()) {
writer.println("\tNo past logs."); writer.println("\tnothing to dump");
} else { } 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))); logEntryList.forEach(entry -> writer.println(toString(entry)));
} }
} }
/** /** Gets the unique key for logging. */
* Gets the unique key for logging, combined with package name, delimiter and user id. static String getPackageNameWithUserId(String packageName, int userId) {
*/ return packageName + ":" + userId;
static String getPackageNameWithUserId(String pkgName, int userId) {
return pkgName + ":" + userId;
} }
private static String toString(BatteryOptimizeHistoricalLogEntry entry) { private static String toString(BatteryOptimizeHistoricalLogEntry entry) {
return String.format("%s\tAction:%s\tEvent:%s\tTimestamp:%s", entry.getPackageName(), return String.format("%s\t%s\taction:%s\tevent:%s",
entry.getAction(), entry.getActionDescription(), ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()),
ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp())); entry.getPackageName(), entry.getAction(),
entry.getActionDescription());
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -70,6 +70,8 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements; import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter; import org.robolectric.annotation.Resetter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -84,6 +86,8 @@ public final class BatteryBackupHelperTest {
private static final int UID1 = 1; private static final int UID1 = 1;
private Context mContext; private Context mContext;
private PrintWriter mPrintWriter;
private StringWriter mStringWriter;
private BatteryBackupHelper mBatteryBackupHelper; private BatteryBackupHelper mBatteryBackupHelper;
@Mock @Mock
@@ -109,6 +113,8 @@ public final class BatteryBackupHelperTest {
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mStringWriter = new StringWriter();
mPrintWriter = new PrintWriter(mStringWriter);
doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
@@ -126,6 +132,7 @@ public final class BatteryBackupHelperTest {
@After @After
public void resetShadows() { public void resetShadows() {
ShadowUserHandle.reset(); ShadowUserHandle.reset();
BatteryBackupHelper.getSharedPreferences(mContext).edit().clear().apply();
} }
@Test @Test
@@ -216,6 +223,8 @@ public final class BatteryBackupHelperTest {
// 2 for UNRESTRICTED mode and 1 for RESTRICTED mode. // 2 for UNRESTRICTED mode and 1 for RESTRICTED mode.
final String expectedResult = PACKAGE_NAME1 + ":2," + PACKAGE_NAME2 + ":1,"; final String expectedResult = PACKAGE_NAME1 + ":2," + PACKAGE_NAME2 + ":1,";
verifyBackupData(expectedResult); verifyBackupData(expectedResult);
verifyDumpHistoryData("com.android.testing.1\taction:BACKUP\tevent:mode: 2");
verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1");
} }
@Test @Test
@@ -232,6 +241,7 @@ public final class BatteryBackupHelperTest {
// "com.android.testing.2" for RESTRICTED mode. // "com.android.testing.2" for RESTRICTED mode.
final String expectedResult = PACKAGE_NAME2 + ":1,"; final String expectedResult = PACKAGE_NAME2 + ":1,";
verifyBackupData(expectedResult); verifyBackupData(expectedResult);
verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1");
} }
@Test @Test
@@ -248,6 +258,7 @@ public final class BatteryBackupHelperTest {
// "com.android.testing.2" for RESTRICTED mode. // "com.android.testing.2" for RESTRICTED mode.
final String expectedResult = PACKAGE_NAME2 + ":1,"; final String expectedResult = PACKAGE_NAME2 + ":1,";
verifyBackupData(expectedResult); verifyBackupData(expectedResult);
verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1");
} }
@Test @Test
@@ -357,6 +368,11 @@ public final class BatteryBackupHelperTest {
doReturn(dataKey).when(mBackupDataInputStream).getKey(); 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 { private void verifyBackupData(String expectedResult) throws Exception {
final byte[] expectedBytes = expectedResult.getBytes(); final byte[] expectedBytes = expectedResult.getBytes();
final ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class); final ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);

View File

@@ -49,7 +49,7 @@ public final class BatteryHistoricalLogUtilTest {
@Test @Test
public void printHistoricalLog_withDefaultLogs() { public void printHistoricalLog_withDefaultLogs() {
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter); BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
assertThat(mTestStringWriter.toString()).contains("No past logs"); assertThat(mTestStringWriter.toString()).contains("nothing to dump");
} }
@Test @Test
@@ -58,7 +58,7 @@ public final class BatteryHistoricalLogUtilTest {
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter); BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
assertThat(mTestStringWriter.toString()).contains( assertThat(mTestStringWriter.toString()).contains(
"pkg1\tAction:APPLY\tEvent:logs\tTimestamp:"); "pkg1\taction:APPLY\tevent:logs");
} }
@Test @Test