Add a mechanism to log battery usage periodic job events
Example history log: Jul 07, 2023, 15:28:51 SCHEDULE_JOB triggerTime=Jul 07, 2023, 16:00:00 Jul 07, 2023, 15:32:16 FETCH_USAGE_DATA Jul 07, 2023, 15:32:17 INSERT_USAGE_DATA size=37 Jul 07, 2023, 15:43:45 FETCH_USAGE_DATA Jul 07, 2023, 15:43:48 INSERT_USAGE_DATA size=47 Jul 07, 2023, 15:43:49 SCHEDULE_JOB triggerTime=Jul 07, 2023, 16:00:00 Bug: 284893240 Test: make test RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.fuelgauge Change-Id: I45a1ce0ce9b70f095702727e53d7b7ce8824abdb
This commit is contained in:
@@ -5,13 +5,12 @@ option java_multiple_files = true;
|
|||||||
option java_package = "com.android.settings.fuelgauge";
|
option java_package = "com.android.settings.fuelgauge";
|
||||||
option java_outer_classname = "FuelgaugeLogProto";
|
option java_outer_classname = "FuelgaugeLogProto";
|
||||||
|
|
||||||
// Stores history of setting optimize mode
|
// Store history of setting optimize mode
|
||||||
message BatteryOptimizeHistoricalLog {
|
message BatteryOptimizeHistoricalLog {
|
||||||
repeated BatteryOptimizeHistoricalLogEntry log_entry = 1;
|
repeated BatteryOptimizeHistoricalLogEntry log_entry = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BatteryOptimizeHistoricalLogEntry {
|
message BatteryOptimizeHistoricalLogEntry {
|
||||||
|
|
||||||
// The action to set optimize mode
|
// The action to set optimize mode
|
||||||
enum Action {
|
enum Action {
|
||||||
UNKNOWN = 0;
|
UNKNOWN = 0;
|
||||||
@@ -28,3 +27,25 @@ message BatteryOptimizeHistoricalLogEntry {
|
|||||||
optional string action_description = 3;
|
optional string action_description = 3;
|
||||||
optional int64 timestamp = 4;
|
optional int64 timestamp = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Store history of battery usage periodic job
|
||||||
|
message BatteryUsageHistoricalLog {
|
||||||
|
repeated BatteryUsageHistoricalLogEntry log_entry = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BatteryUsageHistoricalLogEntry {
|
||||||
|
// The action to record battery usage job event
|
||||||
|
enum Action {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
SCHEDULE_JOB = 1;
|
||||||
|
EXECUTE_JOB = 2;
|
||||||
|
RECHECK_JOB = 3;
|
||||||
|
FETCH_USAGE_DATA = 4;
|
||||||
|
INSERT_USAGE_DATA = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional int64 timestamp = 1;
|
||||||
|
optional Action action = 2;
|
||||||
|
optional string action_description = 3;
|
||||||
|
}
|
||||||
|
@@ -289,12 +289,14 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
|||||||
mLogStringBuilder.append(", onPause mode = ").append(selectedPreference);
|
mLogStringBuilder.append(", onPause mode = ").append(selectedPreference);
|
||||||
logMetricCategory(selectedPreference);
|
logMetricCategory(selectedPreference);
|
||||||
|
|
||||||
BatteryHistoricalLogUtil.writeLog(
|
mExecutor.execute(() -> {
|
||||||
|
BatteryOptimizeLogUtils.writeLog(
|
||||||
getContext().getApplicationContext(),
|
getContext().getApplicationContext(),
|
||||||
Action.LEAVE,
|
Action.LEAVE,
|
||||||
BatteryHistoricalLogUtil.getPackageNameWithUserId(
|
BatteryOptimizeLogUtils.getPackageNameWithUserId(
|
||||||
mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
|
mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
|
||||||
mLogStringBuilder.toString());
|
mLogStringBuilder.toString());
|
||||||
|
});
|
||||||
Log.d(TAG, "Leave with mode: " + selectedPreference);
|
Log.d(TAG, "Leave with mode: " + selectedPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -199,7 +199,7 @@ 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(
|
BatteryOptimizeLogUtils.writeLog(
|
||||||
sharedPreferences, Action.BACKUP, info.packageName,
|
sharedPreferences, Action.BACKUP, info.packageName,
|
||||||
/* actionDescription */ "mode: " + optimizationMode);
|
/* actionDescription */ "mode: " + optimizationMode);
|
||||||
backupCount++;
|
backupCount++;
|
||||||
@@ -275,7 +275,7 @@ public final class BatteryBackupHelper implements BackupHelper {
|
|||||||
|
|
||||||
/** Dump the app optimization mode backup history data. */
|
/** Dump the app optimization mode backup history data. */
|
||||||
public static void dumpHistoricalData(Context context, PrintWriter writer) {
|
public static void dumpHistoricalData(Context context, PrintWriter writer) {
|
||||||
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(
|
BatteryOptimizeLogUtils.printBatteryOptimizeHistoricalLog(
|
||||||
getSharedPreferences(context), writer);
|
getSharedPreferences(context), writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,23 +20,25 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
|
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
|
||||||
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
|
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Writes and reads a historical log of battery related state change events. */
|
/** Writes and reads a historical log of battery related state change events. */
|
||||||
public final class BatteryHistoricalLogUtil {
|
public final class BatteryOptimizeLogUtils {
|
||||||
|
private static final String TAG = "BatteryOptimizeLogUtils";
|
||||||
private static final String BATTERY_OPTIMIZE_FILE_NAME = "battery_optimize_historical_logs";
|
private static final String BATTERY_OPTIMIZE_FILE_NAME = "battery_optimize_historical_logs";
|
||||||
private static final String LOGS_KEY = "battery_optimize_logs_key";
|
private static final String LOGS_KEY = "battery_optimize_logs_key";
|
||||||
private static final String TAG = "BatteryHistoricalLogUtil";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int MAX_ENTRIES = 40;
|
static final int MAX_ENTRIES = 40;
|
||||||
|
|
||||||
|
private BatteryOptimizeLogUtils() {}
|
||||||
|
|
||||||
/** Writes a log entry for battery optimization mode. */
|
/** Writes a log entry for battery optimization mode. */
|
||||||
static void writeLog(
|
static void writeLog(
|
||||||
Context context, Action action, String packageName, String actionDescription) {
|
Context context, Action action, String packageName, String actionDescription) {
|
||||||
@@ -113,6 +115,7 @@ public final class BatteryHistoricalLogUtil {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static SharedPreferences getSharedPreferences(Context context) {
|
static SharedPreferences getSharedPreferences(Context context) {
|
||||||
return context.getSharedPreferences(BATTERY_OPTIMIZE_FILE_NAME, Context.MODE_PRIVATE);
|
return context.getApplicationContext()
|
||||||
|
.getSharedPreferences(BATTERY_OPTIMIZE_FILE_NAME, Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -245,7 +245,7 @@ public class BatteryOptimizeUtils {
|
|||||||
Context context, int appStandbyMode, boolean allowListed, int uid, String packageName,
|
Context context, int appStandbyMode, boolean allowListed, int uid, String packageName,
|
||||||
BatteryUtils batteryUtils, PowerAllowlistBackend powerAllowlistBackend,
|
BatteryUtils batteryUtils, PowerAllowlistBackend powerAllowlistBackend,
|
||||||
Action action) {
|
Action action) {
|
||||||
final String packageNameKey = BatteryHistoricalLogUtil
|
final String packageNameKey = BatteryOptimizeLogUtils
|
||||||
.getPackageNameWithUserId(packageName, UserHandle.myUserId());
|
.getPackageNameWithUserId(packageName, UserHandle.myUserId());
|
||||||
try {
|
try {
|
||||||
batteryUtils.setForceAppStandby(uid, packageName, appStandbyMode);
|
batteryUtils.setForceAppStandby(uid, packageName, appStandbyMode);
|
||||||
@@ -259,7 +259,7 @@ public class BatteryOptimizeUtils {
|
|||||||
appStandbyMode = -1;
|
appStandbyMode = -1;
|
||||||
Log.e(TAG, "set OPTIMIZATION MODE failed for " + packageName, e);
|
Log.e(TAG, "set OPTIMIZATION MODE failed for " + packageName, e);
|
||||||
}
|
}
|
||||||
BatteryHistoricalLogUtil.writeLog(
|
BatteryOptimizeLogUtils.writeLog(
|
||||||
context,
|
context,
|
||||||
action,
|
action,
|
||||||
packageNameKey,
|
packageNameKey,
|
||||||
|
@@ -355,7 +355,7 @@ public class BatteryUtils {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends MessageLite> T parseProtoFromString(
|
public static <T extends MessageLite> T parseProtoFromString(
|
||||||
String serializedProto, T protoClass) {
|
String serializedProto, T protoClass) {
|
||||||
if (serializedProto.isEmpty()) {
|
if (serializedProto == null || serializedProto.isEmpty()) {
|
||||||
return (T) protoClass.getDefaultInstanceForType();
|
return (T) protoClass.getDefaultInstanceForType();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@@ -23,6 +23,9 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ public final class BatteryUsageDataLoader {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static void loadUsageData(final Context context, final boolean isFullChargeStart) {
|
static void loadUsageData(final Context context, final boolean isFullChargeStart) {
|
||||||
|
BatteryUsageLogUtils.writeLog(context, Action.FETCH_USAGE_DATA, "");
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
|
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
|
||||||
final List<BatteryEntry> batteryEntryList =
|
final List<BatteryEntry> batteryEntryList =
|
||||||
|
@@ -24,6 +24,8 @@ import android.os.Looper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.settings.core.instrumentation.ElapsedTimeUtils;
|
import com.android.settings.core.instrumentation.ElapsedTimeUtils;
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -79,8 +81,9 @@ public final class BootBroadcastReceiver extends BroadcastReceiver {
|
|||||||
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
|
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
|
||||||
final Intent recheckIntent = new Intent(ACTION_PERIODIC_JOB_RECHECK);
|
final Intent recheckIntent = new Intent(ACTION_PERIODIC_JOB_RECHECK);
|
||||||
recheckIntent.setClass(context, BootBroadcastReceiver.class);
|
recheckIntent.setClass(context, BootBroadcastReceiver.class);
|
||||||
mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent),
|
final long delayedTime = getRescheduleTimeForBootAction(context);
|
||||||
getRescheduleTimeForBootAction(context));
|
mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent), delayedTime);
|
||||||
|
BatteryUsageLogUtils.writeLog(context, Action.RECHECK_JOB, "delay:" + delayedTime);
|
||||||
} else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) {
|
} else if (ACTION_SETUP_WIZARD_FINISHED.equals(action)) {
|
||||||
ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
|
ElapsedTimeUtils.storeSuwFinishedTimestamp(context, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,9 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
||||||
import com.android.settingslib.fuelgauge.BatteryStatus;
|
import com.android.settingslib.fuelgauge.BatteryStatus;
|
||||||
|
|
||||||
@@ -395,6 +397,7 @@ public final class DatabaseUtils {
|
|||||||
|
|
||||||
int size = 1;
|
int size = 1;
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
|
String errorMessage = "";
|
||||||
// Inserts all ContentValues into battery provider.
|
// Inserts all ContentValues into battery provider.
|
||||||
if (!valuesList.isEmpty()) {
|
if (!valuesList.isEmpty()) {
|
||||||
final ContentValues[] valuesArray = new ContentValues[valuesList.size()];
|
final ContentValues[] valuesArray = new ContentValues[valuesList.size()];
|
||||||
@@ -404,7 +407,8 @@ public final class DatabaseUtils {
|
|||||||
Log.d(TAG, "insert() battery states data into database with isFullChargeStart:"
|
Log.d(TAG, "insert() battery states data into database with isFullChargeStart:"
|
||||||
+ isFullChargeStart);
|
+ isFullChargeStart);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "bulkInsert() battery states data into database error:\n" + e);
|
errorMessage = e.toString();
|
||||||
|
Log.e(TAG, "bulkInsert() data into database error:\n" + errorMessage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Inserts one fake data into battery provider.
|
// Inserts one fake data into battery provider.
|
||||||
@@ -424,11 +428,16 @@ public final class DatabaseUtils {
|
|||||||
+ isFullChargeStart);
|
+ isFullChargeStart);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "insert() data into database error:\n" + e);
|
errorMessage = e.toString();
|
||||||
|
Log.e(TAG, "insert() data into database error:\n" + errorMessage);
|
||||||
}
|
}
|
||||||
valuesList.add(contentValues);
|
valuesList.add(contentValues);
|
||||||
}
|
}
|
||||||
resolver.notifyChange(BATTERY_CONTENT_URI, /*observer=*/ null);
|
resolver.notifyChange(BATTERY_CONTENT_URI, /*observer=*/ null);
|
||||||
|
BatteryUsageLogUtils.writeLog(
|
||||||
|
context,
|
||||||
|
Action.INSERT_USAGE_DATA,
|
||||||
|
"size=" + size + " " + errorMessage);
|
||||||
Log.d(TAG, String.format("sendBatteryEntryData() size=%d in %d/ms",
|
Log.d(TAG, String.format("sendBatteryEntryData() size=%d in %d/ms",
|
||||||
size, (System.currentTimeMillis() - startTime)));
|
size, (System.currentTimeMillis() - startTime)));
|
||||||
if (isFullChargeStart) {
|
if (isFullChargeStart) {
|
||||||
|
@@ -24,6 +24,8 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
@@ -76,8 +78,11 @@ public final class PeriodicJobManager {
|
|||||||
final long triggerAtMillis = getTriggerAtMillis(mContext, Clock.systemUTC(), fromBoot);
|
final long triggerAtMillis = getTriggerAtMillis(mContext, Clock.systemUTC(), fromBoot);
|
||||||
mAlarmManager.setExactAndAllowWhileIdle(
|
mAlarmManager.setExactAndAllowWhileIdle(
|
||||||
AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||||
Log.d(TAG, "schedule next alarm job at "
|
|
||||||
+ ConvertUtils.utcToLocalTimeForLogging(triggerAtMillis));
|
final String utcToLocalTime = ConvertUtils.utcToLocalTimeForLogging(triggerAtMillis);
|
||||||
|
BatteryUsageLogUtils.writeLog(
|
||||||
|
mContext, Action.SCHEDULE_JOB, "triggerTime=" + utcToLocalTime);
|
||||||
|
Log.d(TAG, "schedule next alarm job at " + utcToLocalTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelJob(PendingIntent pendingIntent) {
|
void cancelJob(PendingIntent pendingIntent) {
|
||||||
|
@@ -22,6 +22,9 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
|
||||||
|
|
||||||
/** Receives the periodic alarm {@link PendingIntent} callback. */
|
/** Receives the periodic alarm {@link PendingIntent} callback. */
|
||||||
public final class PeriodicJobReceiver extends BroadcastReceiver {
|
public final class PeriodicJobReceiver extends BroadcastReceiver {
|
||||||
private static final String TAG = "PeriodicJobReceiver";
|
private static final String TAG = "PeriodicJobReceiver";
|
||||||
@@ -39,6 +42,7 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
|
|||||||
Log.w(TAG, "do not refresh job for work profile action=" + action);
|
Log.w(TAG, "do not refresh job for work profile action=" + action);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
BatteryUsageLogUtils.writeLog(context, Action.EXECUTE_JOB, "");
|
||||||
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
|
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
|
||||||
AppUsageDataLoader.enqueueWork(context);
|
AppUsageDataLoader.enqueueWork(context);
|
||||||
Log.d(TAG, "refresh periodic job from action=" + action);
|
Log.d(TAG, "refresh periodic job from action=" + action);
|
||||||
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 com.android.settings.fuelgauge.batteryusage.bugreport;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLog;
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry;
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Writes and reads a historical log of battery usage periodic job events. */
|
||||||
|
public final class BatteryUsageLogUtils {
|
||||||
|
private static final String TAG = "BatteryUsageLogUtils";
|
||||||
|
private static final String BATTERY_USAGE_FILE_NAME = "battery_usage_historical_logs";
|
||||||
|
private static final String LOGS_KEY = "battery_usage_logs_key";
|
||||||
|
|
||||||
|
// 24 hours x 4 events every hour x 3 days
|
||||||
|
static final int MAX_ENTRIES = 288;
|
||||||
|
|
||||||
|
private BatteryUsageLogUtils() {}
|
||||||
|
|
||||||
|
/** Write the log into the {@link SharedPreferences}. */
|
||||||
|
public static void writeLog(Context context, Action action, String actionDescription) {
|
||||||
|
final SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||||
|
final BatteryUsageHistoricalLogEntry newLogEntry =
|
||||||
|
BatteryUsageHistoricalLogEntry.newBuilder()
|
||||||
|
.setTimestamp(System.currentTimeMillis())
|
||||||
|
.setAction(action)
|
||||||
|
.setActionDescription(actionDescription)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final BatteryUsageHistoricalLog existingLog =
|
||||||
|
parseLogFromString(sharedPreferences.getString(LOGS_KEY, ""));
|
||||||
|
final BatteryUsageHistoricalLog.Builder newLogBuilder = existingLog.toBuilder();
|
||||||
|
// Prune old entries to limit the max logging data count.
|
||||||
|
if (existingLog.getLogEntryCount() >= MAX_ENTRIES) {
|
||||||
|
newLogBuilder.removeLogEntry(0);
|
||||||
|
}
|
||||||
|
newLogBuilder.addLogEntry(newLogEntry);
|
||||||
|
|
||||||
|
final String loggingContent =
|
||||||
|
Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT);
|
||||||
|
sharedPreferences
|
||||||
|
.edit()
|
||||||
|
.putString(LOGS_KEY, loggingContent)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prints the historical log that has previously been stored by this utility. */
|
||||||
|
public static void printHistoricalLog(Context context, PrintWriter writer) {
|
||||||
|
final BatteryUsageHistoricalLog existingLog = parseLogFromString(
|
||||||
|
getSharedPreferences(context).getString(LOGS_KEY, ""));
|
||||||
|
final List<BatteryUsageHistoricalLogEntry> logEntryList = existingLog.getLogEntryList();
|
||||||
|
if (logEntryList.isEmpty()) {
|
||||||
|
writer.println("\tnothing to dump");
|
||||||
|
} else {
|
||||||
|
logEntryList.forEach(entry -> writer.println(toString(entry)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static SharedPreferences getSharedPreferences(Context context) {
|
||||||
|
return context.getApplicationContext()
|
||||||
|
.getSharedPreferences(BATTERY_USAGE_FILE_NAME, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BatteryUsageHistoricalLog parseLogFromString(String storedLogs) {
|
||||||
|
return BatteryUtils.parseProtoFromString(
|
||||||
|
storedLogs, BatteryUsageHistoricalLog.getDefaultInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toString(BatteryUsageHistoricalLogEntry entry) {
|
||||||
|
final StringBuilder builder = new StringBuilder("\t")
|
||||||
|
.append(ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()))
|
||||||
|
.append(" " + entry.getAction());
|
||||||
|
final String description = entry.getActionDescription();
|
||||||
|
if (description != null && !description.isEmpty()) {
|
||||||
|
builder.append(" " + description);
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
@@ -39,6 +39,12 @@ public final class LogUtils {
|
|||||||
private static final Duration DUMP_TIME_OFFSET_FOR_ENTRY = Duration.ofHours(4);
|
private static final Duration DUMP_TIME_OFFSET_FOR_ENTRY = Duration.ofHours(4);
|
||||||
|
|
||||||
static void dumpBatteryUsageDatabaseHist(Context context, PrintWriter writer) {
|
static void dumpBatteryUsageDatabaseHist(Context context, PrintWriter writer) {
|
||||||
|
// Dumps periodic job events.
|
||||||
|
writer.println("\nBattery PeriodicJob History:");
|
||||||
|
BatteryUsageLogUtils.printHistoricalLog(context, writer);
|
||||||
|
writer.flush();
|
||||||
|
|
||||||
|
// Dumps phenotype environments.
|
||||||
DatabaseUtils.dump(context, writer);
|
DatabaseUtils.dump(context, writer);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
final BatteryStateDao dao =
|
final BatteryStateDao dao =
|
||||||
@@ -47,6 +53,7 @@ public final class LogUtils {
|
|||||||
.batteryStateDao();
|
.batteryStateDao();
|
||||||
final long timeOffset =
|
final long timeOffset =
|
||||||
Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis();
|
Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis();
|
||||||
|
|
||||||
// Gets all distinct timestamps.
|
// Gets all distinct timestamps.
|
||||||
final List<Long> timestamps = dao.getDistinctTimestamps(timeOffset);
|
final List<Long> timestamps = dao.getDistinctTimestamps(timeOffset);
|
||||||
final int distinctCount = timestamps.size();
|
final int distinctCount = timestamps.size();
|
||||||
|
@@ -33,7 +33,7 @@ import java.io.PrintWriter;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public final class BatteryHistoricalLogUtilTest {
|
public final class BatteryOptimizeLogUtilsTest {
|
||||||
|
|
||||||
private final StringWriter mTestStringWriter = new StringWriter();
|
private final StringWriter mTestStringWriter = new StringWriter();
|
||||||
private final PrintWriter mTestPrintWriter = new PrintWriter(mTestStringWriter);
|
private final PrintWriter mTestPrintWriter = new PrintWriter(mTestStringWriter);
|
||||||
@@ -43,19 +43,19 @@ public final class BatteryHistoricalLogUtilTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mContext = ApplicationProvider.getApplicationContext();
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
BatteryHistoricalLogUtil.getSharedPreferences(mContext).edit().clear().commit();
|
BatteryOptimizeLogUtils.getSharedPreferences(mContext).edit().clear().commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void printHistoricalLog_withDefaultLogs() {
|
public void printHistoricalLog_withDefaultLogs() {
|
||||||
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
BatteryOptimizeLogUtils.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
||||||
assertThat(mTestStringWriter.toString()).contains("nothing to dump");
|
assertThat(mTestStringWriter.toString()).contains("nothing to dump");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeLog_withExpectedLogs() {
|
public void writeLog_withExpectedLogs() {
|
||||||
BatteryHistoricalLogUtil.writeLog(mContext, Action.APPLY, "pkg1", "logs");
|
BatteryOptimizeLogUtils.writeLog(mContext, Action.APPLY, "pkg1", "logs");
|
||||||
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
BatteryOptimizeLogUtils.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
||||||
|
|
||||||
assertThat(mTestStringWriter.toString()).contains(
|
assertThat(mTestStringWriter.toString()).contains(
|
||||||
"pkg1\taction:APPLY\tevent:logs");
|
"pkg1\taction:APPLY\tevent:logs");
|
||||||
@@ -63,21 +63,27 @@ public final class BatteryHistoricalLogUtilTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeLog_multipleLogs_withCorrectCounts() {
|
public void writeLog_multipleLogs_withCorrectCounts() {
|
||||||
for (int i = 0; i < BatteryHistoricalLogUtil.MAX_ENTRIES; i++) {
|
final int expectedCount = 10;
|
||||||
BatteryHistoricalLogUtil.writeLog(mContext, Action.LEAVE, "pkg" + i, "logs");
|
for (int i = 0; i < expectedCount; i++) {
|
||||||
|
BatteryOptimizeLogUtils.writeLog(mContext, Action.LEAVE, "pkg" + i, "logs");
|
||||||
}
|
}
|
||||||
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
BatteryOptimizeLogUtils.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
||||||
|
|
||||||
assertThat(mTestStringWriter.toString().split("LEAVE").length).isEqualTo(41);
|
assertActionCount("LEAVE", expectedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeLog_overMaxEntriesLogs_withCorrectCounts() {
|
public void writeLog_overMaxEntriesLogs_withCorrectCounts() {
|
||||||
for (int i = 0; i < BatteryHistoricalLogUtil.MAX_ENTRIES + 10; i++) {
|
for (int i = 0; i < BatteryOptimizeLogUtils.MAX_ENTRIES + 10; i++) {
|
||||||
BatteryHistoricalLogUtil.writeLog(mContext, Action.RESET, "pkg" + i, "logs");
|
BatteryOptimizeLogUtils.writeLog(mContext, Action.RESET, "pkg" + i, "logs");
|
||||||
}
|
}
|
||||||
BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
BatteryOptimizeLogUtils.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter);
|
||||||
|
|
||||||
assertThat(mTestStringWriter.toString().split("RESET").length).isEqualTo(41);
|
assertActionCount("RESET", BatteryOptimizeLogUtils.MAX_ENTRIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertActionCount(String token, int count) {
|
||||||
|
final String dumpResults = mTestStringWriter.toString();
|
||||||
|
assertThat(dumpResults.split(token).length).isEqualTo(count + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -40,6 +40,7 @@ import com.android.settings.fuelgauge.BatteryUtils;
|
|||||||
import com.android.settings.fuelgauge.batteryusage.BatteryEntry.NameAndIcon;
|
import com.android.settings.fuelgauge.batteryusage.BatteryEntry.NameAndIcon;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -232,6 +233,7 @@ public class BatteryEntryTest {
|
|||||||
assertThat(entry.getTimeInBackgroundMs()).isEqualTo(0);
|
assertThat(entry.getTimeInBackgroundMs()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
public void testUidCache_switchLocale_shouldCleanCache() {
|
public void testUidCache_switchLocale_shouldCleanCache() {
|
||||||
Locale.setDefault(new Locale("en_US"));
|
Locale.setDefault(new Locale("en_US"));
|
||||||
|
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 com.android.settings.fuelgauge.batteryusage.bugreport;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public final class BatteryUsageLogUtilsTest {
|
||||||
|
|
||||||
|
private StringWriter mTestStringWriter;
|
||||||
|
private PrintWriter mTestPrintWriter;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mTestStringWriter = new StringWriter();
|
||||||
|
mTestPrintWriter = new PrintWriter(mTestStringWriter);
|
||||||
|
BatteryUsageLogUtils.getSharedPreferences(mContext).edit().clear().commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void printHistoricalLog_withDefaultLogs() {
|
||||||
|
final String expectedInformation = "nothing to dump";
|
||||||
|
// Environment checking.
|
||||||
|
assertThat(mTestStringWriter.toString().contains(expectedInformation)).isFalse();
|
||||||
|
|
||||||
|
BatteryUsageLogUtils.printHistoricalLog(mContext, mTestPrintWriter);
|
||||||
|
assertThat(mTestStringWriter.toString()).contains(expectedInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeLog_multipleLogs_withCorrectCounts() {
|
||||||
|
final int expectedCount = 10;
|
||||||
|
for (int i = 0; i < expectedCount; i++) {
|
||||||
|
BatteryUsageLogUtils.writeLog(mContext, Action.SCHEDULE_JOB, "");
|
||||||
|
}
|
||||||
|
BatteryUsageLogUtils.writeLog(mContext, Action.EXECUTE_JOB, "");
|
||||||
|
|
||||||
|
BatteryUsageLogUtils.printHistoricalLog(mContext, mTestPrintWriter);
|
||||||
|
|
||||||
|
assertActionCount("SCHEDULE_JOB", expectedCount);
|
||||||
|
assertActionCount("EXECUTE_JOB", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeLog_overMaxEntriesLogs_withCorrectCounts() {
|
||||||
|
BatteryUsageLogUtils.writeLog(mContext, Action.SCHEDULE_JOB, "");
|
||||||
|
BatteryUsageLogUtils.writeLog(mContext, Action.SCHEDULE_JOB, "");
|
||||||
|
for (int i = 0; i < BatteryUsageLogUtils.MAX_ENTRIES * 2; i++) {
|
||||||
|
BatteryUsageLogUtils.writeLog(mContext, Action.EXECUTE_JOB, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
BatteryUsageLogUtils.printHistoricalLog(mContext, mTestPrintWriter);
|
||||||
|
|
||||||
|
final String dumpResults = mTestStringWriter.toString();
|
||||||
|
assertThat(dumpResults.contains("SCHEDULE_JOB")).isFalse();
|
||||||
|
assertActionCount("EXECUTE_JOB", BatteryUsageLogUtils.MAX_ENTRIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertActionCount(String token, int count) {
|
||||||
|
final String dumpResults = mTestStringWriter.toString();
|
||||||
|
assertThat(dumpResults.split(token).length).isEqualTo(count + 1);
|
||||||
|
}
|
||||||
|
}
|
@@ -87,6 +87,7 @@ public final class BugReportContentProviderTest {
|
|||||||
mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {});
|
mBugReportContentProvider.dump(FileDescriptor.out, mPrintWriter, new String[] {});
|
||||||
|
|
||||||
String dumpContent = mStringWriter.toString();
|
String dumpContent = mStringWriter.toString();
|
||||||
|
assertThat(dumpContent).contains("Battery PeriodicJob History");
|
||||||
assertThat(dumpContent).contains("Battery DatabaseHistory");
|
assertThat(dumpContent).contains("Battery DatabaseHistory");
|
||||||
assertThat(dumpContent).contains(PACKAGE_NAME1);
|
assertThat(dumpContent).contains(PACKAGE_NAME1);
|
||||||
assertThat(dumpContent).contains(PACKAGE_NAME2);
|
assertThat(dumpContent).contains(PACKAGE_NAME2);
|
||||||
|
Reference in New Issue
Block a user