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:
@@ -19,10 +19,11 @@ 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;
|
||||||
optional Action action = 2;
|
optional Action action = 2;
|
||||||
optional string action_description = 3;
|
optional string action_description = 3;
|
||||||
optional int64 timestamp = 4;
|
optional int64 timestamp = 4;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user