Merge "Format battery java code" into main

This commit is contained in:
Jun Lan
2023-11-06 06:48:20 +00:00
committed by Android (Google) Code Review
127 changed files with 4343 additions and 3581 deletions

View File

@@ -61,15 +61,15 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
/** /**
* Power usage detail fragment for each app, this fragment contains * Power usage detail fragment for each app, this fragment contains <br>
* * <br>
* 1. Detail battery usage information for app(i.e. usage time, usage amount) * 1. Detail battery usage information for app(i.e. usage time, usage amount) <br>
* 2. Battery related controls for app(i.e uninstall, force stop) * 2. Battery related controls for app(i.e uninstall, force stop)
*/ */
public class AdvancedPowerUsageDetail extends DashboardFragment implements public class AdvancedPowerUsageDetail extends DashboardFragment
ButtonActionDialogFragment.AppButtonsDialogListener, implements ButtonActionDialogFragment.AppButtonsDialogListener,
Preference.OnPreferenceClickListener, Preference.OnPreferenceClickListener,
Preference.OnPreferenceChangeListener { Preference.OnPreferenceChangeListener {
public static final String TAG = "AdvancedPowerDetail"; public static final String TAG = "AdvancedPowerDetail";
public static final String EXTRA_UID = "extra_uid"; public static final String EXTRA_UID = "extra_uid";
public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
@@ -96,23 +96,17 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
private AppButtonsPreferenceController mAppButtonsPreferenceController; private AppButtonsPreferenceController mAppButtonsPreferenceController;
private PowerUsageTimeController mPowerUsageTimeController; private PowerUsageTimeController mPowerUsageTimeController;
@VisibleForTesting @VisibleForTesting LayoutPreference mHeaderPreference;
LayoutPreference mHeaderPreference; @VisibleForTesting ApplicationsState mState;
@VisibleForTesting @VisibleForTesting ApplicationsState.AppEntry mAppEntry;
ApplicationsState mState; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
@VisibleForTesting @VisibleForTesting PrimarySwitchPreference mAllowBackgroundUsagePreference;
ApplicationsState.AppEntry mAppEntry;
@VisibleForTesting @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode
BatteryOptimizeUtils mBatteryOptimizeUtils;
@VisibleForTesting
PrimarySwitchPreference mAllowBackgroundUsagePreference;
@VisibleForTesting
@BatteryOptimizeUtils.OptimizationMode
int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN;
@VisibleForTesting
BackupManager mBackupManager; @VisibleForTesting BackupManager mBackupManager;
@VisibleForTesting @VisibleForTesting StringBuilder mLogStringBuilder;
StringBuilder mLogStringBuilder;
// A wrapper class to carry LaunchBatteryDetailPage required arguments. // A wrapper class to carry LaunchBatteryDetailPage required arguments.
private static final class LaunchBatteryDetailPageArgs { private static final class LaunchBatteryDetailPageArgs {
@@ -134,9 +128,14 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
/** Launches battery details page for an individual battery consumer fragment. */ /** Launches battery details page for an individual battery consumer fragment. */
public static void startBatteryDetailPage( public static void startBatteryDetailPage(
Context context, int sourceMetricsCategory, Context context,
BatteryDiffEntry diffEntry, String usagePercent, String slotInformation, int sourceMetricsCategory,
boolean showTimeInformation, String anomalyHintPrefKey, String anomalyHintText) { BatteryDiffEntry diffEntry,
String usagePercent,
String slotInformation,
boolean showTimeInformation,
String anomalyHintPrefKey,
String anomalyHintText) {
final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs(); final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
// configure the launch argument. // configure the launch argument.
launchArgs.mUsagePercent = usagePercent; launchArgs.mUsagePercent = usagePercent;
@@ -159,8 +158,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
} }
/** Launches battery details page for an individual battery consumer. */ /** Launches battery details page for an individual battery consumer. */
public static void startBatteryDetailPage(Activity caller, public static void startBatteryDetailPage(
InstrumentedPreferenceFragment fragment, BatteryEntry entry, String usagePercent) { Activity caller,
InstrumentedPreferenceFragment fragment,
BatteryEntry entry,
String usagePercent) {
final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs(); final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
// configure the launch argument. // configure the launch argument.
launchArgs.mUsagePercent = usagePercent; launchArgs.mUsagePercent = usagePercent;
@@ -197,8 +199,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
args.putBoolean(EXTRA_SHOW_TIME_INFO, launchArgs.mShowTimeInformation); args.putBoolean(EXTRA_SHOW_TIME_INFO, launchArgs.mShowTimeInformation);
args.putString(EXTRA_ANOMALY_HINT_PREF_KEY, launchArgs.mAnomalyHintPrefKey); args.putString(EXTRA_ANOMALY_HINT_PREF_KEY, launchArgs.mAnomalyHintPrefKey);
args.putString(EXTRA_ANOMALY_HINT_TEXT, launchArgs.mAnomalyHintText); args.putString(EXTRA_ANOMALY_HINT_TEXT, launchArgs.mAnomalyHintText);
final int userId = launchArgs.mIsUserEntry ? ActivityManager.getCurrentUser() final int userId =
: UserHandle.getUserId(launchArgs.mUid); launchArgs.mIsUserEntry
? ActivityManager.getCurrentUser()
: UserHandle.getUserId(launchArgs.mUid);
new SubSettingLauncher(context) new SubSettingLauncher(context)
.setDestination(AdvancedPowerUsageDetail.class.getName()) .setDestination(AdvancedPowerUsageDetail.class.getName())
@@ -209,11 +213,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
.launch(); .launch();
} }
/** /** Start packageName's battery detail page. */
* Start packageName's battery detail page.
*/
public static void startBatteryDetailPage( public static void startBatteryDetailPage(
Activity caller, Instrumentable instrumentable, String packageName, Activity caller,
Instrumentable instrumentable,
String packageName,
UserHandle userHandle) { UserHandle userHandle) {
final Bundle args = new Bundle(3); final Bundle args = new Bundle(3);
final PackageManager packageManager = caller.getPackageManager(); final PackageManager packageManager = caller.getPackageManager();
@@ -261,15 +265,18 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
initHeader(); initHeader();
mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode();
initFooter(); initFooter();
mExecutor.execute(() -> { mExecutor.execute(
final String packageName = BatteryUtils () -> {
.getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); final String packageName =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() BatteryUtils.getLoggingPackageName(
.action( getContext(), mBatteryOptimizeUtils.getPackageName());
getContext(), FeatureFactory.getFeatureFactory()
SettingsEnums.OPEN_APP_BATTERY_USAGE, .getMetricsFeatureProvider()
packageName); .action(
}); getContext(),
SettingsEnums.OPEN_APP_BATTERY_USAGE,
packageName);
});
mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode);
} }
@@ -282,22 +289,23 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
logMetricCategory(currentOptimizeMode); logMetricCategory(currentOptimizeMode);
mExecutor.execute(() -> { mExecutor.execute(
BatteryOptimizeLogUtils.writeLog( () -> {
getContext().getApplicationContext(), BatteryOptimizeLogUtils.writeLog(
Action.LEAVE, getContext().getApplicationContext(),
BatteryOptimizeLogUtils.getPackageNameWithUserId( Action.LEAVE,
mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), BatteryOptimizeLogUtils.getPackageNameWithUserId(
mLogStringBuilder.toString()); mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
}); mLogStringBuilder.toString());
});
Log.d(TAG, "Leave with mode: " + currentOptimizeMode); Log.d(TAG, "Leave with mode: " + currentOptimizeMode);
} }
@VisibleForTesting @VisibleForTesting
void notifyBackupManager() { void notifyBackupManager() {
if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) {
final BackupManager backupManager = mBackupManager != null final BackupManager backupManager =
? mBackupManager : new BackupManager(getContext()); mBackupManager != null ? mBackupManager : new BackupManager(getContext());
backupManager.dataChanged(); backupManager.dataChanged();
} }
} }
@@ -307,10 +315,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header);
final Activity context = getActivity(); final Activity context = getActivity();
final Bundle bundle = getArguments(); final Bundle bundle = getArguments();
EntityHeaderController controller = EntityHeaderController EntityHeaderController controller =
.newInstance(context, this, appSnippet) EntityHeaderController.newInstance(context, this, appSnippet)
.setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, .setButtonActions(
EntityHeaderController.ActionType.ACTION_NONE); EntityHeaderController.ActionType.ACTION_NONE,
EntityHeaderController.ActionType.ACTION_NONE);
if (mAppEntry == null) { if (mAppEntry == null) {
controller.setLabel(bundle.getString(EXTRA_LABEL)); controller.setLabel(bundle.getString(EXTRA_LABEL));
@@ -334,8 +343,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME); final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
final String anomalyHintPrefKey = bundle.getString(EXTRA_ANOMALY_HINT_PREF_KEY); final String anomalyHintPrefKey = bundle.getString(EXTRA_ANOMALY_HINT_PREF_KEY);
final String anomalyHintText = bundle.getString(EXTRA_ANOMALY_HINT_TEXT); final String anomalyHintText = bundle.getString(EXTRA_ANOMALY_HINT_TEXT);
mPowerUsageTimeController.handleScreenTimeUpdated(slotTime, screenOnTimeInMs, mPowerUsageTimeController.handleScreenTimeUpdated(
backgroundTimeMs, anomalyHintPrefKey, anomalyHintText); slotTime,
screenOnTimeInMs,
backgroundTimeMs,
anomalyHintPrefKey,
anomalyHintText);
} }
controller.done(true /* rebindActions */); controller.done(true /* rebindActions */);
} }
@@ -387,9 +400,15 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
final int uid = bundle.getInt(EXTRA_UID, 0); final int uid = bundle.getInt(EXTRA_UID, 0);
final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
mAppButtonsPreferenceController = new AppButtonsPreferenceController( mAppButtonsPreferenceController =
(SettingsActivity) getActivity(), this, getSettingsLifecycle(), new AppButtonsPreferenceController(
packageName, mState, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); (SettingsActivity) getActivity(),
this,
getSettingsLifecycle(),
packageName,
mState,
REQUEST_UNINSTALL,
REQUEST_REMOVE_DEVICE_ADMIN);
if (bundle.getBoolean(EXTRA_SHOW_TIME_INFO, false)) { if (bundle.getBoolean(EXTRA_SHOW_TIME_INFO, false)) {
mPowerUsageTimeController = new PowerUsageTimeController(getContext()); mPowerUsageTimeController = new PowerUsageTimeController(getContext());
controllers.add(mPowerUsageTimeController); controllers.add(mPowerUsageTimeController);
@@ -461,17 +480,20 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
return; return;
} }
int finalMetricCategory = metricCategory; int finalMetricCategory = metricCategory;
mExecutor.execute(() -> { mExecutor.execute(
String packageName = BatteryUtils () -> {
.getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); String packageName =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() BatteryUtils.getLoggingPackageName(
.action( getContext(), mBatteryOptimizeUtils.getPackageName());
/* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE, FeatureFactory.getFeatureFactory()
/* action */ finalMetricCategory, .getMetricsFeatureProvider()
/* pageId */ SettingsEnums.OPEN_APP_BATTERY_USAGE, .action(
packageName, /* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE,
getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT)); /* action */ finalMetricCategory,
}); /* pageId */ SettingsEnums.OPEN_APP_BATTERY_USAGE,
packageName,
getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT));
});
} }
private void onCreateBackgroundUsageState(String packageName) { private void onCreateBackgroundUsageState(String packageName) {

View File

@@ -29,14 +29,11 @@ import android.util.Log;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
/** /** Trampoline activity for launching the {@link AdvancedPowerUsageDetail} fragment. */
* Trampoline activity for launching the {@link AdvancedPowerUsageDetail} fragment.
*/
public class AdvancedPowerUsageDetailActivity extends AppCompatActivity { public class AdvancedPowerUsageDetailActivity extends AppCompatActivity {
private static final String TAG = "AdvancedPowerDetailActivity"; private static final String TAG = "AdvancedPowerDetailActivity";

View File

@@ -53,8 +53,9 @@ public class AllowBackgroundPreferenceController extends AbstractPreferenceContr
public void updateState(Preference preference) { public void updateState(Preference preference) {
preference.setEnabled(mBatteryOptimizeUtils.isOptimizeModeMutable()); preference.setEnabled(mBatteryOptimizeUtils.isOptimizeModeMutable());
final boolean isAllowBackground = mBatteryOptimizeUtils.getAppOptimizationMode() final boolean isAllowBackground =
!= BatteryOptimizeUtils.MODE_RESTRICTED; mBatteryOptimizeUtils.getAppOptimizationMode()
!= BatteryOptimizeUtils.MODE_RESTRICTED;
setChecked(preference, isAllowBackground); setChecked(preference, isAllowBackground);
} }

View File

@@ -24,11 +24,9 @@ import androidx.preference.TwoStatePreference;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
/** /** Controller to change and update the auto restriction toggle */
* Controller to change and update the auto restriction toggle public class AutoRestrictionPreferenceController extends BasePreferenceController
*/ implements Preference.OnPreferenceChangeListener {
public class AutoRestrictionPreferenceController extends BasePreferenceController implements
Preference.OnPreferenceChangeListener {
private static final String KEY_SMART_BATTERY = "auto_restriction"; private static final String KEY_SMART_BATTERY = "auto_restriction";
private static final int ON = 1; private static final int ON = 1;
private static final int OFF = 0; private static final int OFF = 0;
@@ -50,15 +48,20 @@ public class AutoRestrictionPreferenceController extends BasePreferenceControlle
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
super.updateState(preference); super.updateState(preference);
final boolean smartBatteryOn = Settings.Global.getInt(mContext.getContentResolver(), final boolean smartBatteryOn =
Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON) == ON; Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
ON)
== ON;
((TwoStatePreference) preference).setChecked(smartBatteryOn); ((TwoStatePreference) preference).setChecked(smartBatteryOn);
} }
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean smartBatteryOn = (Boolean) newValue; final boolean smartBatteryOn = (Boolean) newValue;
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(
mContext.getContentResolver(),
Settings.Global.APP_AUTO_RESTRICTION_ENABLED, Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
smartBatteryOn ? ON : OFF); smartBatteryOn ? ON : OFF);
return true; return true;

View File

@@ -63,13 +63,15 @@ public class BatteryActiveView extends View {
return; return;
} }
mPaint.setColor(color); mPaint.setColor(color);
canvas.drawRect(start / period * getWidth(), 0, end / period * getWidth(), getHeight(), canvas.drawRect(
mPaint); start / period * getWidth(), 0, end / period * getWidth(), getHeight(), mPaint);
} }
public interface BatteryActiveProvider { public interface BatteryActiveProvider {
boolean hasData(); boolean hasData();
long getPeriod(); long getPeriod();
SparseIntArray getColorArray(); SparseIntArray getColorArray();
} }
} }

View File

@@ -52,6 +52,7 @@ import java.util.List;
public final class BatteryBackupHelper implements BackupHelper { 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";
// Definition for the device build information. // Definition for the device build information.
public static final String KEY_BUILD_BRAND = "device_build_brand"; public static final String KEY_BUILD_BRAND = "device_build_brand";
public static final String KEY_BUILD_PRODUCT = "device_build_product"; public static final String KEY_BUILD_PRODUCT = "device_build_product";
@@ -70,17 +71,12 @@ public final class BatteryBackupHelper implements BackupHelper {
static final String DELIMITER_MODE = ":"; static final String DELIMITER_MODE = ":";
static final String KEY_OPTIMIZATION_LIST = "optimization_mode_list"; static final String KEY_OPTIMIZATION_LIST = "optimization_mode_list";
@VisibleForTesting @VisibleForTesting ArraySet<ApplicationInfo> mTestApplicationInfoList = null;
ArraySet<ApplicationInfo> mTestApplicationInfoList = null;
@VisibleForTesting @VisibleForTesting PowerAllowlistBackend mPowerAllowlistBackend;
PowerAllowlistBackend mPowerAllowlistBackend; @VisibleForTesting IDeviceIdleController mIDeviceIdleController;
@VisibleForTesting @VisibleForTesting IPackageManager mIPackageManager;
IDeviceIdleController mIDeviceIdleController; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
@VisibleForTesting
IPackageManager mIPackageManager;
@VisibleForTesting
BatteryOptimizeUtils mBatteryOptimizeUtils;
private byte[] mOptimizationModeBytes; private byte[] mOptimizationModeBytes;
private boolean mVerifyMigrateConfiguration = false; private boolean mVerifyMigrateConfiguration = false;
@@ -95,8 +91,8 @@ public final class BatteryBackupHelper implements BackupHelper {
} }
@Override @Override
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, public void performBackup(
ParcelFileDescriptor newState) { ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
if (!isOwner() || data == null) { if (!isOwner() || data == null) {
Log.w(TAG, "ignore performBackup() for non-owner or empty data"); Log.w(TAG, "ignore performBackup() for non-owner or empty data");
return; return;
@@ -149,8 +145,7 @@ public final class BatteryBackupHelper implements BackupHelper {
} }
@Override @Override
public void writeNewStateDescription(ParcelFileDescriptor newState) { public void writeNewStateDescription(ParcelFileDescriptor newState) {}
}
private List<String> getFullPowerList() { private List<String> getFullPowerList() {
final long timestamp = System.currentTimeMillis(); final long timestamp = System.currentTimeMillis();
@@ -166,8 +161,11 @@ public final class BatteryBackupHelper implements BackupHelper {
Log.w(TAG, "no data found in the getFullPowerList()"); Log.w(TAG, "no data found in the getFullPowerList()");
return new ArrayList<>(); return new ArrayList<>();
} }
Log.d(TAG, String.format("getFullPowerList() size=%d in %d/ms", Log.d(
allowlistedApps.length, (System.currentTimeMillis() - timestamp))); TAG,
String.format(
"getFullPowerList() size=%d in %d/ms",
allowlistedApps.length, (System.currentTimeMillis() - timestamp)));
return Arrays.asList(allowlistedApps); return Arrays.asList(allowlistedApps);
} }
@@ -187,27 +185,34 @@ public final class BatteryBackupHelper implements BackupHelper {
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);
@BatteryOptimizeUtils.OptimizationMode @BatteryOptimizeUtils.OptimizationMode
final int optimizationMode = BatteryOptimizeUtils.getAppOptimizationMode( final int optimizationMode =
mode, allowlistedApps.contains(info.packageName)); BatteryOptimizeUtils.getAppOptimizationMode(
mode, allowlistedApps.contains(info.packageName));
// Ignores default optimized/unknown state or system/default apps. // Ignores default optimized/unknown state or system/default apps.
if (optimizationMode == BatteryOptimizeUtils.MODE_OPTIMIZED if (optimizationMode == BatteryOptimizeUtils.MODE_OPTIMIZED
|| optimizationMode == BatteryOptimizeUtils.MODE_UNKNOWN || optimizationMode == BatteryOptimizeUtils.MODE_UNKNOWN
|| isSystemOrDefaultApp(info.packageName, info.uid)) { || isSystemOrDefaultApp(info.packageName, info.uid)) {
continue; continue;
} }
final String packageOptimizeMode = final String packageOptimizeMode = 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);
BatteryOptimizeLogUtils.writeLog( BatteryOptimizeLogUtils.writeLog(
sharedPreferences, Action.BACKUP, info.packageName, sharedPreferences,
Action.BACKUP,
info.packageName,
/* actionDescription */ "mode: " + optimizationMode); /* actionDescription */ "mode: " + optimizationMode);
backupCount++; backupCount++;
} }
writeBackupData(data, KEY_OPTIMIZATION_LIST, builder.toString()); writeBackupData(data, KEY_OPTIMIZATION_LIST, builder.toString());
Log.d(TAG, String.format("backup getInstalledApplications():%d count=%d in %d/ms", Log.d(
applications.size(), backupCount, (System.currentTimeMillis() - timestamp))); TAG,
String.format(
"backup getInstalledApplications():%d count=%d in %d/ms",
applications.size(),
backupCount,
(System.currentTimeMillis() - timestamp)));
} }
@VisibleForTesting @VisibleForTesting
@@ -225,8 +230,8 @@ public final class BatteryBackupHelper implements BackupHelper {
} }
int restoreCount = 0; int restoreCount = 0;
for (int index = 0; index < appConfigurations.length; index++) { for (int index = 0; index < appConfigurations.length; index++) {
final String[] results = appConfigurations[index] final String[] results =
.split(BatteryBackupHelper.DELIMITER_MODE); appConfigurations[index].split(BatteryBackupHelper.DELIMITER_MODE);
// Example format: com.android.systemui:2 we should have length=2 // Example format: com.android.systemui:2 we should have length=2
if (results == null || results.length != 2) { if (results == null || results.length != 2) {
Log.w(TAG, "invalid raw data found:" + appConfigurations[index]); Log.w(TAG, "invalid raw data found:" + appConfigurations[index]);
@@ -244,15 +249,17 @@ public final class BatteryBackupHelper implements BackupHelper {
try { try {
optimizationMode = Integer.parseInt(results[1]); optimizationMode = Integer.parseInt(results[1]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.e(TAG, "failed to parse the optimization mode: " Log.e(TAG, "failed to parse the optimization mode: " + appConfigurations[index], e);
+ appConfigurations[index], e);
continue; continue;
} }
restoreOptimizationMode(packageName, optimizationMode); restoreOptimizationMode(packageName, optimizationMode);
restoreCount++; restoreCount++;
} }
Log.d(TAG, String.format("restoreOptimizationMode() count=%d in %d/ms", Log.d(
restoreCount, (System.currentTimeMillis() - timestamp))); TAG,
String.format(
"restoreOptimizationMode() count=%d in %d/ms",
restoreCount, (System.currentTimeMillis() - timestamp)));
return restoreCount; return restoreCount;
} }
@@ -319,8 +326,9 @@ public final class BatteryBackupHelper implements BackupHelper {
if (mIDeviceIdleController != null) { if (mIDeviceIdleController != null) {
return mIDeviceIdleController; return mIDeviceIdleController;
} }
mIDeviceIdleController = IDeviceIdleController.Stub.asInterface( mIDeviceIdleController =
ServiceManager.getService(DEVICE_IDLE_SERVICE)); IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(DEVICE_IDLE_SERVICE));
return mIDeviceIdleController; return mIDeviceIdleController;
} }
@@ -374,8 +382,7 @@ public final class BatteryBackupHelper implements BackupHelper {
return dataBytes; return dataBytes;
} }
private static void writeBackupData( private static void writeBackupData(BackupDataOutput data, String dataKey, String dataContent) {
BackupDataOutput data, String dataKey, String dataContent) {
if (dataContent == null || dataContent.isEmpty()) { if (dataContent == null || dataContent.isEmpty()) {
return; return;
} }

View File

@@ -34,19 +34,19 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** /**
* Use this broadcastReceiver to listen to the battery change and it will invoke * Use this broadcastReceiver to listen to the battery change and it will invoke {@link
* {@link OnBatteryChangedListener} * OnBatteryChangedListener}
*/ */
public class BatteryBroadcastReceiver extends BroadcastReceiver { public class BatteryBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BatteryBroadcastRcvr"; private static final String TAG = "BatteryBroadcastRcvr";
/** /**
* Callback if any of the monitored fields has been changed: * Callback if any of the monitored fields has been changed: <br>
* * <br>
* Battery level(e.g. 100%->99%) * Battery level(e.g. 100%->99%) Battery status(e.g. plugged->unplugged) <br>
* Battery status(e.g. plugged->unplugged) * Battery saver(e.g.off->on) <br>
* Battery saver(e.g. off->on) * Battery health(e.g. good->overheat) <br>
* Battery health(e.g. good->overheat)
* Battery charging status(e.g. default->long life) * Battery charging status(e.g. default->long life)
*/ */
public interface OnBatteryChangedListener { public interface OnBatteryChangedListener {
@@ -54,13 +54,15 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver {
} }
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({BatteryUpdateType.MANUAL, @IntDef({
BatteryUpdateType.BATTERY_LEVEL, BatteryUpdateType.MANUAL,
BatteryUpdateType.BATTERY_SAVER, BatteryUpdateType.BATTERY_LEVEL,
BatteryUpdateType.BATTERY_STATUS, BatteryUpdateType.BATTERY_SAVER,
BatteryUpdateType.BATTERY_HEALTH, BatteryUpdateType.BATTERY_STATUS,
BatteryUpdateType.CHARGING_STATUS, BatteryUpdateType.BATTERY_HEALTH,
BatteryUpdateType.BATTERY_NOT_PRESENT}) BatteryUpdateType.CHARGING_STATUS,
BatteryUpdateType.BATTERY_NOT_PRESENT
})
public @interface BatteryUpdateType { public @interface BatteryUpdateType {
int MANUAL = 0; int MANUAL = 0;
int BATTERY_LEVEL = 1; int BATTERY_LEVEL = 1;
@@ -71,14 +73,10 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver {
int BATTERY_NOT_PRESENT = 6; int BATTERY_NOT_PRESENT = 6;
} }
@VisibleForTesting @VisibleForTesting String mBatteryLevel;
String mBatteryLevel; @VisibleForTesting String mBatteryStatus;
@VisibleForTesting @VisibleForTesting int mChargingStatus;
String mBatteryStatus; @VisibleForTesting int mBatteryHealth;
@VisibleForTesting
int mChargingStatus;
@VisibleForTesting
int mBatteryHealth;
private OnBatteryChangedListener mBatteryListener; private OnBatteryChangedListener mBatteryListener;
private Context mContext; private Context mContext;
@@ -102,8 +100,8 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver {
intentFilter.addAction(BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION); intentFilter.addAction(BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION);
intentFilter.addAction(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED); intentFilter.addAction(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED);
final Intent intent = mContext.registerReceiver(this, intentFilter, final Intent intent =
Context.RECEIVER_EXPORTED); mContext.registerReceiver(this, intentFilter, Context.RECEIVER_EXPORTED);
updateBatteryStatus(intent, true /* forceUpdate */); updateBatteryStatus(intent, true /* forceUpdate */);
} }
@@ -121,10 +119,13 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver {
final String batteryLevel = Utils.getBatteryPercentage(intent); final String batteryLevel = Utils.getBatteryPercentage(intent);
final String batteryStatus = final String batteryStatus =
Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false); Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false);
final int chargingStatus = intent.getIntExtra( final int chargingStatus =
BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT); intent.getIntExtra(
final int batteryHealth = intent.getIntExtra( BatteryManager.EXTRA_CHARGING_STATUS,
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN); BatteryManager.CHARGING_POLICY_DEFAULT);
final int batteryHealth =
intent.getIntExtra(
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
Log.d( Log.d(
TAG, TAG,
"Battery changed: level: " "Battery changed: level: "
@@ -144,7 +145,7 @@ public class BatteryBroadcastReceiver extends BroadcastReceiver {
mBatteryListener.onBatteryChanged(BatteryUpdateType.CHARGING_STATUS); mBatteryListener.onBatteryChanged(BatteryUpdateType.CHARGING_STATUS);
} else if (batteryHealth != mBatteryHealth) { } else if (batteryHealth != mBatteryHealth) {
mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH); mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH);
} else if(!batteryLevel.equals(mBatteryLevel)) { } else if (!batteryLevel.equals(mBatteryLevel)) {
mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL); mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL);
} else if (!batteryStatus.equals(mBatteryStatus)) { } else if (!batteryStatus.equals(mBatteryStatus)) {
mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_STATUS); mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_STATUS);

View File

@@ -34,21 +34,16 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils; import com.android.settingslib.Utils;
import com.android.settingslib.widget.UsageProgressBarPreference; import com.android.settingslib.widget.UsageProgressBarPreference;
/** /** Controller that update the battery header view */
* Controller that update the battery header view
*/
public class BatteryHeaderPreferenceController extends BasePreferenceController public class BatteryHeaderPreferenceController extends BasePreferenceController
implements PreferenceControllerMixin, BatteryPreferenceController { implements PreferenceControllerMixin, BatteryPreferenceController {
private static final String TAG = "BatteryHeaderPreferenceController"; private static final String TAG = "BatteryHeaderPreferenceController";
@VisibleForTesting @VisibleForTesting static final String KEY_BATTERY_HEADER = "battery_header";
static final String KEY_BATTERY_HEADER = "battery_header";
private static final int BATTERY_MAX_LEVEL = 100; private static final int BATTERY_MAX_LEVEL = 100;
@VisibleForTesting @VisibleForTesting BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
BatteryStatusFeatureProvider mBatteryStatusFeatureProvider; @VisibleForTesting UsageProgressBarPreference mBatteryUsageProgressBarPref;
@VisibleForTesting
UsageProgressBarPreference mBatteryUsageProgressBarPref;
private BatteryTip mBatteryTip; private BatteryTip mBatteryTip;
private final PowerManager mPowerManager; private final PowerManager mPowerManager;
@@ -56,8 +51,8 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
public BatteryHeaderPreferenceController(Context context, String key) { public BatteryHeaderPreferenceController(Context context, String key) {
super(context, key); super(context, key);
mPowerManager = context.getSystemService(PowerManager.class); mPowerManager = context.getSystemService(PowerManager.class);
mBatteryStatusFeatureProvider = FeatureFactory.getFeatureFactory() mBatteryStatusFeatureProvider =
.getBatteryStatusFeatureProvider(); FeatureFactory.getFeatureFactory().getBatteryStatusFeatureProvider();
} }
@Override @Override
@@ -96,12 +91,11 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
R.string.battery_state_and_duration, info.statusLabel, info.remainingLabel); R.string.battery_state_and_duration, info.statusLabel, info.remainingLabel);
} else if (mPowerManager.isPowerSaveMode()) { } else if (mPowerManager.isPowerSaveMode()) {
// Power save mode is on // Power save mode is on
final String powerSaverOn = mContext.getString( final String powerSaverOn =
R.string.battery_tip_early_heads_up_done_title); mContext.getString(R.string.battery_tip_early_heads_up_done_title);
return mContext.getString( return mContext.getString(
R.string.battery_state_and_duration, powerSaverOn, info.remainingLabel); R.string.battery_state_and_duration, powerSaverOn, info.remainingLabel);
} else if (mBatteryTip != null } else if (mBatteryTip != null && mBatteryTip.getType() == BatteryTip.TipType.LOW_BATTERY) {
&& mBatteryTip.getType() == BatteryTip.TipType.LOW_BATTERY) {
// Low battery state // Low battery state
final String lowBattery = mContext.getString(R.string.low_battery_summary); final String lowBattery = mContext.getString(R.string.low_battery_summary);
return mContext.getString( return mContext.getString(
@@ -122,9 +116,7 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
mBatteryUsageProgressBarPref.setPercent(info.batteryLevel, BATTERY_MAX_LEVEL); mBatteryUsageProgressBarPref.setPercent(info.batteryLevel, BATTERY_MAX_LEVEL);
} }
/** /** Callback which receives text for the summary line. */
* Callback which receives text for the summary line.
*/
public void updateBatteryStatus(String label, BatteryInfo info) { public void updateBatteryStatus(String label, BatteryInfo info) {
final CharSequence summary = label != null ? label : generateLabel(info); final CharSequence summary = label != null ? label : generateLabel(info);
mBatteryUsageProgressBarPref.setBottomSummary(summary); mBatteryUsageProgressBarPref.setBottomSummary(summary);
@@ -132,8 +124,8 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
} }
public void quickUpdateHeaderPreference() { public void quickUpdateHeaderPreference() {
Intent batteryBroadcast = com.android.settingslib.fuelgauge.BatteryUtils Intent batteryBroadcast =
.getBatteryIntent(mContext); com.android.settingslib.fuelgauge.BatteryUtils.getBatteryIntent(mContext);
final int batteryLevel = Utils.getBatteryLevel(batteryBroadcast); final int batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
final boolean discharging = final boolean discharging =
batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0; batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;
@@ -142,9 +134,7 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
mBatteryUsageProgressBarPref.setPercent(batteryLevel, BATTERY_MAX_LEVEL); mBatteryUsageProgressBarPref.setPercent(batteryLevel, BATTERY_MAX_LEVEL);
} }
/** /** Update summary when battery tips changed. */
* Update summary when battery tips changed.
*/
public void updateHeaderByBatteryTips(BatteryTip batteryTip, BatteryInfo batteryInfo) { public void updateHeaderByBatteryTips(BatteryTip batteryTip, BatteryInfo batteryInfo) {
mBatteryTip = batteryTip; mBatteryTip = batteryTip;
@@ -154,7 +144,8 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
} }
private CharSequence formatBatteryPercentageText(int batteryLevel) { private CharSequence formatBatteryPercentageText(int batteryLevel) {
return TextUtils.expandTemplate(mContext.getText(R.string.battery_header_title_alternate), return TextUtils.expandTemplate(
mContext.getText(R.string.battery_header_title_alternate),
NumberFormat.getIntegerInstance().format(batteryLevel)); NumberFormat.getIntegerInstance().format(batteryLevel));
} }
} }

View File

@@ -68,93 +68,110 @@ public class BatteryInfo {
public void bindHistory(final UsageView view, BatteryDataParser... parsers) { public void bindHistory(final UsageView view, BatteryDataParser... parsers) {
final Context context = view.getContext(); final Context context = view.getContext();
BatteryDataParser parser = new BatteryDataParser() { BatteryDataParser parser =
SparseIntArray points = new SparseIntArray(); new BatteryDataParser() {
long startTime; SparseIntArray mPoints = new SparseIntArray();
int lastTime = -1; long mStartTime;
byte lastLevel; int mLastTime = -1;
byte mLastLevel;
@Override @Override
public void onParsingStarted(long startTime, long endTime) { public void onParsingStarted(long startTime, long endTime) {
this.startTime = startTime; this.mStartTime = startTime;
timePeriod = endTime - startTime; timePeriod = endTime - startTime;
view.clearPaths(); view.clearPaths();
// Initially configure the graph for history only. // Initially configure the graph for history only.
view.configureGraph((int) timePeriod, 100); view.configureGraph((int) timePeriod, 100);
} }
@Override @Override
public void onDataPoint(long time, HistoryItem record) { public void onDataPoint(long time, HistoryItem record) {
lastTime = (int) time; mLastTime = (int) time;
lastLevel = record.batteryLevel; mLastLevel = record.batteryLevel;
points.put(lastTime, lastLevel); mPoints.put(mLastTime, mLastLevel);
} }
@Override @Override
public void onDataGap() { public void onDataGap() {
if (points.size() > 1) { if (mPoints.size() > 1) {
view.addPath(points); view.addPath(mPoints);
} }
points.clear(); mPoints.clear();
} }
@Override @Override
public void onParsingDone() { public void onParsingDone() {
onDataGap(); onDataGap();
// Add projection if we have an estimate. // Add projection if we have an estimate.
if (remainingTimeUs != 0) { if (remainingTimeUs != 0) {
PowerUsageFeatureProvider provider = PowerUsageFeatureProvider provider =
FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); FeatureFactory.getFeatureFactory()
if (!mCharging && provider.isEnhancedBatteryPredictionEnabled(context)) { .getPowerUsageFeatureProvider();
points = provider.getEnhancedBatteryPredictionCurve(context, startTime); if (!mCharging
} else { && provider.isEnhancedBatteryPredictionEnabled(context)) {
// Linear extrapolation. mPoints =
if (lastTime >= 0) { provider.getEnhancedBatteryPredictionCurve(
points.put(lastTime, lastLevel); context, mStartTime);
points.put((int) (timePeriod + } else {
PowerUtil.convertUsToMs(remainingTimeUs)), // Linear extrapolation.
mCharging ? 100 : 0); if (mLastTime >= 0) {
mPoints.put(mLastTime, mLastLevel);
mPoints.put(
(int)
(timePeriod
+ PowerUtil.convertUsToMs(
remainingTimeUs)),
mCharging ? 100 : 0);
}
}
}
// If we have a projection, reconfigure the graph to show it.
if (mPoints != null && mPoints.size() > 0) {
int maxTime = mPoints.keyAt(mPoints.size() - 1);
view.configureGraph(maxTime, 100);
view.addProjectedPath(mPoints);
} }
} }
} };
// If we have a projection, reconfigure the graph to show it.
if (points != null && points.size() > 0) {
int maxTime = points.keyAt(points.size() - 1);
view.configureGraph(maxTime, 100);
view.addProjectedPath(points);
}
}
};
BatteryDataParser[] parserList = new BatteryDataParser[parsers.length + 1]; BatteryDataParser[] parserList = new BatteryDataParser[parsers.length + 1];
for (int i = 0; i < parsers.length; i++) { for (int i = 0; i < parsers.length; i++) {
parserList[i] = parsers[i]; parserList[i] = parsers[i];
} }
parserList[parsers.length] = parser; parserList[parsers.length] = parser;
parseBatteryHistory(parserList); parseBatteryHistory(parserList);
String timeString = context.getString(com.android.settingslib.R.string.charge_length_format, String timeString =
Formatter.formatShortElapsedTime(context, timePeriod)); context.getString(
com.android.settingslib.R.string.charge_length_format,
Formatter.formatShortElapsedTime(context, timePeriod));
String remaining = ""; String remaining = "";
if (remainingTimeUs != 0) { if (remainingTimeUs != 0) {
remaining = context.getString(com.android.settingslib.R.string.remaining_length_format, remaining =
Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000)); context.getString(
com.android.settingslib.R.string.remaining_length_format,
Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000));
} }
view.setBottomLabels(new CharSequence[]{timeString, remaining}); view.setBottomLabels(new CharSequence[] {timeString, remaining});
} }
public static void getBatteryInfo(final Context context, final Callback callback, /** Gets battery info */
boolean shortString) { public static void getBatteryInfo(
BatteryInfo.getBatteryInfo(context, callback, /* batteryUsageStats */ null, shortString); final Context context, final Callback callback, boolean shortString) {
BatteryInfo.getBatteryInfo(context, callback, /* batteryUsageStats */ null, shortString);
} }
static long getSettingsChargeTimeRemaining(final Context context) { static long getSettingsChargeTimeRemaining(final Context context) {
return Settings.Global.getLong( return Settings.Global.getLong(
context.getContentResolver(), context.getContentResolver(),
com.android.settingslib.fuelgauge.BatteryUtils.GLOBAL_TIME_TO_FULL_MILLIS, -1); com.android.settingslib.fuelgauge.BatteryUtils.GLOBAL_TIME_TO_FULL_MILLIS,
-1);
} }
public static void getBatteryInfo(final Context context, final Callback callback, /** Gets battery info */
public static void getBatteryInfo(
final Context context,
final Callback callback,
@Nullable final BatteryUsageStats batteryUsageStats, @Nullable final BatteryUsageStats batteryUsageStats,
boolean shortString) { boolean shortString) {
new AsyncTask<Void, Void, BatteryInfo>() { new AsyncTask<Void, Void, BatteryInfo>() {
@@ -166,8 +183,9 @@ public class BatteryInfo {
stats = batteryUsageStats; stats = batteryUsageStats;
} else { } else {
try { try {
stats = context.getSystemService(BatteryStatsManager.class) stats =
.getBatteryUsageStats(); context.getSystemService(BatteryStatsManager.class)
.getBatteryUsageStats();
shouldCloseBatteryUsageStats = true; shouldCloseBatteryUsageStats = true;
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "getBatteryInfo() from getBatteryUsageStats()", e); Log.e(TAG, "getBatteryInfo() from getBatteryUsageStats()", e);
@@ -175,8 +193,7 @@ public class BatteryInfo {
stats = new BatteryUsageStats.Builder(new String[0]).build(); stats = new BatteryUsageStats.Builder(new String[0]).build();
} }
} }
final BatteryInfo batteryInfo = final BatteryInfo batteryInfo = getBatteryInfo(context, stats, shortString);
getBatteryInfo(context, stats, shortString);
if (shouldCloseBatteryUsageStats) { if (shouldCloseBatteryUsageStats) {
try { try {
stats.close(); stats.close();
@@ -196,23 +213,22 @@ public class BatteryInfo {
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
/** /** Creates a BatteryInfo based on BatteryUsageStats */
* Creates a BatteryInfo based on BatteryUsageStats
*/
@WorkerThread @WorkerThread
public static BatteryInfo getBatteryInfo(final Context context, public static BatteryInfo getBatteryInfo(
@NonNull final BatteryUsageStats batteryUsageStats, boolean shortString) { final Context context,
@NonNull final BatteryUsageStats batteryUsageStats,
boolean shortString) {
final long batteryStatsTime = System.currentTimeMillis(); final long batteryStatsTime = System.currentTimeMillis();
BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime); BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime);
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
PowerUsageFeatureProvider provider = PowerUsageFeatureProvider provider =
FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider();
final long elapsedRealtimeUs = final long elapsedRealtimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
final Intent batteryBroadcast = context.registerReceiver(null, final Intent batteryBroadcast =
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
// 0 means we are discharging, anything else means charging // 0 means we are discharging, anything else means charging
final boolean discharging = final boolean discharging =
batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0; batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;
@@ -221,40 +237,65 @@ public class BatteryInfo {
Estimate estimate = provider.getEnhancedBatteryPrediction(context); Estimate estimate = provider.getEnhancedBatteryPrediction(context);
if (estimate != null) { if (estimate != null) {
Estimate.storeCachedEstimate(context, estimate); Estimate.storeCachedEstimate(context, estimate);
BatteryUtils BatteryUtils.logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime);
.logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime); return BatteryInfo.getBatteryInfo(
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, batteryUsageStats, context,
estimate, elapsedRealtimeUs, shortString); batteryBroadcast,
batteryUsageStats,
estimate,
elapsedRealtimeUs,
shortString);
} }
} }
final long prediction = discharging ? batteryUsageStats.getBatteryTimeRemainingMs() : 0; final long prediction = discharging ? batteryUsageStats.getBatteryTimeRemainingMs() : 0;
final Estimate estimate = new Estimate( final Estimate estimate =
prediction, new Estimate(
false, /* isBasedOnUsage */ prediction,
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); false, /* isBasedOnUsage */
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime); BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime);
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, batteryUsageStats, return BatteryInfo.getBatteryInfo(
estimate, elapsedRealtimeUs, shortString); context,
batteryBroadcast,
batteryUsageStats,
estimate,
elapsedRealtimeUs,
shortString);
} }
@WorkerThread @WorkerThread
public static BatteryInfo getBatteryInfoOld(Context context, Intent batteryBroadcast, public static BatteryInfo getBatteryInfoOld(
BatteryUsageStats batteryUsageStats, long elapsedRealtimeUs, boolean shortString) { Context context,
Estimate estimate = new Estimate( Intent batteryBroadcast,
batteryUsageStats.getBatteryTimeRemainingMs(), BatteryUsageStats batteryUsageStats,
false, long elapsedRealtimeUs,
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); boolean shortString) {
return getBatteryInfo(context, batteryBroadcast, batteryUsageStats, estimate, Estimate estimate =
elapsedRealtimeUs, shortString); new Estimate(
batteryUsageStats.getBatteryTimeRemainingMs(),
false,
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
return getBatteryInfo(
context,
batteryBroadcast,
batteryUsageStats,
estimate,
elapsedRealtimeUs,
shortString);
} }
@WorkerThread @WorkerThread
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast, public static BatteryInfo getBatteryInfo(
@NonNull BatteryUsageStats batteryUsageStats, Estimate estimate, Context context,
long elapsedRealtimeUs, boolean shortString) { Intent batteryBroadcast,
@NonNull BatteryUsageStats batteryUsageStats,
Estimate estimate,
long elapsedRealtimeUs,
boolean shortString) {
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
final boolean isCompactStatus = context.getResources().getBoolean( final boolean isCompactStatus =
com.android.settings.R.bool.config_use_compact_battery_status); context.getResources()
.getBoolean(com.android.settings.R.bool.config_use_compact_battery_status);
BatteryInfo info = new BatteryInfo(); BatteryInfo info = new BatteryInfo();
info.mBatteryUsageStats = batteryUsageStats; info.mBatteryUsageStats = batteryUsageStats;
info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast); info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
@@ -262,25 +303,32 @@ public class BatteryInfo {
info.pluggedStatus = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); info.pluggedStatus = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
info.mCharging = info.pluggedStatus != 0; info.mCharging = info.pluggedStatus != 0;
info.averageTimeToDischarge = estimate.getAverageDischargeTime(); info.averageTimeToDischarge = estimate.getAverageDischargeTime();
info.isBatteryDefender = batteryBroadcast.getIntExtra( info.isBatteryDefender =
BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT) batteryBroadcast.getIntExtra(
== BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE; BatteryManager.EXTRA_CHARGING_STATUS,
BatteryManager.CHARGING_POLICY_DEFAULT)
== BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE;
info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus); info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus);
info.batteryStatus = batteryBroadcast.getIntExtra( info.batteryStatus =
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
if (!info.mCharging) { if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info); updateBatteryInfoDischarging(context, shortString, estimate, info);
} else { } else {
updateBatteryInfoCharging(context, batteryBroadcast, batteryUsageStats, updateBatteryInfoCharging(
info, isCompactStatus); context, batteryBroadcast, batteryUsageStats, info, isCompactStatus);
} }
BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime); BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
return info; return info;
} }
private static void updateBatteryInfoCharging(Context context, Intent batteryBroadcast, private static void updateBatteryInfoCharging(
BatteryUsageStats stats, BatteryInfo info, boolean compactStatus) { Context context,
Intent batteryBroadcast,
BatteryUsageStats stats,
BatteryInfo info,
boolean compactStatus) {
final Resources resources = context.getResources(); final Resources resources = context.getResources();
final long chargeTimeMs = stats.getChargeTimeRemainingMs(); final long chargeTimeMs = stats.getChargeTimeRemainingMs();
if (getSettingsChargeTimeRemaining(context) != chargeTimeMs) { if (getSettingsChargeTimeRemaining(context) != chargeTimeMs) {
@@ -290,59 +338,76 @@ public class BatteryInfo {
chargeTimeMs); chargeTimeMs);
} }
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS, final int status =
BatteryManager.BATTERY_STATUS_UNKNOWN); batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
info.discharging = false; info.discharging = false;
info.suggestionLabel = null; info.suggestionLabel = null;
int dockDefenderMode = BatteryUtils.getCurrentDockDefenderMode(context, info); int dockDefenderMode = BatteryUtils.getCurrentDockDefenderMode(context, info);
if ((info.isBatteryDefender && status != BatteryManager.BATTERY_STATUS_FULL if ((info.isBatteryDefender
&& dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED) && status != BatteryManager.BATTERY_STATUS_FULL
&& dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED)
|| dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) { || dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) {
// Battery defender active, battery charging paused // Battery defender active, battery charging paused
info.remainingLabel = null; info.remainingLabel = null;
int chargingLimitedResId = com.android.settingslib.R.string.power_charging_limited; int chargingLimitedResId = com.android.settingslib.R.string.power_charging_limited;
info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString); info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString);
} else if ((chargeTimeMs > 0 && status != BatteryManager.BATTERY_STATUS_FULL } else if ((chargeTimeMs > 0
&& dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED) && status != BatteryManager.BATTERY_STATUS_FULL
&& dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED)
|| dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) { || dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) {
// Battery is charging to full // Battery is charging to full
info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs); info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs);
final CharSequence timeString = StringUtil.formatElapsedTime(context, final CharSequence timeString =
(double) PowerUtil.convertUsToMs(info.remainingTimeUs), false /* withSeconds */, StringUtil.formatElapsedTime(
true /* collapseTimeUnit */); context,
(double) PowerUtil.convertUsToMs(info.remainingTimeUs),
false /* withSeconds */,
true /* collapseTimeUnit */);
int resId = com.android.settingslib.R.string.power_charging_duration; int resId = com.android.settingslib.R.string.power_charging_duration;
info.remainingLabel = chargeTimeMs <= 0 ? null : context.getString( info.remainingLabel =
com.android.settingslib.R.string.power_remaining_charging_duration_only, chargeTimeMs <= 0
timeString); ? null
info.chargeLabel = chargeTimeMs <= 0 ? info.batteryPercentString : context.getString(
: context.getString(resId, info.batteryPercentString, timeString); com.android.settingslib.R.string
.power_remaining_charging_duration_only,
timeString);
info.chargeLabel =
chargeTimeMs <= 0
? info.batteryPercentString
: context.getString(resId, info.batteryPercentString, timeString);
} else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) { } else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) {
// Dock defender will be triggered in the future, charging will be optimized. // Dock defender will be triggered in the future, charging will be optimized.
info.chargeLabel = context.getString( info.chargeLabel =
com.android.settingslib.R.string.power_charging_future_paused, context.getString(
info.batteryPercentString); com.android.settingslib.R.string.power_charging_future_paused,
info.batteryPercentString);
} else { } else {
final String chargeStatusLabel = Utils.getBatteryStatus(context, batteryBroadcast, final String chargeStatusLabel =
compactStatus); Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
info.remainingLabel = null; info.remainingLabel = null;
info.chargeLabel = info.batteryLevel == 100 ? info.batteryPercentString : info.chargeLabel =
resources.getString(com.android.settingslib.R.string.power_charging, info.batteryLevel == 100
info.batteryPercentString, chargeStatusLabel); ? info.batteryPercentString
: resources.getString(
com.android.settingslib.R.string.power_charging,
info.batteryPercentString,
chargeStatusLabel);
} }
} }
private static void updateBatteryInfoDischarging(Context context, boolean shortString, private static void updateBatteryInfoDischarging(
Estimate estimate, BatteryInfo info) { Context context, boolean shortString, Estimate estimate, BatteryInfo info) {
final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis()); final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis());
if (drainTimeUs > 0) { if (drainTimeUs > 0) {
info.remainingTimeUs = drainTimeUs; info.remainingTimeUs = drainTimeUs;
info.remainingLabel = PowerUtil.getBatteryRemainingShortStringFormatted( info.remainingLabel =
context, PowerUtil.getBatteryRemainingShortStringFormatted(
PowerUtil.convertUsToMs(drainTimeUs) context, PowerUtil.convertUsToMs(drainTimeUs));
);
info.chargeLabel = info.remainingLabel; info.chargeLabel = info.remainingLabel;
info.suggestionLabel = PowerUtil.getBatteryTipStringFormatted( info.suggestionLabel =
context, PowerUtil.convertUsToMs(drainTimeUs)); PowerUtil.getBatteryTipStringFormatted(
context, PowerUtil.convertUsToMs(drainTimeUs));
} else { } else {
info.remainingLabel = null; info.remainingLabel = null;
info.suggestionLabel = null; info.suggestionLabel = null;
@@ -361,8 +426,8 @@ public class BatteryInfo {
} }
/** /**
* Iterates over battery history included in the BatteryUsageStats that this object * Iterates over battery history included in the BatteryUsageStats that this object was
* was initialized with. * initialized with.
*/ */
public void parseBatteryHistory(BatteryDataParser... parsers) { public void parseBatteryHistory(BatteryDataParser... parsers) {
long startWalltime = 0; long startWalltime = 0;
@@ -384,8 +449,7 @@ public class BatteryInfo {
first = false; first = false;
historyStart = rec.time; historyStart = rec.time;
} }
if (rec.cmd == HistoryItem.CMD_CURRENT_TIME if (rec.cmd == HistoryItem.CMD_CURRENT_TIME || rec.cmd == HistoryItem.CMD_RESET) {
|| rec.cmd == HistoryItem.CMD_RESET) {
// If there is a ridiculously large jump in time, then we won't be // If there is a ridiculously large jump in time, then we won't be
// able to create a good chart with that data, so just ignore the // able to create a good chart with that data, so just ignore the
// times we got before and pretend like our data extends back from // times we got before and pretend like our data extends back from
@@ -447,7 +511,7 @@ public class BatteryInfo {
if (rec.cmd != HistoryItem.CMD_OVERFLOW if (rec.cmd != HistoryItem.CMD_OVERFLOW
&& (rec.cmd != HistoryItem.CMD_CURRENT_TIME && (rec.cmd != HistoryItem.CMD_CURRENT_TIME
|| Math.abs(lastWalltime - curWalltime) > (60 * 60 * 1000))) { || Math.abs(lastWalltime - curWalltime) > (60 * 60 * 1000))) {
for (int j = 0; j < parsers.length; j++) { for (int j = 0; j < parsers.length; j++) {
parsers[j].onDataGap(); parsers[j].onDataGap();
} }

View File

@@ -26,11 +26,10 @@ import com.android.settingslib.utils.AsyncLoaderCompat;
* automatically grab enhanced battery estimates if available or fall back to the system estimate * automatically grab enhanced battery estimates if available or fall back to the system estimate
* when not available. * when not available.
*/ */
public class BatteryInfoLoader extends AsyncLoaderCompat<BatteryInfo>{ public class BatteryInfoLoader extends AsyncLoaderCompat<BatteryInfo> {
private static final String LOG_TAG = "BatteryInfoLoader"; private static final String LOG_TAG = "BatteryInfoLoader";
@VisibleForTesting @VisibleForTesting BatteryUtils mBatteryUtils;
BatteryUtils mBatteryUtils;
public BatteryInfoLoader(Context context) { public BatteryInfoLoader(Context context) {
super(context); super(context);
@@ -38,9 +37,7 @@ public class BatteryInfoLoader extends AsyncLoaderCompat<BatteryInfo>{
} }
@Override @Override
protected void onDiscardResult(BatteryInfo result) { protected void onDiscardResult(BatteryInfo result) {}
}
@Override @Override
public BatteryInfo loadInBackground() { public BatteryInfo loadInBackground() {

View File

@@ -29,14 +29,10 @@ import com.android.settings.Utils;
import com.android.settingslib.graph.ThemedBatteryDrawable; import com.android.settingslib.graph.ThemedBatteryDrawable;
public class BatteryMeterView extends ImageView { public class BatteryMeterView extends ImageView {
@VisibleForTesting @VisibleForTesting BatteryMeterDrawable mDrawable;
BatteryMeterDrawable mDrawable; @VisibleForTesting ColorFilter mErrorColorFilter;
@VisibleForTesting @VisibleForTesting ColorFilter mAccentColorFilter;
ColorFilter mErrorColorFilter; @VisibleForTesting ColorFilter mForegroundColorFilter;
@VisibleForTesting
ColorFilter mAccentColorFilter;
@VisibleForTesting
ColorFilter mForegroundColorFilter;
public BatteryMeterView(Context context) { public BatteryMeterView(Context context) {
this(context, null, 0); this(context, null, 0);
@@ -51,12 +47,15 @@ public class BatteryMeterView extends ImageView {
final int frameColor = final int frameColor =
context.getColor(com.android.settingslib.R.color.meter_background_color); context.getColor(com.android.settingslib.R.color.meter_background_color);
mAccentColorFilter = Utils.getAlphaInvariantColorFilterForColor( mAccentColorFilter =
Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent)); Utils.getAlphaInvariantColorFilterForColor(
mErrorColorFilter = Utils.getAlphaInvariantColorFilterForColor( Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent));
context.getColor(R.color.battery_icon_color_error)); mErrorColorFilter =
mForegroundColorFilter = Utils.getAlphaInvariantColorFilterForColor( Utils.getAlphaInvariantColorFilterForColor(
Utils.getColorAttrDefaultColor(context, android.R.attr.colorForeground)); context.getColor(R.color.battery_icon_color_error));
mForegroundColorFilter =
Utils.getAlphaInvariantColorFilterForColor(
Utils.getColorAttrDefaultColor(context, android.R.attr.colorForeground));
mDrawable = new BatteryMeterDrawable(context, frameColor); mDrawable = new BatteryMeterDrawable(context, frameColor);
mDrawable.setColorFilter(mAccentColorFilter); mDrawable.setColorFilter(mAccentColorFilter);
setImageDrawable(mDrawable); setImageDrawable(mDrawable);
@@ -108,10 +107,10 @@ public class BatteryMeterView extends ImageView {
public BatteryMeterDrawable(Context context, int frameColor) { public BatteryMeterDrawable(Context context, int frameColor) {
super(context, frameColor); super(context, frameColor);
mIntrinsicWidth = context.getResources() mIntrinsicWidth =
.getDimensionPixelSize(R.dimen.battery_meter_width); context.getResources().getDimensionPixelSize(R.dimen.battery_meter_width);
mIntrinsicHeight = context.getResources() mIntrinsicHeight =
.getDimensionPixelSize(R.dimen.battery_meter_height); context.getResources().getDimensionPixelSize(R.dimen.battery_meter_height);
} }
public BatteryMeterDrawable(Context context, int frameColor, int width, int height) { public BatteryMeterDrawable(Context context, int frameColor, int width, int height) {

View File

@@ -34,8 +34,7 @@ public final class 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";
@VisibleForTesting @VisibleForTesting static final int MAX_ENTRIES = 40;
static final int MAX_ENTRIES = 40;
private BatteryOptimizeLogUtils() {} private BatteryOptimizeLogUtils() {}
@@ -45,8 +44,11 @@ public final class BatteryOptimizeLogUtils {
writeLog(getSharedPreferences(context), action, packageName, actionDescription); writeLog(getSharedPreferences(context), action, packageName, actionDescription);
} }
static void writeLog(SharedPreferences sharedPreferences, Action action, static void writeLog(
String packageName, String actionDescription) { SharedPreferences sharedPreferences,
Action action,
String packageName,
String actionDescription) {
writeLog( writeLog(
sharedPreferences, sharedPreferences,
BatteryOptimizeHistoricalLogEntry.newBuilder() BatteryOptimizeHistoricalLogEntry.newBuilder()
@@ -70,10 +72,7 @@ public final class BatteryOptimizeLogUtils {
String loggingContent = String loggingContent =
Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT); Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT);
sharedPreferences sharedPreferences.edit().putString(LOGS_KEY, loggingContent).apply();
.edit()
.putString(LOGS_KEY, loggingContent)
.apply();
} }
private static BatteryOptimizeHistoricalLog parseLogFromString(String storedLogs) { private static BatteryOptimizeHistoricalLog parseLogFromString(String storedLogs) {
@@ -107,9 +106,11 @@ public final class BatteryOptimizeLogUtils {
} }
private static String toString(BatteryOptimizeHistoricalLogEntry entry) { private static String toString(BatteryOptimizeHistoricalLogEntry entry) {
return String.format("%s\t%s\taction:%s\tevent:%s", return String.format(
"%s\t%s\taction:%s\tevent:%s",
ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()), ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()),
entry.getPackageName(), entry.getAction(), entry.getPackageName(),
entry.getAction(),
entry.getActionDescription()); entry.getActionDescription());
} }

View File

@@ -62,11 +62,11 @@ public class BatteryOptimizeUtils {
// If current user is admin, match apps from all users. Otherwise, only match the currect user. // If current user is admin, match apps from all users. Otherwise, only match the currect user.
private static final int RETRIEVE_FLAG_ADMIN = private static final int RETRIEVE_FLAG_ADMIN =
PackageManager.MATCH_ANY_USER PackageManager.MATCH_ANY_USER
| PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
private static final int RETRIEVE_FLAG = private static final int RETRIEVE_FLAG =
PackageManager.MATCH_DISABLED_COMPONENTS PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
// Optimization modes. // Optimization modes.
static final int MODE_UNKNOWN = 0; static final int MODE_UNKNOWN = 0;
@@ -74,12 +74,14 @@ public class BatteryOptimizeUtils {
static final int MODE_UNRESTRICTED = 2; static final int MODE_UNRESTRICTED = 2;
static final int MODE_OPTIMIZED = 3; static final int MODE_OPTIMIZED = 3;
@IntDef(prefix = {"MODE_"}, value = { @IntDef(
MODE_UNKNOWN, prefix = {"MODE_"},
MODE_RESTRICTED, value = {
MODE_UNRESTRICTED, MODE_UNKNOWN,
MODE_OPTIMIZED, MODE_RESTRICTED,
}) MODE_UNRESTRICTED,
MODE_OPTIMIZED,
})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
static @interface OptimizationMode {} static @interface OptimizationMode {}
@@ -118,8 +120,12 @@ public class BatteryOptimizeUtils {
/** Resets optimization mode for all applications. */ /** Resets optimization mode for all applications. */
public static void resetAppOptimizationMode( public static void resetAppOptimizationMode(
Context context, IPackageManager ipm, AppOpsManager aom) { Context context, IPackageManager ipm, AppOpsManager aom) {
resetAppOptimizationMode(context, ipm, aom, resetAppOptimizationMode(
PowerAllowlistBackend.getInstance(context), BatteryUtils.getInstance(context)); context,
ipm,
aom,
PowerAllowlistBackend.getInstance(context),
BatteryUtils.getInstance(context));
} }
/** Sets the {@link OptimizationMode} for associated app. */ /** Sets the {@link OptimizationMode} for associated app. */
@@ -138,17 +144,13 @@ public class BatteryOptimizeUtils {
|| mBatteryUtils.getPackageUid(mPackageName) == BatteryUtils.UID_NULL; || mBatteryUtils.getPackageUid(mPackageName) == BatteryUtils.UID_NULL;
} }
/** /** Return {@code true} if this package is system or default active app. */
* Return {@code true} if this package is system or default active app.
*/
public boolean isSystemOrDefaultApp() { public boolean isSystemOrDefaultApp() {
mPowerAllowListBackend.refreshList(); mPowerAllowListBackend.refreshList();
return isSystemOrDefaultApp(mContext, mPowerAllowListBackend, mPackageName, mUid); return isSystemOrDefaultApp(mContext, mPowerAllowListBackend, mPackageName, mUid);
} }
/** /** Return {@code true} if the optimization mode of this package can be changed */
* Return {@code true} if the optimization mode of this package can be changed
*/
public boolean isOptimizeModeMutable() { public boolean isOptimizeModeMutable() {
return !isDisabledForOptimizeModeOnly() && !isSystemOrDefaultApp(); return !isDisabledForOptimizeModeOnly() && !isSystemOrDefaultApp();
} }
@@ -162,9 +164,7 @@ public class BatteryOptimizeUtils {
&& getAppOptimizationMode() != BatteryOptimizeUtils.MODE_RESTRICTED; && getAppOptimizationMode() != BatteryOptimizeUtils.MODE_RESTRICTED;
} }
/** /** Gets the list of installed applications. */
* Gets the list of installed applications.
*/
public static ArraySet<ApplicationInfo> getInstalledApplications( public static ArraySet<ApplicationInfo> getInstalledApplications(
Context context, IPackageManager ipm) { Context context, IPackageManager ipm) {
final ArraySet<ApplicationInfo> applications = new ArraySet<>(); final ArraySet<ApplicationInfo> applications = new ArraySet<>();
@@ -172,9 +172,10 @@ public class BatteryOptimizeUtils {
for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) { for (UserInfo userInfo : um.getProfiles(UserHandle.myUserId())) {
try { try {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final ParceledListSlice<ApplicationInfo> infoList = ipm.getInstalledApplications( final ParceledListSlice<ApplicationInfo> infoList =
userInfo.isAdmin() ? RETRIEVE_FLAG_ADMIN : RETRIEVE_FLAG, ipm.getInstalledApplications(
userInfo.id); userInfo.isAdmin() ? RETRIEVE_FLAG_ADMIN : RETRIEVE_FLAG,
userInfo.id);
if (infoList != null) { if (infoList != null) {
applications.addAll(infoList.getList()); applications.addAll(infoList.getList());
} }
@@ -185,15 +186,19 @@ public class BatteryOptimizeUtils {
} }
// Removes the application which is disabled by the system. // Removes the application which is disabled by the system.
applications.removeIf( applications.removeIf(
info -> info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER info ->
&& !info.enabled); info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
&& !info.enabled);
return applications; return applications;
} }
@VisibleForTesting @VisibleForTesting
static void resetAppOptimizationMode( static void resetAppOptimizationMode(
Context context, IPackageManager ipm, AppOpsManager aom, Context context,
PowerAllowlistBackend allowlistBackend, BatteryUtils batteryUtils) { IPackageManager ipm,
AppOpsManager aom,
PowerAllowlistBackend allowlistBackend,
BatteryUtils batteryUtils) {
final ArraySet<ApplicationInfo> applications = getInstalledApplications(context, ipm); final ArraySet<ApplicationInfo> applications = getInstalledApplications(context, ipm);
if (applications == null || applications.isEmpty()) { if (applications == null || applications.isEmpty()) {
Log.w(TAG, "no data found in the getInstalledApplications()"); Log.w(TAG, "no data found in the getInstalledApplications()");
@@ -203,11 +208,13 @@ public class BatteryOptimizeUtils {
allowlistBackend.refreshList(); allowlistBackend.refreshList();
// Resets optimization mode for each application. // Resets optimization mode for each application.
for (ApplicationInfo info : applications) { for (ApplicationInfo info : applications) {
final int mode = aom.checkOpNoThrow( final int mode =
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, info.uid, info.packageName); aom.checkOpNoThrow(
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, info.uid, info.packageName);
@OptimizationMode @OptimizationMode
final int optimizationMode = getAppOptimizationMode( final int optimizationMode =
mode, allowlistBackend.isAllowlisted(info.packageName, info.uid)); getAppOptimizationMode(
mode, allowlistBackend.isAllowlisted(info.packageName, info.uid));
// Ignores default optimized/unknown state or system/default apps. // Ignores default optimized/unknown state or system/default apps.
if (optimizationMode == MODE_OPTIMIZED if (optimizationMode == MODE_OPTIMIZED
|| optimizationMode == MODE_UNKNOWN || optimizationMode == MODE_UNKNOWN
@@ -217,8 +224,14 @@ public class BatteryOptimizeUtils {
} }
// Resets to the default mode: MODE_OPTIMIZED. // Resets to the default mode: MODE_OPTIMIZED.
setAppUsageStateInternal(context, MODE_OPTIMIZED, info.uid, info.packageName, setAppUsageStateInternal(
batteryUtils, allowlistBackend, Action.RESET); context,
MODE_OPTIMIZED,
info.uid,
info.packageName,
batteryUtils,
allowlistBackend,
Action.RESET);
} }
} }
@@ -244,25 +257,33 @@ public class BatteryOptimizeUtils {
static List<String> getForceBatteryOptimizeModeList(Context context) { static List<String> getForceBatteryOptimizeModeList(Context context) {
if (sBatteryOptimizeModeList == null) { if (sBatteryOptimizeModeList == null) {
sBatteryOptimizeModeList = Arrays.asList( sBatteryOptimizeModeList =
context.getResources().getStringArray( Arrays.asList(
R.array.config_force_battery_optimize_mode_apps)); context.getResources()
.getStringArray(
R.array.config_force_battery_optimize_mode_apps));
} }
return sBatteryOptimizeModeList; return sBatteryOptimizeModeList;
} }
static List<String> getForceBatteryUnrestrictModeList(Context context) { static List<String> getForceBatteryUnrestrictModeList(Context context) {
if (sBatteryUnrestrictModeList == null) { if (sBatteryUnrestrictModeList == null) {
sBatteryUnrestrictModeList = Arrays.asList( sBatteryUnrestrictModeList =
context.getResources().getStringArray( Arrays.asList(
R.array.config_force_battery_unrestrict_mode_apps)); context.getResources()
.getStringArray(
R.array.config_force_battery_unrestrict_mode_apps));
} }
return sBatteryUnrestrictModeList; return sBatteryUnrestrictModeList;
} }
private static void setAppUsageStateInternal( private static void setAppUsageStateInternal(
Context context, @OptimizationMode int mode, int uid, String packageName, Context context,
BatteryUtils batteryUtils, PowerAllowlistBackend powerAllowlistBackend, @OptimizationMode int mode,
int uid,
String packageName,
BatteryUtils batteryUtils,
PowerAllowlistBackend powerAllowlistBackend,
Action action) { Action action) {
if (mode == MODE_UNKNOWN) { if (mode == MODE_UNKNOWN) {
Log.d(TAG, "set unknown app optimization mode."); Log.d(TAG, "set unknown app optimization mode.");
@@ -276,16 +297,29 @@ public class BatteryOptimizeUtils {
mode == MODE_RESTRICTED ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED; mode == MODE_RESTRICTED ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
final boolean allowListed = mode == MODE_UNRESTRICTED; final boolean allowListed = mode == MODE_UNRESTRICTED;
setAppOptimizationModeInternal(context, appOpsManagerMode, allowListed, uid, setAppOptimizationModeInternal(
packageName, batteryUtils, powerAllowlistBackend, action); context,
appOpsManagerMode,
allowListed,
uid,
packageName,
batteryUtils,
powerAllowlistBackend,
action);
} }
private static void setAppOptimizationModeInternal( private static void setAppOptimizationModeInternal(
Context context, int appStandbyMode, boolean allowListed, int uid, String packageName, Context context,
BatteryUtils batteryUtils, PowerAllowlistBackend powerAllowlistBackend, int appStandbyMode,
boolean allowListed,
int uid,
String packageName,
BatteryUtils batteryUtils,
PowerAllowlistBackend powerAllowlistBackend,
Action action) { Action action) {
final String packageNameKey = BatteryOptimizeLogUtils final String packageNameKey =
.getPackageNameWithUserId(packageName, UserHandle.myUserId()); BatteryOptimizeLogUtils.getPackageNameWithUserId(
packageName, UserHandle.myUserId());
try { try {
batteryUtils.setForceAppStandby(uid, packageName, appStandbyMode); batteryUtils.setForceAppStandby(uid, packageName, appStandbyMode);
if (allowListed) { if (allowListed) {
@@ -299,24 +333,27 @@ public class BatteryOptimizeUtils {
Log.e(TAG, "set OPTIMIZATION MODE failed for " + packageName, e); Log.e(TAG, "set OPTIMIZATION MODE failed for " + packageName, e);
} }
BatteryOptimizeLogUtils.writeLog( BatteryOptimizeLogUtils.writeLog(
context, context, action, packageNameKey, createLogEvent(appStandbyMode, allowListed));
action,
packageNameKey,
createLogEvent(appStandbyMode, allowListed));
} }
private void refreshState() { private void refreshState() {
mPowerAllowListBackend.refreshList(); mPowerAllowListBackend.refreshList();
mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName, mUid); mAllowListed = mPowerAllowListBackend.isAllowlisted(mPackageName, mUid);
mMode = mAppOpsManager mMode =
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName); mAppOpsManager.checkOpNoThrow(
Log.d(TAG, String.format("refresh %s state, allowlisted = %s, mode = %d", AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mPackageName);
mPackageName, mAllowListed, mMode)); Log.d(
TAG,
String.format(
"refresh %s state, allowlisted = %s, mode = %d",
mPackageName, mAllowListed, mMode));
} }
private static String createLogEvent(int appStandbyMode, boolean allowListed) { private static String createLogEvent(int appStandbyMode, boolean allowListed) {
return appStandbyMode < 0 ? "Apply optimize setting ERROR" : return appStandbyMode < 0
String.format("\tStandbyMode: %s, allowListed: %s, mode: %s", ? "Apply optimize setting ERROR"
: String.format(
"\tStandbyMode: %s, allowListed: %s, mode: %s",
appStandbyMode, appStandbyMode,
allowListed, allowListed,
getAppOptimizationMode(appStandbyMode, allowListed)); getAppOptimizationMode(appStandbyMode, allowListed));

View File

@@ -16,15 +16,12 @@
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
/** /** Common interface for a preference controller that updates battery status */
* Common interface for a preference controller that updates battery status
*/
public interface BatteryPreferenceController { public interface BatteryPreferenceController {
/** /**
* Updates the label for the preference controller. If the label is null, the * Updates the label for the preference controller. If the label is null, the implementation
* implementation should revert back to the original label based on the * should revert back to the original label based on the battery info.
* battery info.
*/ */
void updateBatteryStatus(String label, BatteryInfo info); void updateBatteryStatus(String label, BatteryInfo info);
} }

View File

@@ -35,22 +35,20 @@ import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.BatterySaverUtils;
/** /** Controller to update the battery saver entry preference. */
* Controller to update the battery saver entry preference.
*/
public class BatterySaverController extends BasePreferenceController public class BatterySaverController extends BasePreferenceController
implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener { implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
private static final String KEY_BATTERY_SAVER = "battery_saver_summary"; private static final String KEY_BATTERY_SAVER = "battery_saver_summary";
private final BatterySaverReceiver mBatteryStateChangeReceiver; private final BatterySaverReceiver mBatteryStateChangeReceiver;
private final PowerManager mPowerManager; private final PowerManager mPowerManager;
private Preference mBatterySaverPref; private Preference mBatterySaverPref;
private final ContentObserver mObserver = new ContentObserver( private final ContentObserver mObserver =
new Handler(Looper.getMainLooper())) { new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override @Override
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
updateSummary(); updateSummary();
} }
}; };
public BatterySaverController(Context context) { public BatterySaverController(Context context) {
super(context, KEY_BATTERY_SAVER); super(context, KEY_BATTERY_SAVER);
@@ -79,9 +77,11 @@ public class BatterySaverController extends BasePreferenceController
@Override @Override
public void onStart() { public void onStart() {
mContext.getContentResolver().registerContentObserver( mContext.getContentResolver()
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), .registerContentObserver(
true /* notifyForDescendants */, mObserver); Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
true /* notifyForDescendants */,
mObserver);
mBatteryStateChangeReceiver.setListening(true); mBatteryStateChangeReceiver.setListening(true);
updateSummary(); updateSummary();
@@ -101,15 +101,20 @@ public class BatterySaverController extends BasePreferenceController
} }
final ContentResolver resolver = mContext.getContentResolver(); final ContentResolver resolver = mContext.getContentResolver();
final int mode = Settings.Global.getInt(resolver, final int mode =
Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); Settings.Global.getInt(
resolver,
Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) { if (mode == PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE) {
final int percent = Settings.Global.getInt(resolver, final int percent =
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); Settings.Global.getInt(
return percent != 0 ? resolver, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
mContext.getString(R.string.battery_saver_off_scheduled_summary, return percent != 0
Utils.formatPercentage(percent)) : ? mContext.getString(
mContext.getString(R.string.battery_saver_off_summary); R.string.battery_saver_off_scheduled_summary,
Utils.formatPercentage(percent))
: mContext.getString(R.string.battery_saver_off_summary);
} else { } else {
return mContext.getString(R.string.battery_saver_pref_auto_routine_summary); return mContext.getString(R.string.battery_saver_pref_auto_routine_summary);
} }
@@ -127,6 +132,5 @@ public class BatterySaverController extends BasePreferenceController
} }
@Override @Override
public void onBatteryChanged(boolean pluggedIn) { public void onBatteryChanged(boolean pluggedIn) {}
}
} }

View File

@@ -23,9 +23,7 @@ import android.graphics.PorterDuffColorFilter;
import com.android.settingslib.Utils; import com.android.settingslib.Utils;
import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.settingslib.graph.BatteryMeterDrawableBase;
/** /** Drawable that shows a static battery saver icon - a full battery symbol and a plus sign. */
* Drawable that shows a static battery saver icon - a full battery symbol and a plus sign.
*/
public class BatterySaverDrawable extends BatteryMeterDrawableBase { public class BatterySaverDrawable extends BatteryMeterDrawableBase {
private static final int MAX_BATTERY = 100; private static final int MAX_BATTERY = 100;

View File

@@ -27,8 +27,8 @@ import com.android.settings.utils.VoiceSettingsActivity;
import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.BatterySaverUtils;
/** /**
* Activity for modifying the {@link android.os.PowerManager} power save mode * Activity for modifying the {@link android.os.PowerManager} power save mode setting using the
* setting using the Voice Interaction API. * Voice Interaction API.
*/ */
public class BatterySaverModeVoiceActivity extends VoiceSettingsActivity { public class BatterySaverModeVoiceActivity extends VoiceSettingsActivity {
private static final String TAG = "BatterySaverModeVoiceActivity"; private static final String TAG = "BatterySaverModeVoiceActivity";
@@ -36,9 +36,11 @@ public class BatterySaverModeVoiceActivity extends VoiceSettingsActivity {
@Override @Override
protected boolean onVoiceSettingInteraction(Intent intent) { protected boolean onVoiceSettingInteraction(Intent intent) {
if (intent.hasExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED)) { if (intent.hasExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED)) {
if (BatterySaverUtils.setPowerSaveMode(this, if (BatterySaverUtils.setPowerSaveMode(
this,
intent.getBooleanExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED, false), intent.getBooleanExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED, false),
/*needFirstTimeWarning=*/ true, SAVER_ENABLED_VOICE)) { /* needFirstTimeWarning= */ true,
SAVER_ENABLED_VOICE)) {
notifySuccess(null); notifySuccess(null);
} else { } else {
Log.v(TAG, "Unable to set power mode"); Log.v(TAG, "Unable to set power mode");

View File

@@ -71,6 +71,7 @@ public class BatterySaverReceiver extends BroadcastReceiver {
public interface BatterySaverListener { public interface BatterySaverListener {
void onPowerSaveModeChanged(); void onPowerSaveModeChanged();
void onBatteryChanged(boolean pluggedIn); void onBatteryChanged(boolean pluggedIn);
} }
} }

View File

@@ -34,8 +34,7 @@ import java.util.List;
public final class BatterySettingsMigrateChecker extends BroadcastReceiver { public final class BatterySettingsMigrateChecker extends BroadcastReceiver {
private static final String TAG = "BatterySettingsMigrateChecker"; private static final String TAG = "BatterySettingsMigrateChecker";
@VisibleForTesting @VisibleForTesting static BatteryOptimizeUtils sBatteryOptimizeUtils = null;
static BatteryOptimizeUtils sBatteryOptimizeUtils = null;
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@@ -71,31 +70,38 @@ public final class BatterySettingsMigrateChecker extends BroadcastReceiver {
Context context, Context context,
@BatteryOptimizeUtils.OptimizationMode int optimizationMode, @BatteryOptimizeUtils.OptimizationMode int optimizationMode,
List<String> allowList) { List<String> allowList) {
allowList.forEach(packageName -> { allowList.forEach(
final BatteryOptimizeUtils batteryOptimizeUtils = packageName -> {
BatteryBackupHelper.newBatteryOptimizeUtils(context, packageName, final BatteryOptimizeUtils batteryOptimizeUtils =
/* testOptimizeUtils */ sBatteryOptimizeUtils); BatteryBackupHelper.newBatteryOptimizeUtils(
if (batteryOptimizeUtils == null) { context,
return; packageName,
} /* testOptimizeUtils */ sBatteryOptimizeUtils);
if (batteryOptimizeUtils.getAppOptimizationMode() != optimizationMode) { if (batteryOptimizeUtils == null) {
Log.w(TAG, "Reset " + packageName + " battery mode into " + optimizationMode); return;
batteryOptimizeUtils.setAppUsageState( }
optimizationMode, if (batteryOptimizeUtils.getAppOptimizationMode() != optimizationMode) {
BatteryOptimizeHistoricalLogEntry.Action.FORCE_RESET); Log.w(
} TAG,
}); "Reset " + packageName + " battery mode into " + optimizationMode);
batteryOptimizeUtils.setAppUsageState(
optimizationMode,
BatteryOptimizeHistoricalLogEntry.Action.FORCE_RESET);
}
});
} }
static void verifySaverConfiguration(Context context) { static void verifySaverConfiguration(Context context) {
Log.d(TAG, "invoke verifySaverConfiguration()"); Log.d(TAG, "invoke verifySaverConfiguration()");
final ContentResolver resolver = context.getContentResolver(); final ContentResolver resolver = context.getContentResolver();
final int threshold = Settings.Global.getInt(resolver, final int threshold =
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
// Force refine the invalid scheduled battery level. // Force refine the invalid scheduled battery level.
if (threshold < BatterySaverScheduleRadioButtonsController.TRIGGER_LEVEL_MIN if (threshold < BatterySaverScheduleRadioButtonsController.TRIGGER_LEVEL_MIN
&& threshold > 0) { && threshold > 0) {
Settings.Global.putInt(resolver, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, Settings.Global.putInt(
resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL,
BatterySaverScheduleRadioButtonsController.TRIGGER_LEVEL_MIN); BatterySaverScheduleRadioButtonsController.TRIGGER_LEVEL_MIN);
Log.w(TAG, "Reset invalid scheduled battery level from: " + threshold); Log.w(TAG, "Reset invalid scheduled battery level from: " + threshold);
} }

View File

@@ -16,14 +16,9 @@
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
/** /** Feature Provider used to retrieve battery status */
* Feature Provider used to retrieve battery status
*/
public interface BatteryStatusFeatureProvider { public interface BatteryStatusFeatureProvider {
/** /** Trigger a battery status update; return false if built-in status should be used. */
* Trigger a battery status update; return false if built-in status should be used. boolean triggerBatteryStatusUpdate(BatteryPreferenceController controller, BatteryInfo info);
*/
boolean triggerBatteryStatusUpdate(
BatteryPreferenceController controller, BatteryInfo info);
} }

View File

@@ -18,9 +18,7 @@ package com.android.settings.fuelgauge;
import android.content.Context; import android.content.Context;
/** /** Used to override battery status string in Battery Header. */
* Used to override battery status string in Battery Header.
*/
public class BatteryStatusFeatureProviderImpl implements BatteryStatusFeatureProvider { public class BatteryStatusFeatureProviderImpl implements BatteryStatusFeatureProvider {
protected Context mContext; protected Context mContext;

View File

@@ -70,15 +70,15 @@ import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle; import java.time.format.FormatStyle;
import java.util.List; import java.util.List;
/** /** Utils for battery operation */
* Utils for battery operation
*/
public class BatteryUtils { public class BatteryUtils {
public static final int UID_ZERO = 0; public static final int UID_ZERO = 0;
public static final int UID_NULL = -1; public static final int UID_NULL = -1;
public static final int SDK_NULL = -1; public static final int SDK_NULL = -1;
/** Special UID value for data usage by removed apps. */ /** Special UID value for data usage by removed apps. */
public static final int UID_REMOVED_APPS = -4; public static final int UID_REMOVED_APPS = -4;
/** Special UID value for data usage by tethering. */ /** Special UID value for data usage by tethering. */
public static final int UID_TETHERING = -5; public static final int UID_TETHERING = -5;
@@ -91,11 +91,7 @@ public class BatteryUtils {
private static final String PACKAGE_NAME_NONE = "none"; private static final String PACKAGE_NAME_NONE = "none";
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({StatusType.SCREEN_USAGE, @IntDef({StatusType.SCREEN_USAGE, StatusType.FOREGROUND, StatusType.BACKGROUND, StatusType.ALL})
StatusType.FOREGROUND,
StatusType.BACKGROUND,
StatusType.ALL
})
public @interface StatusType { public @interface StatusType {
int SCREEN_USAGE = 0; int SCREEN_USAGE = 0;
int FOREGROUND = 1; int FOREGROUND = 1;
@@ -104,10 +100,12 @@ public class BatteryUtils {
} }
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({DockDefenderMode.FUTURE_BYPASS, @IntDef({
DockDefenderMode.ACTIVE, DockDefenderMode.FUTURE_BYPASS,
DockDefenderMode.TEMPORARILY_BYPASSED, DockDefenderMode.ACTIVE,
DockDefenderMode.DISABLED}) DockDefenderMode.TEMPORARILY_BYPASSED,
DockDefenderMode.DISABLED
})
public @interface DockDefenderMode { public @interface DockDefenderMode {
int FUTURE_BYPASS = 0; int FUTURE_BYPASS = 0;
int ACTIVE = 1; int ACTIVE = 1;
@@ -122,8 +120,7 @@ public class BatteryUtils {
private AppOpsManager mAppOpsManager; private AppOpsManager mAppOpsManager;
private Context mContext; private Context mContext;
@VisibleForTesting @VisibleForTesting PowerUsageFeatureProvider mPowerUsageFeatureProvider;
PowerUsageFeatureProvider mPowerUsageFeatureProvider;
public static BatteryUtils getInstance(Context context) { public static BatteryUtils getInstance(Context context) {
if (sInstance == null || sInstance.isDataCorrupted()) { if (sInstance == null || sInstance.isDataCorrupted()) {
@@ -147,8 +144,9 @@ public class BatteryUtils {
sInstance = null; sInstance = null;
} }
public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid,
int which) { /** Gets the process time */
public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid, int which) {
if (uid == null) { if (uid == null) {
return 0; return 0;
} }
@@ -192,8 +190,9 @@ public class BatteryUtils {
private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) { private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, int which) {
final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime()); final long rawRealTimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
final long timeUs = uid.getProcessStateTime( final long timeUs =
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which); uid.getProcessStateTime(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, rawRealTimeUs, which);
Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid())); Log.v(TAG, "package: " + mPackageManager.getNameForUid(uid.getUid()));
Log.v(TAG, "background time(us): " + timeUs); Log.v(TAG, "background time(us): " + timeUs);
@@ -207,17 +206,17 @@ public class BatteryUtils {
} }
/** /**
* Returns true if the specified battery consumer should be excluded from the summary * Returns true if the specified battery consumer should be excluded from the summary battery
* battery consumption list. * consumption list.
*/ */
public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer) { public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer) {
return shouldHideUidBatteryConsumer(consumer, return shouldHideUidBatteryConsumer(
mPackageManager.getPackagesForUid(consumer.getUid())); consumer, mPackageManager.getPackagesForUid(consumer.getUid()));
} }
/** /**
* Returns true if the specified battery consumer should be excluded from the summary * Returns true if the specified battery consumer should be excluded from the summary battery
* battery consumption list. * consumption list.
*/ */
public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer, String[] packages) { public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer, String[] packages) {
return mPowerUsageFeatureProvider.isTypeSystem(consumer.getUid(), packages) return mPowerUsageFeatureProvider.isTypeSystem(consumer.getUid(), packages)
@@ -225,20 +224,16 @@ public class BatteryUtils {
} }
/** /**
* Returns true if the specified battery consumer should be excluded from * Returns true if the specified battery consumer should be excluded from battery consumption
* battery consumption lists, either short or full. * lists, either short or full.
*/ */
public boolean shouldHideUidBatteryConsumerUnconditionally(UidBatteryConsumer consumer, public boolean shouldHideUidBatteryConsumerUnconditionally(
String[] packages) { UidBatteryConsumer consumer, String[] packages) {
final int uid = consumer.getUid(); final int uid = consumer.getUid();
return uid == UID_TETHERING return uid == UID_TETHERING ? false : uid < 0 || isHiddenSystemModule(packages);
? false
: uid < 0 || isHiddenSystemModule(packages);
} }
/** /** Returns true if one the specified packages belongs to a hidden system module. */
* Returns true if one the specified packages belongs to a hidden system module.
*/
public boolean isHiddenSystemModule(String[] packages) { public boolean isHiddenSystemModule(String[] packages) {
if (packages != null) { if (packages != null) {
for (int i = 0, length = packages.length; i < length; i++) { for (int i = 0, length = packages.length; i < length; i++) {
@@ -253,14 +248,14 @@ public class BatteryUtils {
/** /**
* Calculate the power usage percentage for an app * Calculate the power usage percentage for an app
* *
* @param powerUsageMah power used by the app * @param powerUsageMah power used by the app
* @param totalPowerMah total power used in the system * @param totalPowerMah total power used in the system
* @param dischargeAmount The discharge amount calculated by {@link BatteryStats} * @param dischargeAmount The discharge amount calculated by {@link BatteryStats}
* @return A percentage value scaled by {@paramref dischargeAmount} * @return A percentage value scaled by {@paramref dischargeAmount}
* @see BatteryStats#getDischargeAmount(int) * @see BatteryStats#getDischargeAmount(int)
*/ */
public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah, public double calculateBatteryPercent(
int dischargeAmount) { double powerUsageMah, double totalPowerMah, int dischargeAmount) {
if (totalPowerMah == 0) { if (totalPowerMah == 0) {
return 0; return 0;
} }
@@ -272,9 +267,8 @@ public class BatteryUtils {
* Find the package name for a {@link android.os.BatteryStats.Uid} * Find the package name for a {@link android.os.BatteryStats.Uid}
* *
* @param uid id to get the package name * @param uid id to get the package name
* @return the package name. If there are multiple packages related to * @return the package name. If there are multiple packages related to given id, return the
* given id, return the first one. Or return null if there are no known * first one. Or return null if there are no known packages with the given id
* packages with the given id
* @see PackageManager#getPackagesForUid(int) * @see PackageManager#getPackagesForUid(int)
*/ */
public String getPackageName(int uid) { public String getPackageName(int uid) {
@@ -290,8 +284,8 @@ public class BatteryUtils {
*/ */
public int getTargetSdkVersion(final String packageName) { public int getTargetSdkVersion(final String packageName) {
try { try {
ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, ApplicationInfo info =
PackageManager.GET_META_DATA); mPackageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
return info.targetSdkVersion; return info.targetSdkVersion;
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
@@ -301,28 +295,26 @@ public class BatteryUtils {
return SDK_NULL; return SDK_NULL;
} }
/** /** Check whether background restriction is enabled */
* Check whether background restriction is enabled public boolean isBackgroundRestrictionEnabled(
*/ final int targetSdkVersion, final int uid, final String packageName) {
public boolean isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid,
final String packageName) {
if (targetSdkVersion >= Build.VERSION_CODES.O) { if (targetSdkVersion >= Build.VERSION_CODES.O) {
return true; return true;
} }
final int mode = mAppOpsManager final int mode =
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName); mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName);
return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED; return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED;
} }
/** /**
* Calculate the time since last full charge, including the device off time * Calculate the time since last full charge, including the device off time
* *
* @param batteryUsageStats class that contains the data * @param batteryUsageStats class that contains the data
* @param currentTimeMs current wall time * @param currentTimeMs current wall time
* @return time in millis * @return time in millis
*/ */
public long calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats, public long calculateLastFullChargeTime(
long currentTimeMs) { BatteryUsageStats batteryUsageStats, long currentTimeMs) {
return currentTimeMs - batteryUsageStats.getStatsStartTimestamp(); return currentTimeMs - batteryUsageStats.getStatsStartTimestamp();
} }
@@ -330,9 +322,7 @@ public class BatteryUtils {
Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms"); Log.d(tag, message + ": " + (System.currentTimeMillis() - startTime) + "ms");
} }
/** /** Return {@code true} if battery defender is on and charging. */
* Return {@code true} if battery defender is on and charging.
*/
public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) { public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) {
return batteryInfo.isBatteryDefender && !batteryInfo.discharging; return batteryInfo.isBatteryDefender && !batteryInfo.discharging;
} }
@@ -341,13 +331,14 @@ public class BatteryUtils {
* Find package uid from package name * Find package uid from package name
* *
* @param packageName used to find the uid * @param packageName used to find the uid
* @return uid for packageName, or {@link #UID_NULL} if exception happens or * @return uid for packageName, or {@link #UID_NULL} if exception happens or {@code packageName}
* {@code packageName} is null * is null
*/ */
public int getPackageUid(String packageName) { public int getPackageUid(String packageName) {
try { try {
return packageName == null ? UID_NULL : mPackageManager.getPackageUid(packageName, return packageName == null
PackageManager.GET_META_DATA); ? UID_NULL
: mPackageManager.getPackageUid(packageName, PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
return UID_NULL; return UID_NULL;
} }
@@ -367,16 +358,18 @@ public class BatteryUtils {
return (T) protoClass.getDefaultInstanceForType(); return (T) protoClass.getDefaultInstanceForType();
} }
try { try {
return (T) protoClass.getParserForType() return (T)
.parseFrom(Base64.decode(serializedProto, Base64.DEFAULT)); protoClass
.getParserForType()
.parseFrom(Base64.decode(serializedProto, Base64.DEFAULT));
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
Log.e(TAG, "Failed to deserialize proto class", e); Log.e(TAG, "Failed to deserialize proto class", e);
return (T) protoClass.getDefaultInstanceForType(); return (T) protoClass.getDefaultInstanceForType();
} }
} }
public void setForceAppStandby(int uid, String packageName, /** Sets force app standby mode */
int mode) { public void setForceAppStandby(int uid, String packageName, int mode) {
final boolean isPreOApp = isPreOApp(packageName); final boolean isPreOApp = isPreOApp(packageName);
if (isPreOApp) { if (isPreOApp) {
// Control whether app could run in the background if it is pre O app // Control whether app could run in the background if it is pre O app
@@ -385,22 +378,27 @@ public class BatteryUtils {
// Control whether app could run jobs in the background // Control whether app could run jobs in the background
mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(
final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager () -> {
.getInstance(mContext); final BatteryDatabaseManager batteryDatabaseManager =
if (mode == AppOpsManager.MODE_IGNORED) { BatteryDatabaseManager.getInstance(mContext);
batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, if (mode == AppOpsManager.MODE_IGNORED) {
uid, packageName, System.currentTimeMillis()); batteryDatabaseManager.insertAction(
} else if (mode == AppOpsManager.MODE_ALLOWED) { AnomalyDatabaseHelper.ActionType.RESTRICTION,
batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, uid,
uid, packageName); packageName,
} System.currentTimeMillis());
}); } else if (mode == AppOpsManager.MODE_ALLOWED) {
batteryDatabaseManager.deleteAction(
AnomalyDatabaseHelper.ActionType.RESTRICTION, uid, packageName);
}
});
} }
public boolean isForceAppStandbyEnabled(int uid, String packageName) { public boolean isForceAppStandbyEnabled(int uid, String packageName) {
return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, return mAppOpsManager.checkOpNoThrow(
packageName) == AppOpsManager.MODE_IGNORED; AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName)
== AppOpsManager.MODE_IGNORED;
} }
public boolean clearForceAppStandby(String packageName) { public boolean clearForceAppStandby(String packageName) {
@@ -415,12 +413,13 @@ public class BatteryUtils {
@WorkerThread @WorkerThread
public BatteryInfo getBatteryInfo(final String tag) { public BatteryInfo getBatteryInfo(final String tag) {
final BatteryStatsManager systemService = mContext.getSystemService( final BatteryStatsManager systemService =
BatteryStatsManager.class); mContext.getSystemService(BatteryStatsManager.class);
BatteryUsageStats batteryUsageStats; BatteryUsageStats batteryUsageStats;
try { try {
batteryUsageStats = systemService.getBatteryUsageStats( batteryUsageStats =
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); systemService.getBatteryUsageStats(
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "getBatteryInfo() error from getBatteryUsageStats()", e); Log.e(TAG, "getBatteryInfo() error from getBatteryUsageStats()", e);
// Use default BatteryUsageStats. // Use default BatteryUsageStats.
@@ -432,23 +431,29 @@ public class BatteryUtils {
// Stuff we always need to get BatteryInfo // Stuff we always need to get BatteryInfo
final Intent batteryBroadcast = getBatteryIntent(mContext); final Intent batteryBroadcast = getBatteryIntent(mContext);
final long elapsedRealtimeUs = PowerUtil.convertMsToUs( final long elapsedRealtimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
SystemClock.elapsedRealtime());
BatteryInfo batteryInfo; BatteryInfo batteryInfo;
Estimate estimate = getEnhancedEstimate(); Estimate estimate = getEnhancedEstimate();
// couldn't get estimate from cache or provider, use fallback // couldn't get estimate from cache or provider, use fallback
if (estimate == null) { if (estimate == null) {
estimate = new Estimate( estimate =
batteryUsageStats.getBatteryTimeRemainingMs(), new Estimate(
false /* isBasedOnUsage */, batteryUsageStats.getBatteryTimeRemainingMs(),
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); false /* isBasedOnUsage */,
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
} }
BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime); BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime);
batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast, batteryInfo =
batteryUsageStats, estimate, elapsedRealtimeUs, false /* shortString */); BatteryInfo.getBatteryInfo(
mContext,
batteryBroadcast,
batteryUsageStats,
estimate,
elapsedRealtimeUs,
false /* shortString */);
BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime); BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime);
try { try {
@@ -463,9 +468,9 @@ public class BatteryUtils {
Estimate getEnhancedEstimate() { Estimate getEnhancedEstimate() {
// Align the same logic in the BatteryControllerImpl.updateEstimate() // Align the same logic in the BatteryControllerImpl.updateEstimate()
Estimate estimate = Estimate.getCachedEstimateIfAvailable(mContext); Estimate estimate = Estimate.getCachedEstimateIfAvailable(mContext);
if (estimate == null && if (estimate == null
mPowerUsageFeatureProvider != null && && mPowerUsageFeatureProvider != null
mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) { && mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext); estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
if (estimate != null) { if (estimate != null) {
Estimate.storeCachedEstimate(mContext, estimate); Estimate.storeCachedEstimate(mContext, estimate);
@@ -500,8 +505,8 @@ public class BatteryUtils {
public boolean isPreOApp(final String packageName) { public boolean isPreOApp(final String packageName) {
try { try {
ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, ApplicationInfo info =
PackageManager.GET_META_DATA); mPackageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
return info.targetSdkVersion < Build.VERSION_CODES.O; return info.targetSdkVersion < Build.VERSION_CODES.O;
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
@@ -525,18 +530,17 @@ public class BatteryUtils {
return false; return false;
} }
/** /** Return {@code true} if we should hide anomaly app represented by {@code uid} */
* Return {@code true} if we should hide anomaly app represented by {@code uid} public boolean shouldHideAnomaly(
*/ PowerAllowlistBackend powerAllowlistBackend, int uid, AnomalyInfo anomalyInfo) {
public boolean shouldHideAnomaly(PowerAllowlistBackend powerAllowlistBackend, int uid,
AnomalyInfo anomalyInfo) {
final String[] packageNames = mPackageManager.getPackagesForUid(uid); final String[] packageNames = mPackageManager.getPackagesForUid(uid);
if (ArrayUtils.isEmpty(packageNames)) { if (ArrayUtils.isEmpty(packageNames)) {
// Don't show it if app has been uninstalled // Don't show it if app has been uninstalled
return true; return true;
} }
return isSystemUid(uid) || powerAllowlistBackend.isAllowlisted(packageNames, uid) return isSystemUid(uid)
|| powerAllowlistBackend.isAllowlisted(packageNames, uid)
|| (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames)) || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames))
|| (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames)); || (isExcessiveBackgroundAnomaly(anomalyInfo) && !isPreOApp(packageNames));
} }
@@ -554,8 +558,8 @@ public class BatteryUtils {
private boolean isSystemApp(PackageManager packageManager, String[] packageNames) { private boolean isSystemApp(PackageManager packageManager, String[] packageNames) {
for (String packageName : packageNames) { for (String packageName : packageNames) {
try { try {
final ApplicationInfo info = packageManager.getApplicationInfo(packageName, final ApplicationInfo info =
0 /* flags */); packageManager.getApplicationInfo(packageName, 0 /* flags */);
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return true; return true;
} }
@@ -577,11 +581,13 @@ public class BatteryUtils {
// components // components
// with ComponentInfo#directBootAware == false will be filtered. We should // with ComponentInfo#directBootAware == false will be filtered. We should
// explicitly include both direct boot aware and unaware components here. // explicitly include both direct boot aware and unaware components here.
final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent, final List<ResolveInfo> resolveInfos =
PackageManager.MATCH_DISABLED_COMPONENTS mPackageManager.queryIntentActivities(
| PackageManager.MATCH_DIRECT_BOOT_AWARE launchIntent,
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_SYSTEM_ONLY); | PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_SYSTEM_ONLY);
for (int i = 0, size = resolveInfos.size(); i < size; i++) { for (int i = 0, size = resolveInfos.size(); i < size; i++) {
final ResolveInfo resolveInfo = resolveInfos.get(i); final ResolveInfo resolveInfo = resolveInfos.get(i);
if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) { if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) {
@@ -598,8 +604,8 @@ public class BatteryUtils {
*/ */
public long getAppLongVersionCode(String packageName) { public long getAppLongVersionCode(String packageName) {
try { try {
final PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, final PackageInfo packageInfo =
0 /* flags */); mPackageManager.getPackageInfo(packageName, 0 /* flags */);
return packageInfo.getLongVersionCode(); return packageInfo.getLongVersionCode();
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Cannot find package: " + packageName, e); Log.e(TAG, "Cannot find package: " + packageName, e);
@@ -626,7 +632,8 @@ public class BatteryUtils {
/** Gets the logging package name. */ /** Gets the logging package name. */
public static String getLoggingPackageName(Context context, String originalPackingName) { public static String getLoggingPackageName(Context context, String originalPackingName) {
return BatteryUtils.isAppInstalledFromGooglePlayStore(context, originalPackingName) return BatteryUtils.isAppInstalledFromGooglePlayStore(context, originalPackingName)
? originalPackingName : PACKAGE_NAME_NONE; ? originalPackingName
: PACKAGE_NAME_NONE;
} }
/** Gets the latest sticky battery intent from the Android system. */ /** Gets the latest sticky battery intent from the Android system. */
@@ -637,12 +644,14 @@ public class BatteryUtils {
/** Gets the current dock defender mode */ /** Gets the current dock defender mode */
public static int getCurrentDockDefenderMode(Context context, BatteryInfo batteryInfo) { public static int getCurrentDockDefenderMode(Context context, BatteryInfo batteryInfo) {
if (batteryInfo.pluggedStatus == BatteryManager.BATTERY_PLUGGED_DOCK) { if (batteryInfo.pluggedStatus == BatteryManager.BATTERY_PLUGGED_DOCK) {
if (Settings.Global.getInt(context.getContentResolver(), if (Settings.Global.getInt(
SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, 0) == 1) { context.getContentResolver(), SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS, 0)
== 1) {
return DockDefenderMode.TEMPORARILY_BYPASSED; return DockDefenderMode.TEMPORARILY_BYPASSED;
} else if (batteryInfo.isBatteryDefender && FeatureFactory.getFeatureFactory() } else if (batteryInfo.isBatteryDefender
.getPowerUsageFeatureProvider() && FeatureFactory.getFeatureFactory()
.isExtraDefend()) { .getPowerUsageFeatureProvider()
.isExtraDefend()) {
return DockDefenderMode.ACTIVE; return DockDefenderMode.ACTIVE;
} else if (!batteryInfo.isBatteryDefender) { } else if (!batteryInfo.isBatteryDefender) {
return DockDefenderMode.FUTURE_BYPASS; return DockDefenderMode.FUTURE_BYPASS;
@@ -651,59 +660,82 @@ public class BatteryUtils {
return DockDefenderMode.DISABLED; return DockDefenderMode.DISABLED;
} }
/** Formats elapsed time without commas in between. */ /** Formats elapsed time without commas in between. */
public static CharSequence formatElapsedTimeWithoutComma( public static CharSequence formatElapsedTimeWithoutComma(
Context context, double millis, boolean withSeconds, boolean collapseTimeUnit) { Context context, double millis, boolean withSeconds, boolean collapseTimeUnit) {
return StringUtil.formatElapsedTime(context, millis, withSeconds, collapseTimeUnit) return StringUtil.formatElapsedTime(context, millis, withSeconds, collapseTimeUnit)
.toString().replaceAll(",", ""); .toString()
.replaceAll(",", "");
} }
/** Builds the battery usage time summary. */ /** Builds the battery usage time summary. */
public static String buildBatteryUsageTimeSummary(final Context context, final boolean isSystem, public static String buildBatteryUsageTimeSummary(
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs, final Context context,
final boolean isSystem,
final long foregroundUsageTimeInMs,
final long backgroundUsageTimeInMs,
final long screenOnTimeInMs) { final long screenOnTimeInMs) {
StringBuilder summary = new StringBuilder(); StringBuilder summary = new StringBuilder();
if (isSystem) { if (isSystem) {
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs; final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
if (totalUsageTimeInMs != 0) { if (totalUsageTimeInMs != 0) {
summary.append(buildBatteryUsageTimeInfo(context, totalUsageTimeInMs, summary.append(
R.string.battery_usage_total_less_than_one_minute, buildBatteryUsageTimeInfo(
R.string.battery_usage_for_total_time)); context,
totalUsageTimeInMs,
R.string.battery_usage_total_less_than_one_minute,
R.string.battery_usage_for_total_time));
} }
} else { } else {
if (screenOnTimeInMs != 0) { if (screenOnTimeInMs != 0) {
summary.append(buildBatteryUsageTimeInfo(context, screenOnTimeInMs, summary.append(
R.string.battery_usage_screen_time_less_than_one_minute, buildBatteryUsageTimeInfo(
R.string.battery_usage_screen_time)); context,
screenOnTimeInMs,
R.string.battery_usage_screen_time_less_than_one_minute,
R.string.battery_usage_screen_time));
} }
if (screenOnTimeInMs != 0 && backgroundUsageTimeInMs != 0) { if (screenOnTimeInMs != 0 && backgroundUsageTimeInMs != 0) {
summary.append('\n'); summary.append('\n');
} }
if (backgroundUsageTimeInMs != 0) { if (backgroundUsageTimeInMs != 0) {
summary.append(buildBatteryUsageTimeInfo(context, backgroundUsageTimeInMs, summary.append(
R.string.battery_usage_background_less_than_one_minute, buildBatteryUsageTimeInfo(
R.string.battery_usage_for_background_time)); context,
backgroundUsageTimeInMs,
R.string.battery_usage_background_less_than_one_minute,
R.string.battery_usage_for_background_time));
} }
} }
return summary.toString(); return summary.toString();
} }
/** Format the date of battery related info */ /** Format the date of battery related info */
public static CharSequence getBatteryInfoFormattedDate(long dateInMs) { public static CharSequence getBatteryInfoFormattedDate(long dateInMs) {
final Instant instant = Instant.ofEpochMilli(dateInMs); final Instant instant = Instant.ofEpochMilli(dateInMs);
final String localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate().format( final String localDate =
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)); instant.atZone(ZoneId.systemDefault())
.toLocalDate()
.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
return localDate; return localDate;
} }
/** Builds the battery usage time information for one timestamp. */ /** Builds the battery usage time information for one timestamp. */
private static String buildBatteryUsageTimeInfo(final Context context, long timeInMs, private static String buildBatteryUsageTimeInfo(
final int lessThanOneMinuteResId, final int normalResId) { final Context context,
long timeInMs,
final int lessThanOneMinuteResId,
final int normalResId) {
if (timeInMs < DateUtils.MINUTE_IN_MILLIS) { if (timeInMs < DateUtils.MINUTE_IN_MILLIS) {
return context.getString(lessThanOneMinuteResId); return context.getString(lessThanOneMinuteResId);
} }
final CharSequence timeSequence = formatElapsedTimeWithoutComma( final CharSequence timeSequence =
context, (double) timeInMs, /*withSeconds=*/ false, /*collapseTimeUnit=*/ false); formatElapsedTimeWithoutComma(
context,
(double) timeInMs,
/* withSeconds= */ false,
/* collapseTimeUnit= */ false);
return context.getString(normalResId, timeSequence); return context.getString(normalResId, timeSequence);
} }
} }

View File

@@ -40,9 +40,7 @@ public class DebugEstimatesLoader extends AsyncLoaderCompat<List<BatteryInfo>> {
} }
@Override @Override
protected void onDiscardResult(List<BatteryInfo> result) { protected void onDiscardResult(List<BatteryInfo> result) {}
}
@Override @Override
public List<BatteryInfo> loadInBackground() { public List<BatteryInfo> loadInBackground() {
@@ -51,29 +49,39 @@ public class DebugEstimatesLoader extends AsyncLoaderCompat<List<BatteryInfo>> {
FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider();
// get stuff we'll need for both BatteryInfo // get stuff we'll need for both BatteryInfo
final long elapsedRealtimeUs = PowerUtil.convertMsToUs( final long elapsedRealtimeUs = PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());
SystemClock.elapsedRealtime()); Intent batteryBroadcast =
Intent batteryBroadcast = getContext().registerReceiver(null, getContext()
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); .registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
BatteryUsageStats batteryUsageStats; BatteryUsageStats batteryUsageStats;
try { try {
batteryUsageStats = context.getSystemService(BatteryStatsManager.class) batteryUsageStats =
.getBatteryUsageStats(); context.getSystemService(BatteryStatsManager.class).getBatteryUsageStats();
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "getBatteryInfo() from getBatteryUsageStats()", e); Log.e(TAG, "getBatteryInfo() from getBatteryUsageStats()", e);
// Use default BatteryUsageStats. // Use default BatteryUsageStats.
batteryUsageStats = new BatteryUsageStats.Builder(new String[0]).build(); batteryUsageStats = new BatteryUsageStats.Builder(new String[0]).build();
} }
BatteryInfo oldinfo = BatteryInfo.getBatteryInfoOld(getContext(), batteryBroadcast, BatteryInfo oldinfo =
batteryUsageStats, elapsedRealtimeUs, false); BatteryInfo.getBatteryInfoOld(
getContext(),
batteryBroadcast,
batteryUsageStats,
elapsedRealtimeUs,
false);
Estimate estimate = powerUsageFeatureProvider.getEnhancedBatteryPrediction(context); Estimate estimate = powerUsageFeatureProvider.getEnhancedBatteryPrediction(context);
if (estimate == null) { if (estimate == null) {
estimate = new Estimate(0, false, EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN); estimate = new Estimate(0, false, EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
} }
BatteryInfo newInfo = BatteryInfo.getBatteryInfo(getContext(), batteryBroadcast, BatteryInfo newInfo =
batteryUsageStats, BatteryInfo.getBatteryInfo(
estimate, elapsedRealtimeUs, false); getContext(),
batteryBroadcast,
batteryUsageStats,
estimate,
elapsedRealtimeUs,
false);
List<BatteryInfo> infos = new ArrayList<>(); List<BatteryInfo> infos = new ArrayList<>();
infos.add(oldinfo); infos.add(oldinfo);

View File

@@ -40,23 +40,18 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
public class HighPowerDetail extends InstrumentedDialogFragment implements OnClickListener, public class HighPowerDetail extends InstrumentedDialogFragment
View.OnClickListener { implements OnClickListener, View.OnClickListener {
private static final String ARG_DEFAULT_ON = "default_on"; private static final String ARG_DEFAULT_ON = "default_on";
@VisibleForTesting @VisibleForTesting PowerAllowlistBackend mBackend;
PowerAllowlistBackend mBackend; @VisibleForTesting BatteryUtils mBatteryUtils;
@VisibleForTesting @VisibleForTesting String mPackageName;
BatteryUtils mBatteryUtils; @VisibleForTesting int mPackageUid;
@VisibleForTesting
String mPackageName;
@VisibleForTesting
int mPackageUid;
private CharSequence mLabel; private CharSequence mLabel;
private boolean mDefaultOn; private boolean mDefaultOn;
@VisibleForTesting @VisibleForTesting boolean mIsEnabled;
boolean mIsEnabled;
private Checkable mOptionOn; private Checkable mOptionOn;
private Checkable mOptionOff; private Checkable mOptionOff;
@@ -85,10 +80,13 @@ public class HighPowerDetail extends InstrumentedDialogFragment implements OnCli
} }
public Checkable setup(View view, boolean on) { public Checkable setup(View view, boolean on) {
((TextView) view.findViewById(android.R.id.title)).setText(on ((TextView) view.findViewById(android.R.id.title))
? R.string.ignore_optimizations_on : R.string.ignore_optimizations_off); .setText(on ? R.string.ignore_optimizations_on : R.string.ignore_optimizations_off);
((TextView) view.findViewById(android.R.id.summary)).setText(on ((TextView) view.findViewById(android.R.id.summary))
? R.string.ignore_optimizations_on_desc : R.string.ignore_optimizations_off_desc); .setText(
on
? R.string.ignore_optimizations_on_desc
: R.string.ignore_optimizations_off_desc);
view.setClickable(true); view.setClickable(true);
view.setOnClickListener(this); view.setOnClickListener(this);
if (!on && mBackend.isSysAllowlisted(mPackageName)) { if (!on && mBackend.isSysAllowlisted(mPackageName)) {
@@ -99,10 +97,11 @@ public class HighPowerDetail extends InstrumentedDialogFragment implements OnCli
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder b = new AlertDialog.Builder(getContext()) AlertDialog.Builder b =
.setTitle(mLabel) new AlertDialog.Builder(getContext())
.setNegativeButton(R.string.cancel, null) .setTitle(mLabel)
.setView(R.layout.ignore_optimizations_content); .setNegativeButton(R.string.cancel, null)
.setView(R.layout.ignore_optimizations_content);
if (!mBackend.isSysAllowlisted(mPackageName)) { if (!mBackend.isSysAllowlisted(mPackageName)) {
b.setPositiveButton(R.string.done, this); b.setPositiveButton(R.string.done, this);
} }
@@ -141,8 +140,8 @@ public class HighPowerDetail extends InstrumentedDialogFragment implements OnCli
if (newValue != oldValue) { if (newValue != oldValue) {
logSpecialPermissionChange(newValue, mPackageName, getContext()); logSpecialPermissionChange(newValue, mPackageName, getContext());
if (newValue) { if (newValue) {
mBatteryUtils.setForceAppStandby(mPackageUid, mPackageName, mBatteryUtils.setForceAppStandby(
AppOpsManager.MODE_ALLOWED); mPackageUid, mPackageName, AppOpsManager.MODE_ALLOWED);
mBackend.addApp(mPackageName); mBackend.addApp(mPackageName);
} else { } else {
mBackend.removeApp(mPackageName); mBackend.removeApp(mPackageName);
@@ -153,10 +152,13 @@ public class HighPowerDetail extends InstrumentedDialogFragment implements OnCli
@VisibleForTesting @VisibleForTesting
static void logSpecialPermissionChange(boolean allowlist, String packageName, Context context) { static void logSpecialPermissionChange(boolean allowlist, String packageName, Context context) {
int logCategory = allowlist ? SettingsEnums.APP_SPECIAL_PERMISSION_BATTERY_DENY int logCategory =
: SettingsEnums.APP_SPECIAL_PERMISSION_BATTERY_ALLOW; allowlist
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().action(context, logCategory, ? SettingsEnums.APP_SPECIAL_PERMISSION_BATTERY_DENY
packageName); : SettingsEnums.APP_SPECIAL_PERMISSION_BATTERY_ALLOW;
FeatureFactory.getFeatureFactory()
.getMetricsFeatureProvider()
.action(context, logCategory, packageName);
} }
@Override @Override
@@ -177,8 +179,8 @@ public class HighPowerDetail extends InstrumentedDialogFragment implements OnCli
} }
@VisibleForTesting @VisibleForTesting
static CharSequence getSummary(Context context, PowerAllowlistBackend powerAllowlist, static CharSequence getSummary(
String pkg, int uid) { Context context, PowerAllowlistBackend powerAllowlist, String pkg, int uid) {
return context.getString( return context.getString(
powerAllowlist.isSysAllowlisted(pkg) || powerAllowlist.isDefaultActiveApp(pkg, uid) powerAllowlist.isSysAllowlisted(pkg) || powerAllowlist.isDefaultActiveApp(pkg, uid)
? R.string.high_power_system ? R.string.high_power_system

View File

@@ -47,15 +47,16 @@ import java.util.List;
public class InactiveApps extends SettingsPreferenceFragment public class InactiveApps extends SettingsPreferenceFragment
implements Preference.OnPreferenceChangeListener { implements Preference.OnPreferenceChangeListener {
private static final CharSequence[] FULL_SETTABLE_BUCKETS_NAMES = private static final CharSequence[] FULL_SETTABLE_BUCKETS_NAMES = {
{"ACTIVE", "WORKING_SET", "FREQUENT", "RARE", "RESTRICTED"}; "ACTIVE", "WORKING_SET", "FREQUENT", "RARE", "RESTRICTED"
};
private static final CharSequence[] FULL_SETTABLE_BUCKETS_VALUES = { private static final CharSequence[] FULL_SETTABLE_BUCKETS_VALUES = {
Integer.toString(STANDBY_BUCKET_ACTIVE), Integer.toString(STANDBY_BUCKET_ACTIVE),
Integer.toString(STANDBY_BUCKET_WORKING_SET), Integer.toString(STANDBY_BUCKET_WORKING_SET),
Integer.toString(STANDBY_BUCKET_FREQUENT), Integer.toString(STANDBY_BUCKET_FREQUENT),
Integer.toString(STANDBY_BUCKET_RARE), Integer.toString(STANDBY_BUCKET_RARE),
Integer.toString(STANDBY_BUCKET_RESTRICTED) Integer.toString(STANDBY_BUCKET_RESTRICTED)
}; };
private UsageStatsManager mUsageStats; private UsageStatsManager mUsageStats;
@@ -118,7 +119,7 @@ public class InactiveApps extends SettingsPreferenceFragment
return possibleBuckets; return possibleBuckets;
} }
if (minBucket < STANDBY_BUCKET_ACTIVE) { if (minBucket < STANDBY_BUCKET_ACTIVE) {
return new CharSequence[]{}; return new CharSequence[] {};
} }
// Use FULL_SETTABLE_BUCKETS_VALUES since we're searching using the int value. The index // Use FULL_SETTABLE_BUCKETS_VALUES since we're searching using the int value. The index
// should apply no matter which array we're going to copy from. // should apply no matter which array we're going to copy from.
@@ -133,13 +134,20 @@ public class InactiveApps extends SettingsPreferenceFragment
static String bucketToName(int bucket) { static String bucketToName(int bucket) {
switch (bucket) { switch (bucket) {
case STANDBY_BUCKET_EXEMPTED: return "EXEMPTED"; case STANDBY_BUCKET_EXEMPTED:
case STANDBY_BUCKET_ACTIVE: return "ACTIVE"; return "EXEMPTED";
case STANDBY_BUCKET_WORKING_SET: return "WORKING_SET"; case STANDBY_BUCKET_ACTIVE:
case STANDBY_BUCKET_FREQUENT: return "FREQUENT"; return "ACTIVE";
case STANDBY_BUCKET_RARE: return "RARE"; case STANDBY_BUCKET_WORKING_SET:
case STANDBY_BUCKET_RESTRICTED: return "RESTRICTED"; return "WORKING_SET";
case STANDBY_BUCKET_NEVER: return "NEVER"; case STANDBY_BUCKET_FREQUENT:
return "FREQUENT";
case STANDBY_BUCKET_RARE:
return "RARE";
case STANDBY_BUCKET_RESTRICTED:
return "RESTRICTED";
case STANDBY_BUCKET_NEVER:
return "NEVER";
} }
return ""; return "";
} }
@@ -148,13 +156,13 @@ public class InactiveApps extends SettingsPreferenceFragment
final Resources res = getActivity().getResources(); final Resources res = getActivity().getResources();
final int appBucket = mUsageStats.getAppStandbyBucket(p.getKey()); final int appBucket = mUsageStats.getAppStandbyBucket(p.getKey());
final String bucketName = bucketToName(appBucket); final String bucketName = bucketToName(appBucket);
p.setSummary(res.getString( p.setSummary(
com.android.settingslib.R.string.standby_bucket_summary, bucketName)); res.getString(com.android.settingslib.R.string.standby_bucket_summary, bucketName));
// Buckets outside of the range of the dynamic ones are only used for special // Buckets outside of the range of the dynamic ones are only used for special
// purposes and can either not be changed out of, or might have undesirable // purposes and can either not be changed out of, or might have undesirable
// side-effects in combination with other assumptions. // side-effects in combination with other assumptions.
final boolean changeable = appBucket >= STANDBY_BUCKET_ACTIVE final boolean changeable =
&& appBucket <= STANDBY_BUCKET_RESTRICTED; appBucket >= STANDBY_BUCKET_ACTIVE && appBucket <= STANDBY_BUCKET_RESTRICTED;
if (changeable) { if (changeable) {
p.setValue(Integer.toString(appBucket)); p.setValue(Integer.toString(appBucket));
} }

View File

@@ -30,10 +30,8 @@ public class OptimizedPreferenceController extends AbstractPreferenceController
private static final String TAG = "OPTIMIZED_PREF"; private static final String TAG = "OPTIMIZED_PREF";
@VisibleForTesting @VisibleForTesting static final String KEY_OPTIMIZED_PREF = "optimized_preference";
static final String KEY_OPTIMIZED_PREF = "optimized_preference"; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
@VisibleForTesting
BatteryOptimizeUtils mBatteryOptimizeUtils;
public OptimizedPreferenceController(Context context, int uid, String packageName) { public OptimizedPreferenceController(Context context, int uid, String packageName) {
super(context); super(context);
@@ -49,9 +47,10 @@ public class OptimizedPreferenceController extends AbstractPreferenceController
public void updateState(Preference preference) { public void updateState(Preference preference) {
preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled());
final boolean isOptimized = mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly() final boolean isOptimized =
|| mBatteryOptimizeUtils.getAppOptimizationMode() mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()
== BatteryOptimizeUtils.MODE_OPTIMIZED; || mBatteryOptimizeUtils.getAppOptimizationMode()
== BatteryOptimizeUtils.MODE_OPTIMIZED;
((SelectorWithWidgetPreference) preference).setChecked(isOptimized); ((SelectorWithWidgetPreference) preference).setChecked(isOptimized);
} }

View File

@@ -16,7 +16,6 @@
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import android.app.Activity; import android.app.Activity;
@@ -53,12 +52,9 @@ import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
/** /** Allow background usage fragment for each app */
* Allow background usage fragment for each app public class PowerBackgroundUsageDetail extends DashboardFragment
*/ implements SelectorWithWidgetPreference.OnClickListener, OnMainSwitchChangeListener {
public class PowerBackgroundUsageDetail extends DashboardFragment implements
SelectorWithWidgetPreference.OnClickListener,
OnMainSwitchChangeListener {
private static final String TAG = "PowerBackgroundUsageDetail"; private static final String TAG = "PowerBackgroundUsageDetail";
public static final String EXTRA_UID = "extra_uid"; public static final String EXTRA_UID = "extra_uid";
@@ -74,28 +70,18 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@VisibleForTesting @VisibleForTesting LayoutPreference mHeaderPreference;
LayoutPreference mHeaderPreference; @VisibleForTesting ApplicationsState mState;
@VisibleForTesting @VisibleForTesting ApplicationsState.AppEntry mAppEntry;
ApplicationsState mState; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
@VisibleForTesting @VisibleForTesting SelectorWithWidgetPreference mOptimizePreference;
ApplicationsState.AppEntry mAppEntry; @VisibleForTesting SelectorWithWidgetPreference mUnrestrictedPreference;
@VisibleForTesting @VisibleForTesting MainSwitchPreference mMainSwitchPreference;
BatteryOptimizeUtils mBatteryOptimizeUtils; @VisibleForTesting FooterPreference mFooterPreference;
@VisibleForTesting @VisibleForTesting BackupManager mBackupManager;
SelectorWithWidgetPreference mOptimizePreference; @VisibleForTesting StringBuilder mLogStringBuilder;
@VisibleForTesting
SelectorWithWidgetPreference mUnrestrictedPreference; @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode
@VisibleForTesting
MainSwitchPreference mMainSwitchPreference;
@VisibleForTesting
FooterPreference mFooterPreference;
@VisibleForTesting
BackupManager mBackupManager;
@VisibleForTesting
StringBuilder mLogStringBuilder;
@VisibleForTesting
@BatteryOptimizeUtils.OptimizationMode
int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN;
@Override @Override
@@ -124,15 +110,18 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
initHeader(); initHeader();
mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode();
initFooter(); initFooter();
mExecutor.execute(() -> { mExecutor.execute(
String packageName = BatteryUtils () -> {
.getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); String packageName =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() BatteryUtils.getLoggingPackageName(
.action( getContext(), mBatteryOptimizeUtils.getPackageName());
getContext(), FeatureFactory.getFeatureFactory()
SettingsEnums.OPEN_APP_BATTERY_USAGE, .getMetricsFeatureProvider()
packageName); .action(
}); getContext(),
SettingsEnums.OPEN_APP_BATTERY_USAGE,
packageName);
});
mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode);
} }
@@ -145,14 +134,15 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
logMetricCategory(currentOptimizeMode); logMetricCategory(currentOptimizeMode);
mExecutor.execute(() -> { mExecutor.execute(
BatteryOptimizeLogUtils.writeLog( () -> {
getContext().getApplicationContext(), BatteryOptimizeLogUtils.writeLog(
Action.LEAVE, getContext().getApplicationContext(),
BatteryOptimizeLogUtils.getPackageNameWithUserId( Action.LEAVE,
mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), BatteryOptimizeLogUtils.getPackageNameWithUserId(
mLogStringBuilder.toString()); mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()),
}); mLogStringBuilder.toString());
});
Log.d(TAG, "Leave with mode: " + currentOptimizeMode); Log.d(TAG, "Leave with mode: " + currentOptimizeMode);
} }
@@ -209,8 +199,8 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
@VisibleForTesting @VisibleForTesting
void notifyBackupManager() { void notifyBackupManager() {
if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) { if (mOptimizationMode != mBatteryOptimizeUtils.getAppOptimizationMode()) {
final BackupManager backupManager = mBackupManager != null final BackupManager backupManager =
? mBackupManager : new BackupManager(getContext()); mBackupManager != null ? mBackupManager : new BackupManager(getContext());
backupManager.dataChanged(); backupManager.dataChanged();
} }
} }
@@ -228,8 +218,7 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
} }
} }
static void startPowerBackgroundUsageDetailPage( static void startPowerBackgroundUsageDetailPage(Context context, Bundle args) {
Context context, Bundle args) {
new SubSettingLauncher(context) new SubSettingLauncher(context)
.setDestination(PowerBackgroundUsageDetail.class.getName()) .setDestination(PowerBackgroundUsageDetail.class.getName())
.setArguments(args) .setArguments(args)
@@ -242,10 +231,11 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header);
final Activity context = getActivity(); final Activity context = getActivity();
final Bundle bundle = getArguments(); final Bundle bundle = getArguments();
EntityHeaderController controller = EntityHeaderController EntityHeaderController controller =
.newInstance(context, this, appSnippet) EntityHeaderController.newInstance(context, this, appSnippet)
.setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, .setButtonActions(
EntityHeaderController.ActionType.ACTION_NONE); EntityHeaderController.ActionType.ACTION_NONE,
EntityHeaderController.ActionType.ACTION_NONE);
if (mAppEntry == null) { if (mAppEntry == null) {
controller.setLabel(bundle.getString(EXTRA_LABEL)); controller.setLabel(bundle.getString(EXTRA_LABEL));
@@ -275,23 +265,26 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) {
// Present optimized only string when the package name is invalid. // Present optimized only string when the package name is invalid.
stateString = context.getString(R.string.manager_battery_usage_optimized_only); stateString = context.getString(R.string.manager_battery_usage_optimized_only);
footerString = context.getString( footerString =
R.string.manager_battery_usage_footer_limited, stateString); context.getString(R.string.manager_battery_usage_footer_limited, stateString);
} else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
// Present unrestricted only string when the package is system or default active app. // Present unrestricted only string when the package is system or default active app.
stateString = context.getString(R.string.manager_battery_usage_unrestricted_only); stateString = context.getString(R.string.manager_battery_usage_unrestricted_only);
footerString = context.getString( footerString =
R.string.manager_battery_usage_footer_limited, stateString); context.getString(R.string.manager_battery_usage_footer_limited, stateString);
} else { } else {
// Present default string to normal app. // Present default string to normal app.
footerString = context.getString(R.string.manager_battery_usage_footer); footerString = context.getString(R.string.manager_battery_usage_footer);
} }
mFooterPreference.setTitle(footerString); mFooterPreference.setTitle(footerString);
final Intent helpIntent = HelpUtils.getHelpIntent(context, context.getString( final Intent helpIntent =
R.string.help_url_app_usage_settings), /*backupContext=*/ ""); HelpUtils.getHelpIntent(
context,
context.getString(R.string.help_url_app_usage_settings),
/* backupContext= */ "");
if (helpIntent != null) { if (helpIntent != null) {
mFooterPreference.setLearnMoreAction(v -> mFooterPreference.setLearnMoreAction(
startActivityForResult(helpIntent, /*requestCode=*/ 0)); v -> startActivityForResult(helpIntent, /* requestCode= */ 0));
mFooterPreference.setLearnMoreText( mFooterPreference.setLearnMoreText(
context.getString(R.string.manager_battery_usage_link_a11y)); context.getString(R.string.manager_battery_usage_link_a11y));
} }
@@ -307,12 +300,13 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
mUnrestrictedPreference.setOnClickListener(this); mUnrestrictedPreference.setOnClickListener(this);
mMainSwitchPreference.addOnSwitchChangeListener(this); mMainSwitchPreference.addOnSwitchChangeListener(this);
mBatteryOptimizeUtils = new BatteryOptimizeUtils( mBatteryOptimizeUtils =
getContext(), getArguments().getInt(EXTRA_UID), packageName); new BatteryOptimizeUtils(
getContext(), getArguments().getInt(EXTRA_UID), packageName);
} }
private void updateSelectorPreferenceState(SelectorWithWidgetPreference preference, private void updateSelectorPreferenceState(
String selectedKey) { SelectorWithWidgetPreference preference, String selectedKey) {
preference.setChecked(TextUtils.equals(selectedKey, preference.getKey())); preference.setChecked(TextUtils.equals(selectedKey, preference.getKey()));
} }
@@ -336,16 +330,19 @@ public class PowerBackgroundUsageDetail extends DashboardFragment implements
return; return;
} }
int finalMetricCategory = metricCategory; int finalMetricCategory = metricCategory;
mExecutor.execute(() -> { mExecutor.execute(
String packageName = BatteryUtils () -> {
.getLoggingPackageName(getContext(), mBatteryOptimizeUtils.getPackageName()); String packageName =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() BatteryUtils.getLoggingPackageName(
.action( getContext(), mBatteryOptimizeUtils.getPackageName());
/* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE, FeatureFactory.getFeatureFactory()
/* action */ finalMetricCategory, .getMetricsFeatureProvider()
/* pageId */ SettingsEnums.OPEN_APP_BATTERY_USAGE, .action(
packageName, /* attribution */ SettingsEnums.OPEN_APP_BATTERY_USAGE,
getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT)); /* action */ finalMetricCategory,
}); /* pageId */ SettingsEnums.OPEN_APP_BATTERY_USAGE,
packageName,
getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT));
});
} }
} }

View File

@@ -28,19 +28,13 @@ import com.android.settingslib.fuelgauge.Estimate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
/** /** Feature Provider used in power usage */
* Feature Provider used in power usage
*/
public interface PowerUsageFeatureProvider { public interface PowerUsageFeatureProvider {
/** /** Check whether the battery usage button is enabled in the battery page */
* Check whether the battery usage button is enabled in the battery page
*/
boolean isBatteryUsageEnabled(); boolean isBatteryUsageEnabled();
/** /** Check whether the battery tips card is enabled in the battery usage page */
* Check whether the battery tips card is enabled in the battery usage page
*/
boolean isBatteryTipsEnabled(); boolean isBatteryTipsEnabled();
/** /**
@@ -48,39 +42,25 @@ public interface PowerUsageFeatureProvider {
*/ */
double getBatteryUsageListScreenOnTimeThresholdInMs(); double getBatteryUsageListScreenOnTimeThresholdInMs();
/** /** Returns a threshold (mA) for the minimal comsume power in battery usage list */
* Returns a threshold (mA) for the minimal comsume power in battery usage list
*/
double getBatteryUsageListConsumePowerThreshold(); double getBatteryUsageListConsumePowerThreshold();
/** /** Returns an allowlist of app names combined into the system-apps item */
* Returns an allowlist of app names combined into the system-apps item
*/
List<String> getSystemAppsAllowlist(); List<String> getSystemAppsAllowlist();
/** /** Check whether location setting is enabled */
* Check whether location setting is enabled
*/
boolean isLocationSettingEnabled(String[] packages); boolean isLocationSettingEnabled(String[] packages);
/** /** Gets an {@link Intent} to show additional battery info */
* Gets an {@link Intent} to show additional battery info
*/
Intent getAdditionalBatteryInfoIntent(); Intent getAdditionalBatteryInfoIntent();
/** /** Check whether it is type service */
* Check whether it is type service
*/
boolean isTypeService(int uid); boolean isTypeService(int uid);
/** /** Check whether it is type system */
* Check whether it is type system
*/
boolean isTypeSystem(int uid, String[] packages); boolean isTypeSystem(int uid, String[] packages);
/** /** Returns an improved prediction for battery time remaining */
* Returns an improved prediction for battery time remaining
*/
Estimate getEnhancedBatteryPrediction(Context context); Estimate getEnhancedBatteryPrediction(Context context);
/** /**
@@ -90,14 +70,10 @@ public interface PowerUsageFeatureProvider {
*/ */
SparseIntArray getEnhancedBatteryPredictionCurve(Context context, long zeroTime); SparseIntArray getEnhancedBatteryPredictionCurve(Context context, long zeroTime);
/** /** Checks whether the toggle for enhanced battery predictions is enabled */
* Checks whether the toggle for enhanced battery predictions is enabled
*/
boolean isEnhancedBatteryPredictionEnabled(Context context); boolean isEnhancedBatteryPredictionEnabled(Context context);
/** /** Checks whether debugging should be enabled for battery estimates */
* Checks whether debugging should be enabled for battery estimates
*/
boolean isEstimateDebugEnabled(); boolean isEstimateDebugEnabled();
/** /**
@@ -115,88 +91,54 @@ public interface PowerUsageFeatureProvider {
*/ */
String getOldEstimateDebugString(String timeRemaining); String getOldEstimateDebugString(String timeRemaining);
/** /** Checks whether smart battery feature is supported in this device */
* Checks whether smart battery feature is supported in this device
*/
boolean isSmartBatterySupported(); boolean isSmartBatterySupported();
/** /** Checks whether we should show usage information by slots or not */
* Checks whether we should show usage information by slots or not
*/
boolean isChartGraphSlotsEnabled(Context context); boolean isChartGraphSlotsEnabled(Context context);
/** /** Returns {@code true} if current defender mode is extra defend */
* Returns {@code true} if current defender mode is extra defend
*/
boolean isExtraDefend(); boolean isExtraDefend();
/** /** Returns {@code true} if delay the hourly job when device is booting */
* Returns {@code true} if delay the hourly job when device is booting
*/
boolean delayHourlyJobWhenBooting(); boolean delayHourlyJobWhenBooting();
/** /** Insert settings configuration data for anomaly detection */
* Insert settings configuration data for anomaly detection
*/
void insertSettingsData(Context context, double displayDrain); void insertSettingsData(Context context, double displayDrain);
/** /** Returns {@link Bundle} for settings anomaly detection result */
* Returns {@link Bundle} for settings anomaly detection result
*/
PowerAnomalyEventList detectSettingsAnomaly(Context context, double displayDrain); PowerAnomalyEventList detectSettingsAnomaly(Context context, double displayDrain);
/** /** Gets an intent for one time bypass charge limited to resume charging. */
* Gets an intent for one time bypass charge limited to resume charging.
*/
Intent getResumeChargeIntent(boolean isDockDefender); Intent getResumeChargeIntent(boolean isDockDefender);
/** /** Returns the intent action used to mark as the full charge start event. */
* Returns the intent action used to mark as the full charge start event.
*/
String getFullChargeIntentAction(); String getFullChargeIntentAction();
/** /** Returns {@link Set} for the system component ids which are combined into others */
* Returns {@link Set} for the system component ids which are combined into others
*/
Set<Integer> getOthersSystemComponentSet(); Set<Integer> getOthersSystemComponentSet();
/** /** Returns {@link Set} for the custom system component names which are combined into others */
* Returns {@link Set} for the custom system component names which are combined into others
*/
Set<String> getOthersCustomComponentNameSet(); Set<String> getOthersCustomComponentNameSet();
/** /** Returns {@link Set} for hiding system component ids in the usage screen */
* Returns {@link Set} for hiding system component ids in the usage screen
*/
Set<Integer> getHideSystemComponentSet(); Set<Integer> getHideSystemComponentSet();
/** /** Returns {@link Set} for hiding application package names in the usage screen */
* Returns {@link Set} for hiding application package names in the usage screen
*/
Set<String> getHideApplicationSet(); Set<String> getHideApplicationSet();
/** /** Returns {@link Set} for hiding applications background usage time */
* Returns {@link Set} for hiding applications background usage time
*/
Set<String> getHideBackgroundUsageTimeSet(); Set<String> getHideBackgroundUsageTimeSet();
/** /** Returns {@link Set} for ignoring task root class names for screen on time */
* Returns {@link Set} for ignoring task root class names for screen on time
*/
Set<String> getIgnoreScreenOnTimeTaskRootSet(); Set<String> getIgnoreScreenOnTimeTaskRootSet();
/** /** Returns the customized device build information for data backup */
* Returns the customized device build information for data backup
*/
String getBuildMetadata1(Context context); String getBuildMetadata1(Context context);
/** /** Returns the customized device build information for data backup */
* Returns the customized device build information for data backup
*/
String getBuildMetadata2(Context context); String getBuildMetadata2(Context context);
/** /** Whether the app optimization mode is valid to restore */
* Whether the app optimization mode is valid to restore
*/
boolean isValidToRestoreOptimizationMode(ArrayMap<String, String> deviceInfoMap); boolean isValidToRestoreOptimizationMode(ArrayMap<String, String> deviceInfoMap);
} }

View File

@@ -39,8 +39,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar"; private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media"; private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, private static final String[] PACKAGES_SYSTEM = {
PACKAGE_CALENDAR_PROVIDER, SYSTEMUI_PACKAGE_NAME}; PACKAGE_MEDIA_PROVIDER, PACKAGE_CALENDAR_PROVIDER, SYSTEMUI_PACKAGE_NAME
};
protected PackageManager mPackageManager; protected PackageManager mPackageManager;
protected Context mContext; protected Context mContext;
@@ -137,8 +138,8 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
@Override @Override
public boolean isSmartBatterySupported() { public boolean isSmartBatterySupported() {
return mContext.getResources().getBoolean( return mContext.getResources()
com.android.internal.R.bool.config_smart_battery_available); .getBoolean(com.android.internal.R.bool.config_smart_battery_available);
} }
@Override @Override

View File

@@ -36,12 +36,9 @@ public class PowerUsageTimeController extends BasePreferenceController {
private static final String KEY_SCREEN_TIME_PREF = "battery_usage_screen_time"; private static final String KEY_SCREEN_TIME_PREF = "battery_usage_screen_time";
private static final String KEY_BACKGROUND_TIME_PREF = "battery_usage_background_time"; private static final String KEY_BACKGROUND_TIME_PREF = "battery_usage_background_time";
@VisibleForTesting @VisibleForTesting PreferenceCategory mPowerUsageTimeCategory;
PreferenceCategory mPowerUsageTimeCategory; @VisibleForTesting PowerUsageTimePreference mScreenTimePreference;
@VisibleForTesting @VisibleForTesting PowerUsageTimePreference mBackgroundTimePreference;
PowerUsageTimePreference mScreenTimePreference;
@VisibleForTesting
PowerUsageTimePreference mBackgroundTimePreference;
public PowerUsageTimeController(Context context) { public PowerUsageTimeController(Context context) {
super(context, KEY_POWER_USAGE_TIME); super(context, KEY_POWER_USAGE_TIME);
@@ -61,22 +58,37 @@ public class PowerUsageTimeController extends BasePreferenceController {
mPowerUsageTimeCategory.setVisible(false); mPowerUsageTimeCategory.setVisible(false);
} }
void handleScreenTimeUpdated(final String slotTime, void handleScreenTimeUpdated(
final long screenOnTimeInMs, final long backgroundTimeInMs, final String slotTime,
final String anomalyHintPrefKey, final String anomalyHintText) { final long screenOnTimeInMs,
final boolean isShowScreenOnTime = showTimePreference( final long backgroundTimeInMs,
mScreenTimePreference, R.string.power_usage_detail_screen_time, final String anomalyHintPrefKey,
screenOnTimeInMs, anomalyHintPrefKey, anomalyHintText); final String anomalyHintText) {
final boolean isShowBackgroundTime = showTimePreference( final boolean isShowScreenOnTime =
mBackgroundTimePreference, R.string.power_usage_detail_background_time, showTimePreference(
backgroundTimeInMs, anomalyHintPrefKey, anomalyHintText); mScreenTimePreference,
R.string.power_usage_detail_screen_time,
screenOnTimeInMs,
anomalyHintPrefKey,
anomalyHintText);
final boolean isShowBackgroundTime =
showTimePreference(
mBackgroundTimePreference,
R.string.power_usage_detail_background_time,
backgroundTimeInMs,
anomalyHintPrefKey,
anomalyHintText);
if (isShowScreenOnTime || isShowBackgroundTime) { if (isShowScreenOnTime || isShowBackgroundTime) {
showCategoryTitle(slotTime); showCategoryTitle(slotTime);
} }
} }
boolean showTimePreference(PowerUsageTimePreference preference, boolean showTimePreference(
int titleResId, long summaryTimeMs, String anomalyHintKey, String anomalyHintText) { PowerUsageTimePreference preference,
int titleResId,
long summaryTimeMs,
String anomalyHintKey,
String anomalyHintText) {
if (preference == null if (preference == null
|| (summaryTimeMs == 0 && !TextUtils.equals(anomalyHintKey, preference.getKey()))) { || (summaryTimeMs == 0 && !TextUtils.equals(anomalyHintKey, preference.getKey()))) {
return false; return false;
@@ -94,15 +106,19 @@ public class PowerUsageTimeController extends BasePreferenceController {
if (timeInMs < DateUtils.MINUTE_IN_MILLIS) { if (timeInMs < DateUtils.MINUTE_IN_MILLIS) {
return mContext.getString(R.string.power_usage_time_less_than_one_minute); return mContext.getString(R.string.power_usage_time_less_than_one_minute);
} }
return formatElapsedTimeWithoutComma(mContext, (double) timeInMs, return formatElapsedTimeWithoutComma(
/*withSeconds=*/ false, /*collapseTimeUnit=*/ false); mContext,
(double) timeInMs,
/* withSeconds= */ false,
/* collapseTimeUnit= */ false);
} }
@VisibleForTesting @VisibleForTesting
void showCategoryTitle(String slotTimestamp) { void showCategoryTitle(String slotTimestamp) {
mPowerUsageTimeCategory.setTitle(slotTimestamp == null mPowerUsageTimeCategory.setTitle(
? mContext.getString(R.string.battery_app_usage) slotTimestamp == null
: mContext.getString(R.string.battery_app_usage_for, slotTimestamp)); ? mContext.getString(R.string.battery_app_usage)
: mContext.getString(R.string.battery_app_usage_for, slotTimestamp));
mPowerUsageTimeCategory.setVisible(true); mPowerUsageTimeCategory.setVisible(true);
} }
} }

View File

@@ -28,18 +28,13 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
/** /** Custom preference for displaying the app power usage time. */
* Custom preference for displaying the app power usage time.
*/
public class PowerUsageTimePreference extends Preference { public class PowerUsageTimePreference extends Preference {
private static final String TAG = "PowerUsageTimePreference"; private static final String TAG = "PowerUsageTimePreference";
@VisibleForTesting @VisibleForTesting CharSequence mTimeTitle;
CharSequence mTimeTitle; @VisibleForTesting CharSequence mTimeSummary;
@VisibleForTesting @VisibleForTesting CharSequence mAnomalyHintText;
CharSequence mTimeSummary;
@VisibleForTesting
CharSequence mAnomalyHintText;
public PowerUsageTimePreference(Context context, AttributeSet attrs) { public PowerUsageTimePreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);

View File

@@ -31,8 +31,8 @@ import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController; import com.android.internal.app.AlertController;
import com.android.settings.R; import com.android.settings.R;
public class RequestIgnoreBatteryOptimizations extends AlertActivity implements public class RequestIgnoreBatteryOptimizations extends AlertActivity
DialogInterface.OnClickListener { implements DialogInterface.OnClickListener {
private static final String TAG = "RequestIgnoreBatteryOptimizations"; private static final String TAG = "RequestIgnoreBatteryOptimizations";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@@ -42,22 +42,24 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity implements
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addSystemFlags(android.view.WindowManager.LayoutParams getWindow()
.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); .addSystemFlags(
android.view.WindowManager.LayoutParams
.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mPowerWhitelistManager = getSystemService(PowerWhitelistManager.class); mPowerWhitelistManager = getSystemService(PowerWhitelistManager.class);
Uri data = getIntent().getData(); Uri data = getIntent().getData();
if (data == null) { if (data == null) {
debugLog("No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: " debugLog(
+ getIntent()); "No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: " + getIntent());
finish(); finish();
return; return;
} }
mPackageName = data.getSchemeSpecificPart(); mPackageName = data.getSchemeSpecificPart();
if (mPackageName == null) { if (mPackageName == null) {
debugLog("No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: " debugLog(
+ getIntent()); "No data supplied for IGNORE_BATTERY_OPTIMIZATION_SETTINGS in: " + getIntent());
finish(); finish();
return; return;
} }
@@ -69,11 +71,16 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity implements
return; return;
} }
if (getPackageManager().checkPermission( if (getPackageManager()
Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, mPackageName) .checkPermission(
Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
mPackageName)
!= PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
debugLog("Requested package " + mPackageName + " does not hold permission " debugLog(
+ Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); "Requested package "
+ mPackageName
+ " does not hold permission "
+ Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
finish(); finish();
return; return;
} }
@@ -88,9 +95,12 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity implements
} }
final AlertController.AlertParams p = mAlertParams; final AlertController.AlertParams p = mAlertParams;
final CharSequence appLabel = ai.loadSafeLabel(getPackageManager(), final CharSequence appLabel =
PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, PackageItemInfo.SAFE_LABEL_FLAG_TRIM ai.loadSafeLabel(
| PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE); getPackageManager(),
PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
PackageItemInfo.SAFE_LABEL_FLAG_TRIM
| PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
p.mTitle = getText(R.string.high_power_prompt_title); p.mTitle = getText(R.string.high_power_prompt_title);
p.mMessage = getString(R.string.high_power_prompt_body, appLabel); p.mMessage = getString(R.string.high_power_prompt_body, appLabel);
p.mPositiveButtonText = getText(R.string.allow); p.mPositiveButtonText = getText(R.string.allow);

View File

@@ -35,15 +35,11 @@ import com.android.settingslib.utils.StringUtil;
import java.util.List; import java.util.List;
/** /** Controller to change and update the smart battery toggle */
* Controller to change and update the smart battery toggle
*/
public class RestrictAppPreferenceController extends BasePreferenceController { public class RestrictAppPreferenceController extends BasePreferenceController {
@VisibleForTesting @VisibleForTesting static final String KEY_RESTRICT_APP = "restricted_app";
static final String KEY_RESTRICT_APP = "restricted_app";
@VisibleForTesting @VisibleForTesting List<AppInfo> mAppInfos;
List<AppInfo> mAppInfos;
private AppOpsManager mAppOpsManager; private AppOpsManager mAppOpsManager;
private InstrumentedPreferenceFragment mPreferenceFragment; private InstrumentedPreferenceFragment mPreferenceFragment;
private UserManager mUserManager; private UserManager mUserManager;
@@ -65,7 +61,8 @@ public class RestrictAppPreferenceController extends BasePreferenceController {
@Override @Override
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
return mAppInfos.size() > 0 && !mEnableAppBatteryUsagePage ? AVAILABLE return mAppInfos.size() > 0 && !mEnableAppBatteryUsagePage
? AVAILABLE
: CONDITIONALLY_UNAVAILABLE; : CONDITIONALLY_UNAVAILABLE;
} }
@@ -76,17 +73,17 @@ public class RestrictAppPreferenceController extends BasePreferenceController {
final int num = mAppInfos.size(); final int num = mAppInfos.size();
// Fragment change RestrictedAppsList after onPause(), UI needs to be updated in onResume() // Fragment change RestrictedAppsList after onPause(), UI needs to be updated in onResume()
preference.setVisible(num > 0); preference.setVisible(num > 0);
preference.setSummary(StringUtil.getIcuPluralsString(mContext, num, preference.setSummary(
R.string.restricted_app_summary)); StringUtil.getIcuPluralsString(mContext, num, R.string.restricted_app_summary));
} }
@Override @Override
public boolean handlePreferenceTreeClick(Preference preference) { public boolean handlePreferenceTreeClick(Preference preference) {
if (getPreferenceKey().equals(preference.getKey())) { if (getPreferenceKey().equals(preference.getKey())) {
// start fragment // start fragment
RestrictedAppDetails.startRestrictedAppDetails(mPreferenceFragment, RestrictedAppDetails.startRestrictedAppDetails(mPreferenceFragment, mAppInfos);
mAppInfos); FeatureFactory.getFeatureFactory()
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() .getMetricsFeatureProvider()
.action(mContext, SettingsEnums.OPEN_APP_RESTRICTED_LIST); .action(mContext, SettingsEnums.OPEN_APP_RESTRICTED_LIST);
return true; return true;
} }

View File

@@ -52,36 +52,28 @@ import com.android.settingslib.utils.StringUtil;
import java.util.List; import java.util.List;
/** /** Fragment to show a list of anomaly apps, where user could handle these anomalies */
* Fragment to show a list of anomaly apps, where user could handle these anomalies public class RestrictedAppDetails extends DashboardFragment
*/ implements BatteryTipPreferenceController.BatteryTipListener {
public class RestrictedAppDetails extends DashboardFragment implements
BatteryTipPreferenceController.BatteryTipListener {
public static final String TAG = "RestrictedAppDetails"; public static final String TAG = "RestrictedAppDetails";
@VisibleForTesting @VisibleForTesting static final String EXTRA_APP_INFO_LIST = "app_info_list";
static final String EXTRA_APP_INFO_LIST = "app_info_list";
private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list"; private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list";
private static final long TIME_NULL = -1; private static final long TIME_NULL = -1;
@VisibleForTesting @VisibleForTesting List<AppInfo> mAppInfos;
List<AppInfo> mAppInfos; @VisibleForTesting IconDrawableFactory mIconDrawableFactory;
@VisibleForTesting @VisibleForTesting PreferenceGroup mRestrictedAppListGroup;
IconDrawableFactory mIconDrawableFactory; @VisibleForTesting BatteryUtils mBatteryUtils;
@VisibleForTesting @VisibleForTesting PackageManager mPackageManager;
PreferenceGroup mRestrictedAppListGroup; @VisibleForTesting BatteryDatabaseManager mBatteryDatabaseManager;
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
PackageManager mPackageManager;
@VisibleForTesting
BatteryDatabaseManager mBatteryDatabaseManager;
private MetricsFeatureProvider mMetricsFeatureProvider; private MetricsFeatureProvider mMetricsFeatureProvider;
public static void startRestrictedAppDetails(InstrumentedPreferenceFragment fragment, /** Starts restricted app details page */
List<AppInfo> appInfos) { public static void startRestrictedAppDetails(
InstrumentedPreferenceFragment fragment, List<AppInfo> appInfos) {
final Bundle args = new Bundle(); final Bundle args = new Bundle();
args.putParcelableList(EXTRA_APP_INFO_LIST, appInfos); args.putParcelableList(EXTRA_APP_INFO_LIST, appInfos);
@@ -104,8 +96,7 @@ public class RestrictedAppDetails extends DashboardFragment implements
mIconDrawableFactory = IconDrawableFactory.newInstance(context); mIconDrawableFactory = IconDrawableFactory.newInstance(context);
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context); mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context);
mMetricsFeatureProvider = mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
refreshUi(); refreshUi();
} }
@@ -144,39 +135,50 @@ public class RestrictedAppDetails extends DashboardFragment implements
void refreshUi() { void refreshUi() {
mRestrictedAppListGroup.removeAll(); mRestrictedAppListGroup.removeAll();
final Context context = getPrefContext(); final Context context = getPrefContext();
final SparseLongArray timestampArray = mBatteryDatabaseManager final SparseLongArray timestampArray =
.queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION); mBatteryDatabaseManager.queryActionTime(
AnomalyDatabaseHelper.ActionType.RESTRICTION);
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
for (int i = 0, size = mAppInfos.size(); i < size; i++) { for (int i = 0, size = mAppInfos.size(); i < size; i++) {
final CheckBoxPreference checkBoxPreference = new AppCheckBoxPreference(context); final CheckBoxPreference checkBoxPreference = new AppCheckBoxPreference(context);
final AppInfo appInfo = mAppInfos.get(i); final AppInfo appInfo = mAppInfos.get(i);
try { try {
final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfoAsUser( final ApplicationInfo applicationInfo =
appInfo.packageName, 0 /* flags */, UserHandle.getUserId(appInfo.uid)); mPackageManager.getApplicationInfoAsUser(
appInfo.packageName,
0 /* flags */,
UserHandle.getUserId(appInfo.uid));
checkBoxPreference.setChecked( checkBoxPreference.setChecked(
mBatteryUtils.isForceAppStandbyEnabled(appInfo.uid, appInfo.packageName)); mBatteryUtils.isForceAppStandbyEnabled(appInfo.uid, appInfo.packageName));
checkBoxPreference.setTitle(mPackageManager.getApplicationLabel(applicationInfo)); checkBoxPreference.setTitle(mPackageManager.getApplicationLabel(applicationInfo));
checkBoxPreference.setIcon( checkBoxPreference.setIcon(
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, Utils.getBadgedIcon(
mIconDrawableFactory,
mPackageManager,
appInfo.packageName, appInfo.packageName,
UserHandle.getUserId(appInfo.uid))); UserHandle.getUserId(appInfo.uid)));
checkBoxPreference.setKey(getKeyFromAppInfo(appInfo)); checkBoxPreference.setKey(getKeyFromAppInfo(appInfo));
checkBoxPreference.setOnPreferenceChangeListener((pref, value) -> { checkBoxPreference.setOnPreferenceChangeListener(
final BatteryTipDialogFragment fragment = createDialogFragment(appInfo, (pref, value) -> {
(Boolean) value); final BatteryTipDialogFragment fragment =
fragment.setTargetFragment(this, 0 /* requestCode */); createDialogFragment(appInfo, (Boolean) value);
fragment.show(getFragmentManager(), TAG); fragment.setTargetFragment(this, 0 /* requestCode */);
mMetricsFeatureProvider.action(getContext(), fragment.show(getFragmentManager(), TAG);
SettingsEnums.ACTION_APP_RESTRICTED_LIST_UNCHECKED, mMetricsFeatureProvider.action(
appInfo.packageName); getContext(),
return false; SettingsEnums.ACTION_APP_RESTRICTED_LIST_UNCHECKED,
}); appInfo.packageName);
return false;
});
final long timestamp = timestampArray.get(appInfo.uid, TIME_NULL); final long timestamp = timestampArray.get(appInfo.uid, TIME_NULL);
if (timestamp != TIME_NULL) { if (timestamp != TIME_NULL) {
checkBoxPreference.setSummary(getString(R.string.restricted_app_time_summary, checkBoxPreference.setSummary(
StringUtil.formatRelativeTime(context, now - timestamp, false))); getString(
R.string.restricted_app_time_summary,
StringUtil.formatRelativeTime(
context, now - timestamp, false)));
} }
final CharSequence test = checkBoxPreference.getSummaryOn(); final CharSequence test = checkBoxPreference.getSummaryOn();
mRestrictedAppListGroup.addPreference(checkBoxPreference); mRestrictedAppListGroup.addPreference(checkBoxPreference);
@@ -196,8 +198,9 @@ public class RestrictedAppDetails extends DashboardFragment implements
appInfo = ((UnrestrictAppTip) batteryTip).getUnrestrictAppInfo(); appInfo = ((UnrestrictAppTip) batteryTip).getUnrestrictAppInfo();
} }
CheckBoxPreference preference = (CheckBoxPreference) mRestrictedAppListGroup CheckBoxPreference preference =
.findPreference(getKeyFromAppInfo(appInfo)); (CheckBoxPreference)
mRestrictedAppListGroup.findPreference(getKeyFromAppInfo(appInfo));
if (preference != null) { if (preference != null) {
preference.setChecked(isRestricted); preference.setChecked(isRestricted);
} }
@@ -205,12 +208,12 @@ public class RestrictedAppDetails extends DashboardFragment implements
@VisibleForTesting @VisibleForTesting
BatteryTipDialogFragment createDialogFragment(AppInfo appInfo, boolean toRestrict) { BatteryTipDialogFragment createDialogFragment(AppInfo appInfo, boolean toRestrict) {
final BatteryTip batteryTip = toRestrict final BatteryTip batteryTip =
? new RestrictAppTip(BatteryTip.StateType.NEW, appInfo) toRestrict
: new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo); ? new RestrictAppTip(BatteryTip.StateType.NEW, appInfo)
: new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
return BatteryTipDialogFragment.newInstance( return BatteryTipDialogFragment.newInstance(batteryTip, getMetricsCategory());
batteryTip, getMetricsCategory());
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -28,11 +28,9 @@ import com.android.settings.R;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
/** /** Controller to change and update the smart battery toggle */
* Controller to change and update the smart battery toggle public class SmartBatteryPreferenceController extends BasePreferenceController
*/ implements Preference.OnPreferenceChangeListener {
public class SmartBatteryPreferenceController extends BasePreferenceController implements
Preference.OnPreferenceChangeListener {
private static final String KEY_SMART_BATTERY = "smart_battery"; private static final String KEY_SMART_BATTERY = "smart_battery";
private static final int ON = 1; private static final int ON = 1;
@@ -70,16 +68,22 @@ public class SmartBatteryPreferenceController extends BasePreferenceController i
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
super.updateState(preference); super.updateState(preference);
final boolean smartBatteryOn = Settings.Global.getInt(mContext.getContentResolver(), final boolean smartBatteryOn =
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON) == ON; Settings.Global.getInt(
mContext.getContentResolver(),
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
ON)
== ON;
((TwoStatePreference) preference).setChecked(smartBatteryOn); ((TwoStatePreference) preference).setChecked(smartBatteryOn);
} }
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean smartBatteryOn = (Boolean) newValue; final boolean smartBatteryOn = (Boolean) newValue;
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, smartBatteryOn ? ON : OFF); mContext.getContentResolver(),
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
smartBatteryOn ? ON : OFF);
return true; return true;
} }
} }

View File

@@ -32,9 +32,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/** /** Fragment to show smart battery and restricted app controls */
* Fragment to show smart battery and restricted app controls
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class SmartBatterySettings extends DashboardFragment { public class SmartBatterySettings extends DashboardFragment {
public static final String TAG = "SmartBatterySettings"; public static final String TAG = "SmartBatterySettings";
@@ -65,12 +63,12 @@ public class SmartBatterySettings extends DashboardFragment {
} }
private static List<AbstractPreferenceController> buildPreferenceControllers( private static List<AbstractPreferenceController> buildPreferenceControllers(
Context context, SettingsActivity settingsActivity, Context context,
SettingsActivity settingsActivity,
InstrumentedPreferenceFragment fragment) { InstrumentedPreferenceFragment fragment) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();
if (settingsActivity != null && fragment != null) { if (settingsActivity != null && fragment != null) {
controllers.add( controllers.add(new RestrictAppPreferenceController(fragment));
new RestrictAppPreferenceController(fragment));
} else { } else {
controllers.add(new RestrictAppPreferenceController(context)); controllers.add(new RestrictAppPreferenceController(context));
} }

View File

@@ -34,15 +34,13 @@ import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop; import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
public class TopLevelBatteryPreferenceController extends BasePreferenceController implements public class TopLevelBatteryPreferenceController extends BasePreferenceController
LifecycleObserver, OnStart, OnStop, BatteryPreferenceController { implements LifecycleObserver, OnStart, OnStop, BatteryPreferenceController {
private static final String TAG = "TopLvBatteryPrefControl"; private static final String TAG = "TopLvBatteryPrefControl";
@VisibleForTesting @VisibleForTesting Preference mPreference;
Preference mPreference; @VisibleForTesting protected boolean mIsBatteryPresent = true;
@VisibleForTesting
protected boolean mIsBatteryPresent = true;
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver; private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
@@ -53,28 +51,33 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
public TopLevelBatteryPreferenceController(Context context, String preferenceKey) { public TopLevelBatteryPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext); mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
mBatteryBroadcastReceiver.setBatteryChangedListener(type -> { mBatteryBroadcastReceiver.setBatteryChangedListener(
Log.d(TAG, "onBatteryChanged: type=" + type); type -> {
if (type == BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_NOT_PRESENT) { Log.d(TAG, "onBatteryChanged: type=" + type);
mIsBatteryPresent = false; if (type == BatteryBroadcastReceiver.BatteryUpdateType.BATTERY_NOT_PRESENT) {
} mIsBatteryPresent = false;
BatteryInfo.getBatteryInfo(mContext, info -> { }
Log.d(TAG, "getBatteryInfo: " + info); BatteryInfo.getBatteryInfo(
mBatteryInfo = info; mContext,
updateState(mPreference); info -> {
// Update the preference summary text to the latest state. Log.d(TAG, "getBatteryInfo: " + info);
setSummaryAsync(info); mBatteryInfo = info;
}, true /* shortString */); updateState(mPreference);
}); // Update the preference summary text to the latest state.
setSummaryAsync(info);
},
true /* shortString */);
});
mBatteryStatusFeatureProvider = FeatureFactory.getFeatureFactory() mBatteryStatusFeatureProvider =
.getBatteryStatusFeatureProvider(); FeatureFactory.getFeatureFactory().getBatteryStatusFeatureProvider();
} }
@Override @Override
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(R.bool.config_show_top_level_battery) return mContext.getResources().getBoolean(R.bool.config_show_top_level_battery)
? AVAILABLE : UNSUPPORTED_ON_DEVICE; ? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
} }
@Override @Override
@@ -106,13 +109,17 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
return getDashboardLabel(mContext, mBatteryInfo, batteryStatusUpdate); return getDashboardLabel(mContext, mBatteryInfo, batteryStatusUpdate);
} }
protected CharSequence getDashboardLabel(Context context, BatteryInfo info, protected CharSequence getDashboardLabel(
boolean batteryStatusUpdate) { Context context, BatteryInfo info, boolean batteryStatusUpdate) {
if (info == null || context == null) { if (info == null || context == null) {
return null; return null;
} }
Log.d(TAG, "getDashboardLabel: " + mBatteryStatusLabel + " batteryStatusUpdate=" Log.d(
+ batteryStatusUpdate); TAG,
"getDashboardLabel: "
+ mBatteryStatusLabel
+ " batteryStatusUpdate="
+ batteryStatusUpdate);
if (batteryStatusUpdate) { if (batteryStatusUpdate) {
setSummaryAsync(info); setSummaryAsync(info);
@@ -121,19 +128,24 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
} }
private void setSummaryAsync(BatteryInfo info) { private void setSummaryAsync(BatteryInfo info) {
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(
// Return false if built-in status should be used, will use updateBatteryStatus() () -> {
// method to inject the customized battery status label. // Return false if built-in status should be used, will use
final boolean triggerBatteryStatusUpdate = // updateBatteryStatus()
mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info); // method to inject the customized battery status label.
ThreadUtils.postOnMainThread(() -> { final boolean triggerBatteryStatusUpdate =
if (!triggerBatteryStatusUpdate) { mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info);
mBatteryStatusLabel = null; // will generateLabel() ThreadUtils.postOnMainThread(
} () -> {
mPreference.setSummary( if (!triggerBatteryStatusUpdate) {
mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel); mBatteryStatusLabel = null; // will generateLabel()
}); }
}); mPreference.setSummary(
mBatteryStatusLabel == null
? generateLabel(info)
: mBatteryStatusLabel);
});
});
} }
private CharSequence generateLabel(BatteryInfo info) { private CharSequence generateLabel(BatteryInfo info) {
@@ -156,9 +168,7 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
} }
} }
/** /** Callback which receives text for the label. */
* Callback which receives text for the label.
*/
@Override @Override
public void updateBatteryStatus(String label, BatteryInfo info) { public void updateBatteryStatus(String label, BatteryInfo info) {
mBatteryStatusLabel = label; // Null if adaptive charging is not active mBatteryStatusLabel = label; // Null if adaptive charging is not active

View File

@@ -30,8 +30,7 @@ public class UnrestrictedPreferenceController extends AbstractPreferenceControll
private static final String TAG = "UNRESTRICTED_PREF"; private static final String TAG = "UNRESTRICTED_PREF";
@VisibleForTesting @VisibleForTesting static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference";
static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference";
@VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils;
@@ -44,8 +43,9 @@ public class UnrestrictedPreferenceController extends AbstractPreferenceControll
public void updateState(Preference preference) { public void updateState(Preference preference) {
preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled());
final boolean isUnrestricted = mBatteryOptimizeUtils.getAppOptimizationMode() final boolean isUnrestricted =
== BatteryOptimizeUtils.MODE_UNRESTRICTED; mBatteryOptimizeUtils.getAppOptimizationMode()
== BatteryOptimizeUtils.MODE_UNRESTRICTED;
((SelectorWithWidgetPreference) preference).setChecked(isUnrestricted); ((SelectorWithWidgetPreference) preference).setChecked(isUnrestricted);
} }

View File

@@ -37,12 +37,9 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.widget.MainSwitchPreference; import com.android.settingslib.widget.MainSwitchPreference;
/** /** Controller to update the battery saver button */
* Controller to update the battery saver button public class BatterySaverButtonPreferenceController extends TogglePreferenceController
*/ implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
public class BatterySaverButtonPreferenceController extends
TogglePreferenceController implements LifecycleObserver, OnStart, OnStop,
BatterySaverReceiver.BatterySaverListener {
private static final long SWITCH_ANIMATION_DURATION = 350L; private static final long SWITCH_ANIMATION_DURATION = 350L;
private final BatterySaverReceiver mBatterySaverReceiver; private final BatterySaverReceiver mBatterySaverReceiver;
@@ -104,8 +101,8 @@ public class BatterySaverButtonPreferenceController extends
@Override @Override
public boolean setChecked(boolean stateOn) { public boolean setChecked(boolean stateOn) {
return BatterySaverUtils.setPowerSaveMode(mContext, stateOn, return BatterySaverUtils.setPowerSaveMode(
false /* needFirstTimeWarning */, SAVER_ENABLED_SETTINGS); mContext, stateOn, false /* needFirstTimeWarning */, SAVER_ENABLED_SETTINGS);
} }
@Override @Override
@@ -115,8 +112,7 @@ public class BatterySaverButtonPreferenceController extends
@Override @Override
public void onPowerSaveModeChanged() { public void onPowerSaveModeChanged() {
mHandler.postDelayed(() -> onPowerSaveModeChangedInternal(), mHandler.postDelayed(() -> onPowerSaveModeChangedInternal(), SWITCH_ANIMATION_DURATION);
SWITCH_ANIMATION_DURATION);
} }
private void onPowerSaveModeChangedInternal() { private void onPowerSaveModeChangedInternal() {

View File

@@ -32,17 +32,14 @@ import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.BatterySaverUtils;
/** /**
* Simple controller to navigate users to the scheduling page from * Simple controller to navigate users to the scheduling page from "Settings > Battery > Battery
* "Settings > Battery > Battery Saver". Also updates the summary for preference based on * Saver". Also updates the summary for preference based on the currently selected settings.
* the currently selected settings.
*/ */
public class BatterySaverSchedulePreferenceController extends BasePreferenceController { public class BatterySaverSchedulePreferenceController extends BasePreferenceController {
@VisibleForTesting @VisibleForTesting Preference mBatterySaverSchedulePreference;
Preference mBatterySaverSchedulePreference;
public static final String KEY_BATTERY_SAVER_SCHEDULE = "battery_saver_schedule"; public static final String KEY_BATTERY_SAVER_SCHEDULE = "battery_saver_schedule";
public BatterySaverSchedulePreferenceController(Context context) { public BatterySaverSchedulePreferenceController(Context context) {
super(context, KEY_BATTERY_SAVER_SCHEDULE); super(context, KEY_BATTERY_SAVER_SCHEDULE);
BatterySaverUtils.revertScheduleToNoneIfNeeded(context); BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
@@ -66,7 +63,8 @@ public class BatterySaverSchedulePreferenceController extends BasePreferenceCont
if (KEY_PERCENTAGE.equals(mode)) { if (KEY_PERCENTAGE.equals(mode)) {
final int threshold = final int threshold =
Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); Settings.Global.getInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
return mContext.getString(R.string.battery_saver_auto_percentage_summary, return mContext.getString(
R.string.battery_saver_auto_percentage_summary,
Utils.formatPercentage(threshold)); Utils.formatPercentage(threshold));
} }
return mContext.getText(R.string.battery_saver_auto_no_schedule); return mContext.getText(R.string.battery_saver_auto_no_schedule);

View File

@@ -31,12 +31,12 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils;
/** /**
* Responds to user actions in the Settings > Battery > Set a Schedule Screen * Responds to user actions in the Settings > Battery > Set a Schedule Screen
* *
* Note that this is not a preference controller since that screen does not inherit from * <p>Note that this is not a preference controller since that screen does not inherit from
* DashboardFragment. * DashboardFragment.
* *
* Will call the appropriate power manager APIs and modify the correct settings to enable * <p>Will call the appropriate power manager APIs and modify the correct settings to enable users
* users to control their automatic battery saver toggling preferences. * to control their automatic battery saver toggling preferences. See {@link
* See {@link Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details. * Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details.
*/ */
public class BatterySaverScheduleRadioButtonsController { public class BatterySaverScheduleRadioButtonsController {
private static final String TAG = "BatterySaverScheduleRadioButtonsController"; private static final String TAG = "BatterySaverScheduleRadioButtonsController";
@@ -46,8 +46,8 @@ public class BatterySaverScheduleRadioButtonsController {
private Context mContext; private Context mContext;
private BatterySaverScheduleSeekBarController mSeekBarController; private BatterySaverScheduleSeekBarController mSeekBarController;
public BatterySaverScheduleRadioButtonsController(Context context, public BatterySaverScheduleRadioButtonsController(
BatterySaverScheduleSeekBarController seekbar) { Context context, BatterySaverScheduleSeekBarController seekbar) {
mContext = context; mContext = context;
mSeekBarController = seekbar; mSeekBarController = seekbar;
} }
@@ -67,10 +67,11 @@ public class BatterySaverScheduleRadioButtonsController {
case KEY_PERCENTAGE: case KEY_PERCENTAGE:
triggerLevel = TRIGGER_LEVEL_MIN; triggerLevel = TRIGGER_LEVEL_MIN;
confirmationExtras.putBoolean(BatterySaverUtils.EXTRA_CONFIRM_TEXT_ONLY, true); confirmationExtras.putBoolean(BatterySaverUtils.EXTRA_CONFIRM_TEXT_ONLY, true);
confirmationExtras.putInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER, confirmationExtras.putInt(
BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER,
PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
confirmationExtras.putInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL, confirmationExtras.putInt(
triggerLevel); BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL, triggerLevel);
break; break;
default: default:
throw new IllegalStateException( throw new IllegalStateException(
@@ -79,7 +80,7 @@ public class BatterySaverScheduleRadioButtonsController {
if (!TextUtils.equals(key, KEY_NO_SCHEDULE) if (!TextUtils.equals(key, KEY_NO_SCHEDULE)
&& BatterySaverUtils.maybeShowBatterySaverConfirmation( && BatterySaverUtils.maybeShowBatterySaverConfirmation(
mContext, confirmationExtras)) { mContext, confirmationExtras)) {
// reset this if we need to show the confirmation message // reset this if we need to show the confirmation message
mode = PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE; mode = PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE;
triggerLevel = 0; triggerLevel = 0;

View File

@@ -35,30 +35,28 @@ import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.fuelgauge.BatterySaverUtils;
/** /**
* Responds to user actions in the Settings > Battery > Set a Schedule Screen for the seekbar. * Responds to user actions in the Settings > Battery > Set a Schedule Screen for the seekbar. Note
* Note that this seekbar is only visible when the radio button selected is "Percentage". * that this seekbar is only visible when the radio button selected is "Percentage".
* *
* Note that this is not a preference controller since that screen does not inherit from * <p>Note that this is not a preference controller since that screen does not inherit from
* DashboardFragment. * DashboardFragment.
* *
* Will call the appropriate power manager APIs and modify the correct settings to enable * <p>Will call the appropriate power manager APIs and modify the correct settings to enable users
* users to control their automatic battery saver toggling preferences. * to control their automatic battery saver toggling preferences. See {@link
* See {@link Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details. * Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details.
*/ */
public class BatterySaverScheduleSeekBarController implements public class BatterySaverScheduleSeekBarController
OnPreferenceChangeListener, OnSeekBarChangeListener { implements OnPreferenceChangeListener, OnSeekBarChangeListener {
public static final int MAX_SEEKBAR_VALUE = 15; public static final int MAX_SEEKBAR_VALUE = 15;
public static final int MIN_SEEKBAR_VALUE = 2; public static final int MIN_SEEKBAR_VALUE = 2;
public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar"; public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar";
private static final int LEVEL_UNIT_SCALE = 5; private static final int LEVEL_UNIT_SCALE = 5;
@VisibleForTesting @VisibleForTesting public SeekBarPreference mSeekBarPreference;
public SeekBarPreference mSeekBarPreference;
private Context mContext; private Context mContext;
@VisibleForTesting @VisibleForTesting int mPercentage;
int mPercentage;
public BatterySaverScheduleSeekBarController(Context context) { public BatterySaverScheduleSeekBarController(Context context) {
mContext = context; mContext = context;
@@ -93,7 +91,8 @@ public class BatterySaverScheduleSeekBarController implements
@Override @Override
public void onStopTrackingTouch(SeekBar seekBar) { public void onStopTrackingTouch(SeekBar seekBar) {
if (mPercentage > 0) { if (mPercentage > 0) {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(
mContext.getContentResolver(),
Global.LOW_POWER_MODE_TRIGGER_LEVEL, Global.LOW_POWER_MODE_TRIGGER_LEVEL,
mPercentage); mPercentage);
} }
@@ -108,8 +107,7 @@ public class BatterySaverScheduleSeekBarController implements
final int currentSeekbarValue = Math.max(threshold / 5, MIN_SEEKBAR_VALUE); final int currentSeekbarValue = Math.max(threshold / 5, MIN_SEEKBAR_VALUE);
mSeekBarPreference.setVisible(true); mSeekBarPreference.setVisible(true);
mSeekBarPreference.setProgress(currentSeekbarValue); mSeekBarPreference.setProgress(currentSeekbarValue);
final CharSequence stateDescription = formatStateDescription( final CharSequence stateDescription = formatStateDescription(currentSeekbarValue * 5);
currentSeekbarValue * 5);
mSeekBarPreference.setTitle(stateDescription); mSeekBarPreference.setTitle(stateDescription);
mSeekBarPreference.overrideSeekBarStateDescription(stateDescription); mSeekBarPreference.overrideSeekBarStateDescription(stateDescription);
} else { } else {
@@ -130,7 +128,7 @@ public class BatterySaverScheduleSeekBarController implements
} }
private CharSequence formatStateDescription(int percentage) { private CharSequence formatStateDescription(int percentage) {
return mContext.getString(R.string.battery_saver_seekbar_title, return mContext.getString(
Utils.formatPercentage(percentage)); R.string.battery_saver_seekbar_title, Utils.formatPercentage(percentage));
} }
} }

View File

@@ -49,32 +49,32 @@ import com.google.common.collect.Lists;
import java.util.List; import java.util.List;
/** /**
* Fragment that allows users to customize their automatic battery saver mode settings. * Fragment that allows users to customize their automatic battery saver mode settings. <br>
* * <br>
* Location: Settings > Battery > Battery Saver > Set a Schedule * Location: Settings > Battery > Battery Saver > Set a Schedule <br>
* See {@link BatterySaverSchedulePreferenceController} for the controller that manages navigation * See {@link BatterySaverSchedulePreferenceController} for the controller that manages navigation
* to this screen from "Settings > Battery > Battery Saver" and the summary. * to this screen from "Settings > Battery > Battery Saver" and the summary. <br>
* See {@link BatterySaverScheduleRadioButtonsController} & * See {@link BatterySaverScheduleRadioButtonsController} & {@link
* {@link BatterySaverScheduleSeekBarController} for the controller that manages user * BatterySaverScheduleSeekBarController} for the controller that manages user interactions in this
* interactions in this screen. * screen.
*/ */
public class BatterySaverScheduleSettings extends RadioButtonPickerFragment { public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
public BatterySaverScheduleRadioButtonsController mRadioButtonController; public BatterySaverScheduleRadioButtonsController mRadioButtonController;
@VisibleForTesting @VisibleForTesting Context mContext;
Context mContext;
private int mSaverPercentage; private int mSaverPercentage;
private String mSaverScheduleKey; private String mSaverScheduleKey;
private BatterySaverScheduleSeekBarController mSeekBarController; private BatterySaverScheduleSeekBarController mSeekBarController;
@VisibleForTesting @VisibleForTesting
final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { final ContentObserver mSettingsObserver =
@Override new ContentObserver(new Handler()) {
public void onChange(boolean selfChange, Uri uri) { @Override
getPreferenceScreen().removeAll(); public void onChange(boolean selfChange, Uri uri) {
updateCandidates(); getPreferenceScreen().removeAll();
} updateCandidates();
}; }
};
@Override @Override
protected int getPreferenceScreenResId() { protected int getPreferenceScreenResId() {
@@ -85,18 +85,19 @@ public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
mSeekBarController = new BatterySaverScheduleSeekBarController(context); mSeekBarController = new BatterySaverScheduleSeekBarController(context);
mRadioButtonController = new BatterySaverScheduleRadioButtonsController( mRadioButtonController =
context, mSeekBarController); new BatterySaverScheduleRadioButtonsController(context, mSeekBarController);
mContext = context; mContext = context;
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
mContext.getContentResolver().registerContentObserver( mContext.getContentResolver()
Settings.Secure.getUriFor(Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED), .registerContentObserver(
false, Settings.Secure.getUriFor(Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED),
mSettingsObserver); false,
mSettingsObserver);
mSaverScheduleKey = BatterySaverUtils.getBatterySaverScheduleKey(mContext); mSaverScheduleKey = BatterySaverUtils.getBatterySaverScheduleKey(mContext);
mSaverPercentage = getSaverPercentage(); mSaverPercentage = getSaverPercentage();
} }
@@ -124,24 +125,30 @@ public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
protected List<? extends CandidateInfo> getCandidates() { protected List<? extends CandidateInfo> getCandidates() {
Context context = getContext(); Context context = getContext();
List<CandidateInfo> candidates = Lists.newArrayList(); List<CandidateInfo> candidates = Lists.newArrayList();
candidates.add(new BatterySaverScheduleCandidateInfo( candidates.add(
context.getText(R.string.battery_saver_auto_no_schedule), new BatterySaverScheduleCandidateInfo(
/* summary */ null, context.getText(R.string.battery_saver_auto_no_schedule),
KEY_NO_SCHEDULE, /* summary */ null,
/* enabled */ true)); KEY_NO_SCHEDULE,
/* enabled */ true));
BatterySaverUtils.revertScheduleToNoneIfNeeded(context); BatterySaverUtils.revertScheduleToNoneIfNeeded(context);
candidates.add(new BatterySaverScheduleCandidateInfo( candidates.add(
context.getText(R.string.battery_saver_auto_percentage), new BatterySaverScheduleCandidateInfo(
/* summary */ null, context.getText(R.string.battery_saver_auto_percentage),
KEY_PERCENTAGE, /* summary */ null,
/* enabled */ true)); KEY_PERCENTAGE,
/* enabled */ true));
return candidates; return candidates;
} }
@Override @Override
public void bindPreferenceExtra(SelectorWithWidgetPreference pref, String key, public void bindPreferenceExtra(
CandidateInfo info, String defaultKey, String systemDefaultKey) { SelectorWithWidgetPreference pref,
String key,
CandidateInfo info,
String defaultKey,
String systemDefaultKey) {
final BatterySaverScheduleCandidateInfo candidateInfo = final BatterySaverScheduleCandidateInfo candidateInfo =
(BatterySaverScheduleCandidateInfo) info; (BatterySaverScheduleCandidateInfo) info;
final CharSequence summary = candidateInfo.getSummary(); final CharSequence summary = candidateInfo.getSummary();
@@ -174,14 +181,16 @@ public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
private void logPowerSaver() { private void logPowerSaver() {
final int currentSaverPercentage = getSaverPercentage(); final int currentSaverPercentage = getSaverPercentage();
final String currentSaverScheduleKey = BatterySaverUtils.getBatterySaverScheduleKey( final String currentSaverScheduleKey =
mContext); BatterySaverUtils.getBatterySaverScheduleKey(mContext);
if (mSaverScheduleKey.equals(currentSaverScheduleKey) if (mSaverScheduleKey.equals(currentSaverScheduleKey)
&& mSaverPercentage == currentSaverPercentage) { && mSaverPercentage == currentSaverPercentage) {
return; return;
} }
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() FeatureFactory.getFeatureFactory()
.action(SettingsEnums.FUELGAUGE_BATTERY_SAVER, .getMetricsFeatureProvider()
.action(
SettingsEnums.FUELGAUGE_BATTERY_SAVER,
SettingsEnums.FIELD_BATTERY_SAVER_SCHEDULE_TYPE, SettingsEnums.FIELD_BATTERY_SAVER_SCHEDULE_TYPE,
SettingsEnums.FIELD_BATTERY_SAVER_PERCENTAGE_VALUE, SettingsEnums.FIELD_BATTERY_SAVER_PERCENTAGE_VALUE,
currentSaverScheduleKey, currentSaverScheduleKey,
@@ -189,8 +198,8 @@ public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
} }
private int getSaverPercentage() { private int getSaverPercentage() {
return Settings.Global.getInt(mContext.getContentResolver(), return Settings.Global.getInt(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1); mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1);
} }
static class BatterySaverScheduleCandidateInfo extends CandidateInfo { static class BatterySaverScheduleCandidateInfo extends CandidateInfo {
@@ -199,8 +208,8 @@ public class BatterySaverScheduleSettings extends RadioButtonPickerFragment {
private final CharSequence mSummary; private final CharSequence mSummary;
private final String mKey; private final String mKey;
BatterySaverScheduleCandidateInfo(CharSequence label, CharSequence summary, String key, BatterySaverScheduleCandidateInfo(
boolean enabled) { CharSequence label, CharSequence summary, String key, boolean enabled) {
super(enabled); super(enabled);
mLabel = label; mLabel = label;
mKey = key; mKey = key;

View File

@@ -28,9 +28,7 @@ import com.android.settingslib.HelpUtils;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.FooterPreference;
/** /** Battery saver settings page */
* Battery saver settings page
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class BatterySaverSettings extends DashboardFragment { public class BatterySaverSettings extends DashboardFragment {
private static final String TAG = "BatterySaverSettings"; private static final String TAG = "BatterySaverSettings";
@@ -63,9 +61,7 @@ public class BatterySaverSettings extends DashboardFragment {
return R.string.help_url_battery_saver_settings; return R.string.help_url_battery_saver_settings;
} }
/** /** For Search. */
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.battery_saver_settings); new BaseSearchIndexProvider(R.xml.battery_saver_settings);
@@ -83,13 +79,17 @@ public class BatterySaverSettings extends DashboardFragment {
void addHelpLink() { void addHelpLink() {
FooterPreference pref = getPreferenceScreen().findPreference(KEY_FOOTER_PREFERENCE); FooterPreference pref = getPreferenceScreen().findPreference(KEY_FOOTER_PREFERENCE);
if (pref != null) { if (pref != null) {
pref.setLearnMoreAction(v -> { pref.setLearnMoreAction(
mMetricsFeatureProvider.action(getContext(), v -> {
SettingsEnums.ACTION_APP_BATTERY_LEARN_MORE); mMetricsFeatureProvider.action(
startActivityForResult(HelpUtils.getHelpIntent(getContext(), getContext(), SettingsEnums.ACTION_APP_BATTERY_LEARN_MORE);
getString(R.string.help_url_battery_saver_settings), startActivityForResult(
/*backupContext=*/ ""), /*requestCode=*/ 0); HelpUtils.getHelpIntent(
}); getContext(),
getString(R.string.help_url_battery_saver_settings),
/* backupContext= */ ""),
/* requestCode= */ 0);
});
pref.setLearnMoreText(getString(R.string.battery_saver_link_a11y)); pref.setLearnMoreText(getString(R.string.battery_saver_link_a11y));
} }
} }

View File

@@ -12,8 +12,8 @@ import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.TogglePreferenceController; import com.android.settings.core.TogglePreferenceController;
public class BatterySaverStickyPreferenceController extends TogglePreferenceController implements public class BatterySaverStickyPreferenceController extends TogglePreferenceController
PreferenceControllerMixin, Preference.OnPreferenceChangeListener { implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final int DEFAULT_STICKY_SHUTOFF_LEVEL = 90; private static final int DEFAULT_STICKY_SHUTOFF_LEVEL = 90;
private Context mContext; private Context mContext;
@@ -25,34 +25,46 @@ public class BatterySaverStickyPreferenceController extends TogglePreferenceCont
@Override @Override
public boolean isChecked() { public boolean isChecked() {
return Settings.Global.getInt(mContext.getContentResolver(), return Settings.Global.getInt(
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1) == 1; mContext.getContentResolver(),
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED,
1)
== 1;
} }
@Override @Override
public boolean setChecked(boolean isChecked) { public boolean setChecked(boolean isChecked) {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, mContext.getContentResolver(),
isChecked ? 1 : 0); Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED,
isChecked ? 1 : 0);
return true; return true;
} }
@Override @Override
protected void refreshSummary(Preference preference) { protected void refreshSummary(Preference preference) {
super.refreshSummary(preference); super.refreshSummary(preference);
final int stickyShutoffLevel = Settings.Global.getInt(mContext.getContentResolver(), final int stickyShutoffLevel =
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, DEFAULT_STICKY_SHUTOFF_LEVEL); Settings.Global.getInt(
mContext.getContentResolver(),
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL,
DEFAULT_STICKY_SHUTOFF_LEVEL);
final String formatPercentage = Utils.formatPercentage(stickyShutoffLevel); final String formatPercentage = Utils.formatPercentage(stickyShutoffLevel);
preference.setTitle(mContext.getString(R.string.battery_saver_sticky_title_percentage, preference.setTitle(
formatPercentage)); mContext.getString(
preference.setSummary(mContext.getString(R.string.battery_saver_sticky_description_new, R.string.battery_saver_sticky_title_percentage, formatPercentage));
formatPercentage)); preference.setSummary(
mContext.getString(
R.string.battery_saver_sticky_description_new, formatPercentage));
} }
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
int setting = Settings.Global.getInt(mContext.getContentResolver(), int setting =
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); Settings.Global.getInt(
mContext.getContentResolver(),
Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED,
1);
((TwoStatePreference) preference).setChecked(setting == 1); ((TwoStatePreference) preference).setChecked(setting == 1);
refreshSummary(preference); refreshSummary(preference);

View File

@@ -35,8 +35,7 @@ import java.util.concurrent.TimeUnit;
public class AnomalyCleanupJobService extends JobService { public class AnomalyCleanupJobService extends JobService {
private static final String TAG = "AnomalyCleanUpJobService"; private static final String TAG = "AnomalyCleanUpJobService";
@VisibleForTesting @VisibleForTesting static final long CLEAN_UP_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);
static final long CLEAN_UP_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);
public static void scheduleCleanUp(Context context) { public static void scheduleCleanUp(Context context) {
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
@@ -52,23 +51,24 @@ public class AnomalyCleanupJobService extends JobService {
// Don't schedule it if it already exists, to make sure it runs periodically even after // Don't schedule it if it already exists, to make sure it runs periodically even after
// reboot // reboot
if (pending == null && jobScheduler.schedule(jobBuilder.build()) if (pending == null
!= JobScheduler.RESULT_SUCCESS) { && jobScheduler.schedule(jobBuilder.build()) != JobScheduler.RESULT_SUCCESS) {
Log.i(TAG, "Anomaly clean up job service schedule failed."); Log.i(TAG, "Anomaly clean up job service schedule failed.");
} }
} }
@Override @Override
public boolean onStartJob(JobParameters params) { public boolean onStartJob(JobParameters params) {
final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager final BatteryDatabaseManager batteryDatabaseManager =
.getInstance(this); BatteryDatabaseManager.getInstance(this);
final BatteryTipPolicy policy = new BatteryTipPolicy(this); final BatteryTipPolicy policy = new BatteryTipPolicy(this);
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(
batteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp( () -> {
System.currentTimeMillis() - TimeUnit.DAYS.toMillis( batteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp(
policy.dataHistoryRetainDay)); System.currentTimeMillis()
jobFinished(params, false /* wantsReschedule */); - TimeUnit.DAYS.toMillis(policy.dataHistoryRetainDay));
}); jobFinished(params, false /* wantsReschedule */);
});
return true; return true;
} }

View File

@@ -44,8 +44,7 @@ public class AnomalyConfigJobService extends JobService {
public static final String KEY_ANOMALY_CONFIG_VERSION = "anomaly_config_version"; public static final String KEY_ANOMALY_CONFIG_VERSION = "anomaly_config_version";
private static final int DEFAULT_VERSION = 0; private static final int DEFAULT_VERSION = 0;
@VisibleForTesting @VisibleForTesting static final long CONFIG_UPDATE_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);
static final long CONFIG_UPDATE_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1);
public static void scheduleConfigUpdate(Context context) { public static void scheduleConfigUpdate(Context context) {
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
@@ -61,24 +60,25 @@ public class AnomalyConfigJobService extends JobService {
// Don't schedule it if it already exists, to make sure it runs periodically even after // Don't schedule it if it already exists, to make sure it runs periodically even after
// reboot // reboot
if (pending == null && jobScheduler.schedule(jobBuilder.build()) if (pending == null
!= JobScheduler.RESULT_SUCCESS) { && jobScheduler.schedule(jobBuilder.build()) != JobScheduler.RESULT_SUCCESS) {
Log.i(TAG, "Anomaly config update job service schedule failed."); Log.i(TAG, "Anomaly config update job service schedule failed.");
} }
} }
@Override @Override
public boolean onStartJob(JobParameters params) { public boolean onStartJob(JobParameters params) {
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(
final StatsManager statsManager = getSystemService(StatsManager.class); () -> {
checkAnomalyConfig(statsManager); final StatsManager statsManager = getSystemService(StatsManager.class);
try { checkAnomalyConfig(statsManager);
BatteryTipUtils.uploadAnomalyPendingIntent(this, statsManager); try {
} catch (StatsManager.StatsUnavailableException e) { BatteryTipUtils.uploadAnomalyPendingIntent(this, statsManager);
Log.w(TAG, "Failed to uploadAnomalyPendingIntent.", e); } catch (StatsManager.StatsUnavailableException e) {
} Log.w(TAG, "Failed to uploadAnomalyPendingIntent.", e);
jobFinished(params, false /* wantsReschedule */); }
}); jobFinished(params, false /* wantsReschedule */);
});
return true; return true;
} }
@@ -90,37 +90,48 @@ public class AnomalyConfigJobService extends JobService {
@VisibleForTesting @VisibleForTesting
synchronized void checkAnomalyConfig(StatsManager statsManager) { synchronized void checkAnomalyConfig(StatsManager statsManager) {
final SharedPreferences sharedPreferences = getSharedPreferences(PREF_DB, final SharedPreferences sharedPreferences =
Context.MODE_PRIVATE); getSharedPreferences(PREF_DB, Context.MODE_PRIVATE);
final int currentVersion = sharedPreferences.getInt(KEY_ANOMALY_CONFIG_VERSION, final int currentVersion =
DEFAULT_VERSION); sharedPreferences.getInt(KEY_ANOMALY_CONFIG_VERSION, DEFAULT_VERSION);
final int newVersion = Settings.Global.getInt(getContentResolver(), final int newVersion =
Settings.Global.ANOMALY_CONFIG_VERSION, DEFAULT_VERSION); Settings.Global.getInt(
final String rawConfig = Settings.Global.getString(getContentResolver(), getContentResolver(),
Settings.Global.ANOMALY_CONFIG); Settings.Global.ANOMALY_CONFIG_VERSION,
DEFAULT_VERSION);
final String rawConfig =
Settings.Global.getString(getContentResolver(), Settings.Global.ANOMALY_CONFIG);
Log.i(TAG, "CurrentVersion: " + currentVersion + " new version: " + newVersion); Log.i(TAG, "CurrentVersion: " + currentVersion + " new version: " + newVersion);
if (newVersion > currentVersion) { if (newVersion > currentVersion) {
try { try {
statsManager.removeConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY); statsManager.removeConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY);
} catch (StatsManager.StatsUnavailableException e) { } catch (StatsManager.StatsUnavailableException e) {
Log.i(TAG, "When updating anomaly config, failed to first remove the old config " Log.i(
+ StatsManagerConfig.ANOMALY_CONFIG_KEY, e); TAG,
"When updating anomaly config, failed to first remove the old config "
+ StatsManagerConfig.ANOMALY_CONFIG_KEY,
e);
} }
if (!TextUtils.isEmpty(rawConfig)) { if (!TextUtils.isEmpty(rawConfig)) {
try { try {
final byte[] config = Base64.decode(rawConfig, Base64.DEFAULT); final byte[] config = Base64.decode(rawConfig, Base64.DEFAULT);
statsManager.addConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY, config); statsManager.addConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY, config);
Log.i(TAG, "Upload the anomaly config. configKey: " Log.i(
+ StatsManagerConfig.ANOMALY_CONFIG_KEY); TAG,
"Upload the anomaly config. configKey: "
+ StatsManagerConfig.ANOMALY_CONFIG_KEY);
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt(KEY_ANOMALY_CONFIG_VERSION, newVersion); editor.putInt(KEY_ANOMALY_CONFIG_VERSION, newVersion);
editor.commit(); editor.commit();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Log.e(TAG, "Anomaly raw config is in wrong format", e); Log.e(TAG, "Anomaly raw config is in wrong format", e);
} catch (StatsManager.StatsUnavailableException e) { } catch (StatsManager.StatsUnavailableException e) {
Log.i(TAG, "Upload of anomaly config failed for configKey " Log.i(
+ StatsManagerConfig.ANOMALY_CONFIG_KEY, e); TAG,
"Upload of anomaly config failed for configKey "
+ StatsManagerConfig.ANOMALY_CONFIG_KEY,
e);
} }
} }
} }

View File

@@ -23,8 +23,8 @@ import android.content.Intent;
import android.util.Log; import android.util.Log;
/** /**
* Receive broadcast when {@link StatsManager} restart, then check the anomaly config and * Receive broadcast when {@link StatsManager} restart, then check the anomaly config and prepare
* prepare info for {@link StatsManager} * info for {@link StatsManager}
*/ */
public class AnomalyConfigReceiver extends BroadcastReceiver { public class AnomalyConfigReceiver extends BroadcastReceiver {
private static final String TAG = "AnomalyConfigReceiver"; private static final String TAG = "AnomalyConfigReceiver";

View File

@@ -26,9 +26,7 @@ import androidx.annotation.IntDef;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** /** Database controls the anomaly logging(e.g. packageName, anomalyType and time) */
* Database controls the anomaly logging(e.g. packageName, anomalyType and time)
*/
public class AnomalyDatabaseHelper extends SQLiteOpenHelper { public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "BatteryDatabaseHelper"; private static final String TAG = "BatteryDatabaseHelper";
@@ -36,9 +34,7 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 5; private static final int DATABASE_VERSION = 5;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({State.NEW, @IntDef({State.NEW, State.HANDLED, State.AUTO_HANDLED})
State.HANDLED,
State.AUTO_HANDLED})
public @interface State { public @interface State {
int NEW = 0; int NEW = 0;
int HANDLED = 1; int HANDLED = 1;
@@ -57,81 +53,92 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
} }
public interface AnomalyColumns { public interface AnomalyColumns {
/** /** The package name of the anomaly app */
* The package name of the anomaly app
*/
String PACKAGE_NAME = "package_name"; String PACKAGE_NAME = "package_name";
/**
* The uid of the anomaly app /** The uid of the anomaly app */
*/
String UID = "uid"; String UID = "uid";
/** /**
* The type of the anomaly app * The type of the anomaly app
*
* @see StatsManagerConfig.AnomalyType * @see StatsManagerConfig.AnomalyType
*/ */
String ANOMALY_TYPE = "anomaly_type"; String ANOMALY_TYPE = "anomaly_type";
/** /**
* The state of the anomaly app * The state of the anomaly app
*
* @see State * @see State
*/ */
String ANOMALY_STATE = "anomaly_state"; String ANOMALY_STATE = "anomaly_state";
/**
* The time when anomaly happens /** The time when anomaly happens */
*/
String TIME_STAMP_MS = "time_stamp_ms"; String TIME_STAMP_MS = "time_stamp_ms";
} }
private static final String CREATE_ANOMALY_TABLE = private static final String CREATE_ANOMALY_TABLE =
"CREATE TABLE " + Tables.TABLE_ANOMALY + "CREATE TABLE "
"(" + + Tables.TABLE_ANOMALY
AnomalyColumns.UID + + "("
" INTEGER NOT NULL, " + + AnomalyColumns.UID
AnomalyColumns.PACKAGE_NAME + + " INTEGER NOT NULL, "
" TEXT, " + + AnomalyColumns.PACKAGE_NAME
AnomalyColumns.ANOMALY_TYPE + + " TEXT, "
" INTEGER NOT NULL, " + + AnomalyColumns.ANOMALY_TYPE
AnomalyColumns.ANOMALY_STATE + + " INTEGER NOT NULL, "
" INTEGER NOT NULL, " + + AnomalyColumns.ANOMALY_STATE
AnomalyColumns.TIME_STAMP_MS + + " INTEGER NOT NULL, "
" INTEGER NOT NULL, " + + AnomalyColumns.TIME_STAMP_MS
" PRIMARY KEY (" + AnomalyColumns.UID + "," + AnomalyColumns.ANOMALY_TYPE + "," + " INTEGER NOT NULL, "
+ AnomalyColumns.ANOMALY_STATE + "," + AnomalyColumns.TIME_STAMP_MS + ")" + " PRIMARY KEY ("
+ AnomalyColumns.UID
+ ","
+ AnomalyColumns.ANOMALY_TYPE
+ ","
+ AnomalyColumns.ANOMALY_STATE
+ ","
+ AnomalyColumns.TIME_STAMP_MS
+ ")"
+ ")"; + ")";
public interface ActionColumns { public interface ActionColumns {
/** /** The package name of an app been performed an action */
* The package name of an app been performed an action
*/
String PACKAGE_NAME = "package_name"; String PACKAGE_NAME = "package_name";
/**
* The uid of an app been performed an action /** The uid of an app been performed an action */
*/
String UID = "uid"; String UID = "uid";
/** /**
* The type of user action * The type of user action
*
* @see ActionType * @see ActionType
*/ */
String ACTION_TYPE = "action_type"; String ACTION_TYPE = "action_type";
/**
* The time when action been performed /** The time when action been performed */
*/
String TIME_STAMP_MS = "time_stamp_ms"; String TIME_STAMP_MS = "time_stamp_ms";
} }
private static final String CREATE_ACTION_TABLE = private static final String CREATE_ACTION_TABLE =
"CREATE TABLE " + Tables.TABLE_ACTION + "CREATE TABLE "
"(" + + Tables.TABLE_ACTION
ActionColumns.UID + + "("
" INTEGER NOT NULL, " + + ActionColumns.UID
ActionColumns.PACKAGE_NAME + + " INTEGER NOT NULL, "
" TEXT, " + + ActionColumns.PACKAGE_NAME
ActionColumns.ACTION_TYPE + + " TEXT, "
" INTEGER NOT NULL, " + + ActionColumns.ACTION_TYPE
ActionColumns.TIME_STAMP_MS + + " INTEGER NOT NULL, "
" INTEGER NOT NULL, " + + ActionColumns.TIME_STAMP_MS
" PRIMARY KEY (" + ActionColumns.ACTION_TYPE + "," + ActionColumns.UID + "," + " INTEGER NOT NULL, "
+ ActionColumns.PACKAGE_NAME + ")" + " PRIMARY KEY ("
+ ActionColumns.ACTION_TYPE
+ ","
+ ActionColumns.UID
+ ","
+ ActionColumns.PACKAGE_NAME
+ ")"
+ ")"; + ")";
private static AnomalyDatabaseHelper sSingleton; private static AnomalyDatabaseHelper sSingleton;
@@ -155,8 +162,14 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < DATABASE_VERSION) { if (oldVersion < DATABASE_VERSION) {
Log.w(TAG, "Detected schema version '" + oldVersion + "'. " + Log.w(
"Index needs to be rebuilt for schema version '" + newVersion + "'."); TAG,
"Detected schema version '"
+ oldVersion
+ "'. "
+ "Index needs to be rebuilt for schema version '"
+ newVersion
+ "'.");
// We need to drop the tables and recreate them // We need to drop the tables and recreate them
reconstruct(db); reconstruct(db);
} }
@@ -164,8 +177,14 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
@Override @Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Detected schema version '" + oldVersion + "'. " + Log.w(
"Index needs to be rebuilt for schema version '" + newVersion + "'."); TAG,
"Detected schema version '"
+ oldVersion
+ "'. "
+ "Index needs to be rebuilt for schema version '"
+ newVersion
+ "'.");
// We need to drop the tables and recreate them // We need to drop the tables and recreate them
reconstruct(db); reconstruct(db);
} }

View File

@@ -57,22 +57,20 @@ import java.util.concurrent.TimeUnit;
public class AnomalyDetectionJobService extends JobService { public class AnomalyDetectionJobService extends JobService {
private static final String TAG = "AnomalyDetectionService"; private static final String TAG = "AnomalyDetectionService";
private static final int ON = 1; private static final int ON = 1;
@VisibleForTesting @VisibleForTesting static final int UID_NULL = -1;
static final int UID_NULL = -1; @VisibleForTesting static final int STATSD_UID_FILED = 1;
@VisibleForTesting @VisibleForTesting static final long MAX_DELAY_MS = TimeUnit.MINUTES.toMillis(30);
static final int STATSD_UID_FILED = 1;
@VisibleForTesting
static final long MAX_DELAY_MS = TimeUnit.MINUTES.toMillis(30);
private final Object mLock = new Object(); private final Object mLock = new Object();
@GuardedBy("mLock") @GuardedBy("mLock")
@VisibleForTesting @VisibleForTesting
boolean mIsJobCanceled = false; boolean mIsJobCanceled = false;
public static void scheduleAnomalyDetection(Context context, Intent intent) { public static void scheduleAnomalyDetection(Context context, Intent intent) {
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
final ComponentName component = new ComponentName(context, final ComponentName component =
AnomalyDetectionJobService.class); new ComponentName(context, AnomalyDetectionJobService.class);
final JobInfo.Builder jobBuilder = final JobInfo.Builder jobBuilder =
new JobInfo.Builder(R.integer.job_anomaly_detection, component) new JobInfo.Builder(R.integer.job_anomaly_detection, component)
.setOverrideDeadline(MAX_DELAY_MS); .setOverrideDeadline(MAX_DELAY_MS);
@@ -88,30 +86,40 @@ public class AnomalyDetectionJobService extends JobService {
synchronized (mLock) { synchronized (mLock) {
mIsJobCanceled = false; mIsJobCanceled = false;
} }
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(
final Context context = AnomalyDetectionJobService.this; () -> {
final BatteryDatabaseManager batteryDatabaseManager = final Context context = AnomalyDetectionJobService.this;
BatteryDatabaseManager.getInstance(this); final BatteryDatabaseManager batteryDatabaseManager =
final BatteryTipPolicy policy = new BatteryTipPolicy(this); BatteryDatabaseManager.getInstance(this);
final BatteryUtils batteryUtils = BatteryUtils.getInstance(this); final BatteryTipPolicy policy = new BatteryTipPolicy(this);
final ContentResolver contentResolver = getContentResolver(); final BatteryUtils batteryUtils = BatteryUtils.getInstance(this);
final UserManager userManager = getSystemService(UserManager.class); final ContentResolver contentResolver = getContentResolver();
final PowerAllowlistBackend powerAllowlistBackend = final UserManager userManager = getSystemService(UserManager.class);
PowerAllowlistBackend.getInstance(context); final PowerAllowlistBackend powerAllowlistBackend =
final PowerUsageFeatureProvider powerUsageFeatureProvider = PowerAllowlistBackend.getInstance(context);
FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider(); final PowerUsageFeatureProvider powerUsageFeatureProvider =
final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider();
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
for (JobWorkItem item = dequeueWork(params); item != null; item = dequeueWork(params)) { for (JobWorkItem item = dequeueWork(params);
saveAnomalyToDatabase(context, userManager, item != null;
batteryDatabaseManager, batteryUtils, policy, powerAllowlistBackend, item = dequeueWork(params)) {
contentResolver, powerUsageFeatureProvider, metricsFeatureProvider, saveAnomalyToDatabase(
item.getIntent().getExtras()); context,
userManager,
batteryDatabaseManager,
batteryUtils,
policy,
powerAllowlistBackend,
contentResolver,
powerUsageFeatureProvider,
metricsFeatureProvider,
item.getIntent().getExtras());
completeWork(params, item); completeWork(params, item);
} }
}); });
return true; return true;
} }
@@ -125,34 +133,49 @@ public class AnomalyDetectionJobService extends JobService {
} }
@VisibleForTesting @VisibleForTesting
void saveAnomalyToDatabase(Context context, UserManager userManager, void saveAnomalyToDatabase(
BatteryDatabaseManager databaseManager, BatteryUtils batteryUtils, Context context,
BatteryTipPolicy policy, PowerAllowlistBackend powerAllowlistBackend, UserManager userManager,
ContentResolver contentResolver, PowerUsageFeatureProvider powerUsageFeatureProvider, BatteryDatabaseManager databaseManager,
MetricsFeatureProvider metricsFeatureProvider, Bundle bundle) { BatteryUtils batteryUtils,
BatteryTipPolicy policy,
PowerAllowlistBackend powerAllowlistBackend,
ContentResolver contentResolver,
PowerUsageFeatureProvider powerUsageFeatureProvider,
MetricsFeatureProvider metricsFeatureProvider,
Bundle bundle) {
// The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|} // The Example of intentDimsValue is: 35:{1:{1:{1:10013|}|}|}
final StatsDimensionsValue intentDimsValue = final StatsDimensionsValue intentDimsValue =
bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE); bundle.getParcelable(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE);
final long timeMs = bundle.getLong(AnomalyDetectionReceiver.KEY_ANOMALY_TIMESTAMP, final long timeMs =
System.currentTimeMillis()); bundle.getLong(
final ArrayList<String> cookies = bundle.getStringArrayList( AnomalyDetectionReceiver.KEY_ANOMALY_TIMESTAMP, System.currentTimeMillis());
StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES); final ArrayList<String> cookies =
final AnomalyInfo anomalyInfo = new AnomalyInfo( bundle.getStringArrayList(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES);
!ArrayUtils.isEmpty(cookies) ? cookies.get(0) : ""); final AnomalyInfo anomalyInfo =
new AnomalyInfo(!ArrayUtils.isEmpty(cookies) ? cookies.get(0) : "");
Log.i(TAG, "Extra stats value: " + intentDimsValue.toString()); Log.i(TAG, "Extra stats value: " + intentDimsValue.toString());
try { try {
final int uid = extractUidFromStatsDimensionsValue(intentDimsValue); final int uid = extractUidFromStatsDimensionsValue(intentDimsValue);
final boolean autoFeatureOn = powerUsageFeatureProvider.isSmartBatterySupported() final boolean autoFeatureOn =
? Settings.Global.getInt(contentResolver, powerUsageFeatureProvider.isSmartBatterySupported()
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON) == ON ? Settings.Global.getInt(
: Settings.Global.getInt(contentResolver, contentResolver,
Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON) == ON; Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
ON)
== ON
: Settings.Global.getInt(
contentResolver,
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
ON)
== ON;
final String packageName = batteryUtils.getPackageName(uid); final String packageName = batteryUtils.getPackageName(uid);
final long versionCode = batteryUtils.getAppLongVersionCode(packageName); final long versionCode = batteryUtils.getAppLongVersionCode(packageName);
final String versionedPackage = packageName + "/" + versionCode; final String versionedPackage = packageName + "/" + versionCode;
if (batteryUtils.shouldHideAnomaly(powerAllowlistBackend, uid, anomalyInfo)) { if (batteryUtils.shouldHideAnomaly(powerAllowlistBackend, uid, anomalyInfo)) {
metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, metricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_ANOMALY_IGNORED, SettingsEnums.ACTION_ANOMALY_IGNORED,
SettingsEnums.PAGE_UNKNOWN, SettingsEnums.PAGE_UNKNOWN,
versionedPackage, versionedPackage,
@@ -160,17 +183,23 @@ public class AnomalyDetectionJobService extends JobService {
} else { } else {
if (autoFeatureOn && anomalyInfo.autoRestriction) { if (autoFeatureOn && anomalyInfo.autoRestriction) {
// Auto restrict this app // Auto restrict this app
batteryUtils.setForceAppStandby(uid, packageName, batteryUtils.setForceAppStandby(uid, packageName, AppOpsManager.MODE_IGNORED);
AppOpsManager.MODE_IGNORED); databaseManager.insertAnomaly(
databaseManager.insertAnomaly(uid, packageName, anomalyInfo.anomalyType, uid,
packageName,
anomalyInfo.anomalyType,
AnomalyDatabaseHelper.State.AUTO_HANDLED, AnomalyDatabaseHelper.State.AUTO_HANDLED,
timeMs); timeMs);
} else { } else {
databaseManager.insertAnomaly(uid, packageName, anomalyInfo.anomalyType, databaseManager.insertAnomaly(
uid,
packageName,
anomalyInfo.anomalyType,
AnomalyDatabaseHelper.State.NEW, AnomalyDatabaseHelper.State.NEW,
timeMs); timeMs);
} }
metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, metricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_ANOMALY_TRIGGERED, SettingsEnums.ACTION_ANOMALY_TRIGGERED,
SettingsEnums.PAGE_UNKNOWN, SettingsEnums.PAGE_UNKNOWN,
versionedPackage, versionedPackage,
@@ -183,12 +212,12 @@ public class AnomalyDetectionJobService extends JobService {
} }
/** /**
* Extract the uid from {@link StatsDimensionsValue} * Extract the uid from {@link StatsDimensionsValue} <br>
* * <br>
* The uid dimension has the format: 1:<int> inside the tuple list. Here are some examples: * The uid dimension has the format: {1:int} inside the tuple list. Here are some examples: <br>
* 1. Excessive bg anomaly: 27:{1:10089|} * 1.Excessive bg anomaly: 27:{1:10089|} <br>
* 2. Wakeup alarm anomaly: 35:{1:{1:{1:10013|}|}|} * 2.Wakeup alarm anomaly: 35:{1:{1:{1:10013|}|}|} <br>
* 3. Bluetooth anomaly: 3:{1:{1:{1:10140|}|}|} * 3.Bluetooth anomaly: 3:{1:{1:{1:10140|}|}|}
*/ */
@VisibleForTesting @VisibleForTesting
int extractUidFromStatsDimensionsValue(StatsDimensionsValue statsDimensionsValue) { int extractUidFromStatsDimensionsValue(StatsDimensionsValue statsDimensionsValue) {

View File

@@ -23,9 +23,7 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
/** /** Receive the anomaly info from {@link StatsManager} */
* Receive the anomaly info from {@link StatsManager}
*/
public class AnomalyDetectionReceiver extends BroadcastReceiver { public class AnomalyDetectionReceiver extends BroadcastReceiver {
private static final String TAG = "SettingsAnomalyReceiver"; private static final String TAG = "SettingsAnomalyReceiver";
@@ -35,10 +33,16 @@ public class AnomalyDetectionReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
final long configUid = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_UID, -1); final long configUid = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_UID, -1);
final long configKey = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, -1); final long configKey = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, -1);
final long subscriptionId = intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, final long subscriptionId =
-1); intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, -1);
Log.i(TAG, "Anomaly intent received. configUid = " + configUid + " configKey = " Log.i(
+ configKey + " subscriptionId = " + subscriptionId); TAG,
"Anomaly intent received. configUid = "
+ configUid
+ " configKey = "
+ configKey
+ " subscriptionId = "
+ subscriptionId);
final Bundle bundle = intent.getExtras(); final Bundle bundle = intent.getExtras();
if (bundle == null) { if (bundle == null) {

View File

@@ -19,9 +19,7 @@ package com.android.settings.fuelgauge.batterytip;
import android.util.KeyValueListParser; import android.util.KeyValueListParser;
import android.util.Log; import android.util.Log;
/** /** Model class to parse and store anomaly info from statsd. */
* Model class to parse and store anomaly info from statsd.
*/
public class AnomalyInfo { public class AnomalyInfo {
private static final String TAG = "AnomalyInfo"; private static final String TAG = "AnomalyInfo";
@@ -37,5 +35,4 @@ public class AnomalyInfo {
anomalyType = parser.getInt(KEY_ANOMALY_TYPE, -1); anomalyType = parser.getInt(KEY_ANOMALY_TYPE, -1);
autoRestriction = parser.getBoolean(KEY_AUTO_RESTRICTION, false); autoRestriction = parser.getBoolean(KEY_AUTO_RESTRICTION, false);
} }
} }

View File

@@ -25,16 +25,17 @@ import androidx.annotation.VisibleForTesting;
import java.util.Objects; import java.util.Objects;
/** /** Model class stores app info(e.g. package name, type..) that used in battery tip */
* Model class stores app info(e.g. package name, type..) that used in battery tip
*/
public class AppInfo implements Comparable<AppInfo>, Parcelable { public class AppInfo implements Comparable<AppInfo>, Parcelable {
public final String packageName; public final String packageName;
/** /**
* Anomaly type of the app * Anomaly type of the app
*
* @see StatsManagerConfig.AnomalyType * @see StatsManagerConfig.AnomalyType
*/ */
public final ArraySet<Integer> anomalyTypes; public final ArraySet<Integer> anomalyTypes;
public final long screenOnTimeMs; public final long screenOnTimeMs;
public final int uid; public final int uid;
@@ -73,7 +74,11 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
@Override @Override
public String toString() { public String toString() {
return "packageName=" + packageName + ",anomalyTypes=" + anomalyTypes + ",screenTime=" return "packageName="
+ packageName
+ ",anomalyTypes="
+ anomalyTypes
+ ",screenTime="
+ screenOnTimeMs; + screenOnTimeMs;
} }
@@ -93,15 +98,16 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
&& TextUtils.equals(packageName, other.packageName); && TextUtils.equals(packageName, other.packageName);
} }
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public static final Parcelable.Creator CREATOR =
public AppInfo createFromParcel(Parcel in) { new Parcelable.Creator() {
return new AppInfo(in); public AppInfo createFromParcel(Parcel in) {
} return new AppInfo(in);
}
public AppInfo[] newArray(int size) { public AppInfo[] newArray(int size) {
return new AppInfo[size]; return new AppInfo[size];
} }
}; };
public static final class Builder { public static final class Builder {
private ArraySet<Integer> mAnomalyTypes = new ArraySet<>(); private ArraySet<Integer> mAnomalyTypes = new ArraySet<>();
@@ -133,4 +139,4 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
return new AppInfo(this); return new AppInfo(this);
} }
} }
} }

View File

@@ -47,8 +47,8 @@ import java.util.Map;
/** /**
* Database manager for battery data. Now it only contains anomaly data stored in {@link AppInfo}. * Database manager for battery data. Now it only contains anomaly data stored in {@link AppInfo}.
* *
* This manager may be accessed by multi-threads. All the database related methods are synchronized * <p>This manager may be accessed by multi-threads. All the database related methods are
* so each operation won't be interfered by other threads. * synchronized so each operation won't be interfered by other threads.
*/ */
public class BatteryDatabaseManager { public class BatteryDatabaseManager {
private static BatteryDatabaseManager sSingleton; private static BatteryDatabaseManager sSingleton;
@@ -74,16 +74,15 @@ public class BatteryDatabaseManager {
/** /**
* Insert an anomaly log to database. * Insert an anomaly log to database.
* *
* @param uid the uid of the app * @param uid the uid of the app
* @param packageName the package name of the app * @param packageName the package name of the app
* @param type the type of the anomaly * @param type the type of the anomaly
* @param anomalyState the state of the anomaly * @param anomalyState the state of the anomaly
* @param timestampMs the time when it is happened * @param timestampMs the time when it is happened
* @return {@code true} if insert operation succeed * @return {@code true} if insert operation succeed
*/ */
public synchronized boolean insertAnomaly(int uid, String packageName, int type, public synchronized boolean insertAnomaly(
int anomalyState, int uid, String packageName, int type, int anomalyState, long timestampMs) {
long timestampMs) {
final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(UID, uid); values.put(UID, uid);
@@ -105,22 +104,31 @@ public class BatteryDatabaseManager {
final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC"; final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC";
final Map<Integer, AppInfo.Builder> mAppInfoBuilders = new ArrayMap<>(); final Map<Integer, AppInfo.Builder> mAppInfoBuilders = new ArrayMap<>();
final String selection = TIME_STAMP_MS + " > ? AND " + ANOMALY_STATE + " = ? "; final String selection = TIME_STAMP_MS + " > ? AND " + ANOMALY_STATE + " = ? ";
final String[] selectionArgs = new String[]{String.valueOf(timestampMsAfter), final String[] selectionArgs =
String.valueOf(state)}; new String[] {String.valueOf(timestampMsAfter), String.valueOf(state)};
try (Cursor cursor = db.query(TABLE_ANOMALY, projection, selection, selectionArgs, try (Cursor cursor =
null /* groupBy */, null /* having */, orderBy)) { db.query(
TABLE_ANOMALY,
projection,
selection,
selectionArgs,
null /* groupBy */,
null /* having */,
orderBy)) {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
final int uid = cursor.getInt(cursor.getColumnIndex(UID)); final int uid = cursor.getInt(cursor.getColumnIndex(UID));
if (!mAppInfoBuilders.containsKey(uid)) { if (!mAppInfoBuilders.containsKey(uid)) {
final AppInfo.Builder builder = new AppInfo.Builder() final AppInfo.Builder builder =
.setUid(uid) new AppInfo.Builder()
.setPackageName( .setUid(uid)
cursor.getString(cursor.getColumnIndex(PACKAGE_NAME))); .setPackageName(
cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)));
mAppInfoBuilders.put(uid, builder); mAppInfoBuilders.put(uid, builder);
} }
mAppInfoBuilders.get(uid).addAnomalyType( mAppInfoBuilders
cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE))); .get(uid)
.addAnomalyType(cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)));
} }
} }
@@ -133,15 +141,15 @@ public class BatteryDatabaseManager {
public synchronized void deleteAllAnomaliesBeforeTimeStamp(long timestampMs) { public synchronized void deleteAllAnomaliesBeforeTimeStamp(long timestampMs) {
final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
db.delete(TABLE_ANOMALY, TIME_STAMP_MS + " < ?", db.delete(
new String[]{String.valueOf(timestampMs)}); TABLE_ANOMALY, TIME_STAMP_MS + " < ?", new String[] {String.valueOf(timestampMs)});
} }
/** /**
* Update the type of anomalies to {@code state} * Update the type of anomalies to {@code state}
* *
* @param appInfos represents the anomalies * @param appInfos represents the anomalies
* @param state which state to update to * @param state which state to update to
*/ */
public synchronized void updateAnomalies(List<AppInfo> appInfos, int state) { public synchronized void updateAnomalies(List<AppInfo> appInfos, int state) {
if (!appInfos.isEmpty()) { if (!appInfos.isEmpty()) {
@@ -154,8 +162,14 @@ public class BatteryDatabaseManager {
final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
values.put(ANOMALY_STATE, state); values.put(ANOMALY_STATE, state);
db.update(TABLE_ANOMALY, values, PACKAGE_NAME + " IN (" + TextUtils.join(",", db.update(
Collections.nCopies(appInfos.size(), "?")) + ")", whereArgs); TABLE_ANOMALY,
values,
PACKAGE_NAME
+ " IN ("
+ TextUtils.join(",", Collections.nCopies(appInfos.size(), "?"))
+ ")",
whereArgs);
} }
} }
@@ -171,10 +185,17 @@ public class BatteryDatabaseManager {
final SQLiteDatabase db = mDatabaseHelper.getReadableDatabase(); final SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
final String[] projection = {ActionColumns.UID, ActionColumns.TIME_STAMP_MS}; final String[] projection = {ActionColumns.UID, ActionColumns.TIME_STAMP_MS};
final String selection = ActionColumns.ACTION_TYPE + " = ? "; final String selection = ActionColumns.ACTION_TYPE + " = ? ";
final String[] selectionArgs = new String[]{String.valueOf(type)}; final String[] selectionArgs = new String[] {String.valueOf(type)};
try (Cursor cursor = db.query(TABLE_ACTION, projection, selection, selectionArgs, try (Cursor cursor =
null /* groupBy */, null /* having */, null /* orderBy */)) { db.query(
TABLE_ACTION,
projection,
selection,
selectionArgs,
null /* groupBy */,
null /* having */,
null /* orderBy */)) {
final int uidIndex = cursor.getColumnIndex(ActionColumns.UID); final int uidIndex = cursor.getColumnIndex(ActionColumns.UID);
final int timestampIndex = cursor.getColumnIndex(ActionColumns.TIME_STAMP_MS); final int timestampIndex = cursor.getColumnIndex(ActionColumns.TIME_STAMP_MS);
@@ -188,11 +209,12 @@ public class BatteryDatabaseManager {
return timeStamps; return timeStamps;
} }
/** /** Insert an action, or update it if already existed */
* Insert an action, or update it if already existed public synchronized boolean insertAction(
*/ @AnomalyDatabaseHelper.ActionType int type,
public synchronized boolean insertAction(@AnomalyDatabaseHelper.ActionType int type, int uid,
int uid, String packageName, long timestampMs) { String packageName,
long timestampMs) {
final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
values.put(ActionColumns.UID, uid); values.put(ActionColumns.UID, uid);
@@ -203,17 +225,21 @@ public class BatteryDatabaseManager {
return db.insertWithOnConflict(TABLE_ACTION, null, values, CONFLICT_REPLACE) != -1; return db.insertWithOnConflict(TABLE_ACTION, null, values, CONFLICT_REPLACE) != -1;
} }
/** /** Remove an action */
* Remove an action public synchronized boolean deleteAction(
*/ @AnomalyDatabaseHelper.ActionType int type, int uid, String packageName) {
public synchronized boolean deleteAction(@AnomalyDatabaseHelper.ActionType int type,
int uid, String packageName) {
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
final String where = final String where =
ActionColumns.ACTION_TYPE + " = ? AND " + ActionColumns.UID + " = ? AND " ActionColumns.ACTION_TYPE
+ ActionColumns.PACKAGE_NAME + " = ? "; + " = ? AND "
final String[] whereArgs = new String[]{String.valueOf(type), String.valueOf(uid), + ActionColumns.UID
String.valueOf(packageName)}; + " = ? AND "
+ ActionColumns.PACKAGE_NAME
+ " = ? ";
final String[] whereArgs =
new String[] {
String.valueOf(type), String.valueOf(uid), String.valueOf(packageName)
};
return db.delete(TABLE_ACTION, where, whereArgs) != 0; return db.delete(TABLE_ACTION, where, whereArgs) != 0;
} }

View File

@@ -46,19 +46,15 @@ import com.android.settingslib.utils.StringUtil;
import java.util.List; import java.util.List;
/** /** Dialog Fragment to show action dialog for each anomaly */
* Dialog Fragment to show action dialog for each anomaly public class BatteryTipDialogFragment extends InstrumentedDialogFragment
*/ implements DialogInterface.OnClickListener {
public class BatteryTipDialogFragment extends InstrumentedDialogFragment implements
DialogInterface.OnClickListener {
private static final String ARG_BATTERY_TIP = "battery_tip"; private static final String ARG_BATTERY_TIP = "battery_tip";
private static final String ARG_METRICS_KEY = "metrics_key"; private static final String ARG_METRICS_KEY = "metrics_key";
@VisibleForTesting @VisibleForTesting BatteryTip mBatteryTip;
BatteryTip mBatteryTip; @VisibleForTesting int mMetricsKey;
@VisibleForTesting
int mMetricsKey;
public static BatteryTipDialogFragment newInstance(BatteryTip batteryTip, int metricsKey) { public static BatteryTipDialogFragment newInstance(BatteryTip batteryTip, int metricsKey) {
BatteryTipDialogFragment dialogFragment = new BatteryTipDialogFragment(); BatteryTipDialogFragment dialogFragment = new BatteryTipDialogFragment();
@@ -87,16 +83,17 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
.create(); .create();
case BatteryTip.TipType.HIGH_DEVICE_USAGE: case BatteryTip.TipType.HIGH_DEVICE_USAGE:
final HighUsageTip highUsageTip = (HighUsageTip) mBatteryTip; final HighUsageTip highUsageTip = (HighUsageTip) mBatteryTip;
final RecyclerView view = (RecyclerView) LayoutInflater.from(context).inflate( final RecyclerView view =
R.layout.recycler_view, (RecyclerView)
null); LayoutInflater.from(context).inflate(R.layout.recycler_view, null);
view.setLayoutManager(new LinearLayoutManager(context)); view.setLayoutManager(new LinearLayoutManager(context));
view.setAdapter(new HighUsageAdapter(context, view.setAdapter(new HighUsageAdapter(context, highUsageTip.getHighUsageAppList()));
highUsageTip.getHighUsageAppList()));
return new AlertDialog.Builder(context) return new AlertDialog.Builder(context)
.setMessage(getString(R.string.battery_tip_dialog_message, .setMessage(
highUsageTip.getHighUsageAppList().size())) getString(
R.string.battery_tip_dialog_message,
highUsageTip.getHighUsageAppList().size()))
.setView(view) .setView(view)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.create(); .create();
@@ -104,14 +101,19 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
final RestrictAppTip restrictAppTip = (RestrictAppTip) mBatteryTip; final RestrictAppTip restrictAppTip = (RestrictAppTip) mBatteryTip;
final List<AppInfo> restrictedAppList = restrictAppTip.getRestrictAppList(); final List<AppInfo> restrictedAppList = restrictAppTip.getRestrictAppList();
final int num = restrictedAppList.size(); final int num = restrictedAppList.size();
final CharSequence appLabel = Utils.getApplicationLabel(context, final CharSequence appLabel =
restrictedAppList.get(0).packageName); Utils.getApplicationLabel(context, restrictedAppList.get(0).packageName);
final AlertDialog.Builder builder = new AlertDialog.Builder(context) final AlertDialog.Builder builder =
.setTitle(StringUtil.getIcuPluralsString(context, num, new AlertDialog.Builder(context)
R.string.battery_tip_restrict_app_dialog_title)) .setTitle(
.setPositiveButton(R.string.battery_tip_restrict_app_dialog_ok, this) StringUtil.getIcuPluralsString(
.setNegativeButton(android.R.string.cancel, null); context,
num,
R.string.battery_tip_restrict_app_dialog_title))
.setPositiveButton(
R.string.battery_tip_restrict_app_dialog_ok, this)
.setNegativeButton(android.R.string.cancel, null);
if (num == 1) { if (num == 1) {
builder.setMessage( builder.setMessage(
getString(R.string.battery_tip_restrict_app_dialog_message, appLabel)); getString(R.string.battery_tip_restrict_app_dialog_message, appLabel));
@@ -119,22 +121,25 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
builder.setMessage( builder.setMessage(
getString( getString(
R.string.battery_tip_restrict_apps_less_than_5_dialog_message)); R.string.battery_tip_restrict_apps_less_than_5_dialog_message));
final RecyclerView restrictionView = (RecyclerView) LayoutInflater.from( final RecyclerView restrictionView =
context).inflate(R.layout.recycler_view, null); (RecyclerView)
LayoutInflater.from(context)
.inflate(R.layout.recycler_view, null);
restrictionView.setLayoutManager(new LinearLayoutManager(context)); restrictionView.setLayoutManager(new LinearLayoutManager(context));
restrictionView.setAdapter(new HighUsageAdapter(context, restrictedAppList)); restrictionView.setAdapter(new HighUsageAdapter(context, restrictedAppList));
builder.setView(restrictionView); builder.setView(restrictionView);
} else { } else {
builder.setMessage(context.getString( builder.setMessage(
R.string.battery_tip_restrict_apps_more_than_5_dialog_message, context.getString(
restrictAppTip.getRestrictAppsString(context))); R.string.battery_tip_restrict_apps_more_than_5_dialog_message,
restrictAppTip.getRestrictAppsString(context)));
} }
return builder.create(); return builder.create();
case BatteryTip.TipType.REMOVE_APP_RESTRICTION: case BatteryTip.TipType.REMOVE_APP_RESTRICTION:
final UnrestrictAppTip unrestrictAppTip = (UnrestrictAppTip) mBatteryTip; final UnrestrictAppTip unrestrictAppTip = (UnrestrictAppTip) mBatteryTip;
final CharSequence name = Utils.getApplicationLabel(context, final CharSequence name =
unrestrictAppTip.getPackageName()); Utils.getApplicationLabel(context, unrestrictAppTip.getPackageName());
return new AlertDialog.Builder(context) return new AlertDialog.Builder(context)
.setTitle(getString(R.string.battery_tip_unrestrict_app_dialog_title)) .setTitle(getString(R.string.battery_tip_unrestrict_app_dialog_title))
@@ -158,9 +163,11 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
if (lsn == null) { if (lsn == null) {
return; return;
} }
final BatteryTipAction action = BatteryTipUtils.getActionForBatteryTip(mBatteryTip, final BatteryTipAction action =
(SettingsActivity) getActivity(), BatteryTipUtils.getActionForBatteryTip(
(InstrumentedPreferenceFragment) getTargetFragment()); mBatteryTip,
(SettingsActivity) getActivity(),
(InstrumentedPreferenceFragment) getTargetFragment());
if (action != null) { if (action != null) {
action.handlePositiveAction(mMetricsKey); action.handlePositiveAction(mMetricsKey);
} }
@@ -168,10 +175,12 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
} }
private boolean isPluggedIn() { private boolean isPluggedIn() {
final Intent batteryIntent = getContext().registerReceiver(null /* receiver */, final Intent batteryIntent =
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); getContext()
return batteryIntent != null && batteryIntent.getIntExtra( .registerReceiver(
BatteryManager.EXTRA_PLUGGED, 0) != 0; null /* receiver */,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
return batteryIntent != null
&& batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
} }
} }

View File

@@ -29,10 +29,7 @@ import com.android.settings.fuelgauge.batterytip.detectors.DockDefenderDetector;
import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector; import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
import com.android.settings.fuelgauge.batterytip.detectors.IncompatibleChargerDetector; import com.android.settings.fuelgauge.batterytip.detectors.IncompatibleChargerDetector;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector; import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
import com.android.settingslib.fuelgauge.EstimateKt;
import com.android.settingslib.utils.AsyncLoaderCompat; import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList; import java.util.ArrayList;
@@ -47,8 +44,7 @@ public class BatteryTipLoader extends AsyncLoaderCompat<List<BatteryTip>> {
private static final String TAG = "BatteryTipLoader"; private static final String TAG = "BatteryTipLoader";
private BatteryUsageStats mBatteryUsageStats; private BatteryUsageStats mBatteryUsageStats;
@VisibleForTesting @VisibleForTesting BatteryUtils mBatteryUtils;
BatteryUtils mBatteryUtils;
public BatteryTipLoader(Context context, BatteryUsageStats batteryUsageStats) { public BatteryTipLoader(Context context, BatteryUsageStats batteryUsageStats) {
super(context); super(context);
@@ -75,6 +71,5 @@ public class BatteryTipLoader extends AsyncLoaderCompat<List<BatteryTip>> {
} }
@Override @Override
protected void onDiscardResult(List<BatteryTip> result) { protected void onDiscardResult(List<BatteryTip> result) {}
}
} }

View File

@@ -25,10 +25,7 @@ import androidx.annotation.VisibleForTesting;
import java.time.Duration; import java.time.Duration;
/** /** Class to store the policy for battery tips, which comes from {@link Settings.Global} */
* Class to store the policy for battery tips, which comes from
* {@link Settings.Global}
*/
public class BatteryTipPolicy { public class BatteryTipPolicy {
public static final String TAG = "BatteryTipPolicy"; public static final String TAG = "BatteryTipPolicy";
@@ -102,9 +99,9 @@ public class BatteryTipPolicy {
public final long highUsagePeriodMs; public final long highUsagePeriodMs;
/** /**
* The battery draining threshold to detect whether device is heavily used. * The battery draining threshold to detect whether device is heavily used. If battery drains
* If battery drains more than {@link #highUsageBatteryDraining} in last {@link * more than {@link #highUsageBatteryDraining} in last {@link #highUsagePeriodMs}, treat device
* #highUsagePeriodMs}, treat device as heavily used. * as heavily used.
* *
* @see Settings.Global#BATTERY_TIP_CONSTANTS * @see Settings.Global#BATTERY_TIP_CONSTANTS
* @see #KEY_HIGH_USAGE_BATTERY_DRAINING * @see #KEY_HIGH_USAGE_BATTERY_DRAINING
@@ -171,8 +168,8 @@ public class BatteryTipPolicy {
/** /**
* Battery drain percentage threshold for excessive background anomaly(i.e. 10%) * Battery drain percentage threshold for excessive background anomaly(i.e. 10%)
* *
* This is an additional check for excessive background, to check whether battery drain * <p>This is an additional check for excessive background, to check whether battery drain for
* for an app is larger than x% * an app is larger than x%
* *
* @see Settings.Global#BATTERY_TIP_CONSTANTS * @see Settings.Global#BATTERY_TIP_CONSTANTS
* @see #KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE * @see #KEY_EXCESSIVE_BG_DRAIN_PERCENTAGE
@@ -220,8 +217,9 @@ public class BatteryTipPolicy {
@VisibleForTesting @VisibleForTesting
BatteryTipPolicy(Context context, KeyValueListParser parser) { BatteryTipPolicy(Context context, KeyValueListParser parser) {
mParser = parser; mParser = parser;
final String value = Settings.Global.getString(context.getContentResolver(), final String value =
Settings.Global.BATTERY_TIP_CONSTANTS); Settings.Global.getString(
context.getContentResolver(), Settings.Global.BATTERY_TIP_CONSTANTS);
try { try {
mParser.setString(value); mParser.setString(value);
@@ -234,8 +232,8 @@ public class BatteryTipPolicy {
batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true); batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true); highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3); highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
highUsagePeriodMs = mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS, highUsagePeriodMs =
Duration.ofHours(2).toMillis()); mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS, Duration.ofHours(2).toMillis());
highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25); highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25);
appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true); appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
appRestrictionActiveHour = mParser.getInt(KEY_APP_RESTRICTION_ACTIVE_HOUR, 24); appRestrictionActiveHour = mParser.getInt(KEY_APP_RESTRICTION_ACTIVE_HOUR, 24);
@@ -251,5 +249,4 @@ public class BatteryTipPolicy {
testSmartBatteryTip = mParser.getBoolean(KEY_TEST_SMART_BATTERY_TIP, false); testSmartBatteryTip = mParser.getBoolean(KEY_TEST_SMART_BATTERY_TIP, false);
testLowBatteryTip = mParser.getBoolean(KEY_TEST_LOW_BATTERY_TIP, false); testLowBatteryTip = mParser.getBoolean(KEY_TEST_LOW_BATTERY_TIP, false);
} }
} }

View File

@@ -40,9 +40,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /** Controller in charge of the battery tip group */
* Controller in charge of the battery tip group
*/
public class BatteryTipPreferenceController extends BasePreferenceController { public class BatteryTipPreferenceController extends BasePreferenceController {
public static final String PREF_NAME = "battery_tip"; public static final String PREF_NAME = "battery_tip";
@@ -57,10 +55,8 @@ public class BatteryTipPreferenceController extends BasePreferenceController {
private SettingsActivity mSettingsActivity; private SettingsActivity mSettingsActivity;
private MetricsFeatureProvider mMetricsFeatureProvider; private MetricsFeatureProvider mMetricsFeatureProvider;
private boolean mNeedUpdate; private boolean mNeedUpdate;
@VisibleForTesting @VisibleForTesting CardPreference mCardPreference;
CardPreference mCardPreference; @VisibleForTesting Context mPrefContext;
@VisibleForTesting
Context mPrefContext;
InstrumentedPreferenceFragment mFragment; InstrumentedPreferenceFragment mFragment;
public BatteryTipPreferenceController(Context context, String preferenceKey) { public BatteryTipPreferenceController(Context context, String preferenceKey) {
@@ -122,13 +118,15 @@ public class BatteryTipPreferenceController extends BasePreferenceController {
final BatteryTip batteryTip = mBatteryTipMap.get(preference.getKey()); final BatteryTip batteryTip = mBatteryTipMap.get(preference.getKey());
if (batteryTip != null) { if (batteryTip != null) {
if (batteryTip.shouldShowDialog()) { if (batteryTip.shouldShowDialog()) {
BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance( BatteryTipDialogFragment dialogFragment =
batteryTip, mFragment.getMetricsCategory()); BatteryTipDialogFragment.newInstance(
batteryTip, mFragment.getMetricsCategory());
dialogFragment.setTargetFragment(mFragment, REQUEST_ANOMALY_ACTION); dialogFragment.setTargetFragment(mFragment, REQUEST_ANOMALY_ACTION);
dialogFragment.show(mFragment.getFragmentManager(), TAG); dialogFragment.show(mFragment.getFragmentManager(), TAG);
} else { } else {
final BatteryTipAction action = BatteryTipUtils.getActionForBatteryTip(batteryTip, final BatteryTipAction action =
mSettingsActivity, mFragment); BatteryTipUtils.getActionForBatteryTip(
batteryTip, mSettingsActivity, mFragment);
if (action != null) { if (action != null) {
action.handlePositiveAction(mFragment.getMetricsCategory()); action.handlePositiveAction(mFragment.getMetricsCategory());
} }
@@ -183,13 +181,11 @@ public class BatteryTipPreferenceController extends BasePreferenceController {
return visibleBatteryTip.orElse(null); return visibleBatteryTip.orElse(null);
} }
/** /** Listener to give the control back to target fragment */
* Listener to give the control back to target fragment
*/
public interface BatteryTipListener { public interface BatteryTipListener {
/** /**
* This method is invoked once battery tip is handled, then target fragment could do * This method is invoked once battery tip is handled, then target fragment could do extra
* extra work. * work.
* *
* @param batteryTip that has been handled * @param batteryTip that has been handled
*/ */

View File

@@ -48,15 +48,13 @@ import java.util.List;
public class BatteryTipUtils { public class BatteryTipUtils {
private static final int REQUEST_CODE = 0; private static final int REQUEST_CODE = 0;
/** /** Get a list of restricted apps with {@link AppOpsManager#OP_RUN_ANY_IN_BACKGROUND} */
* Get a list of restricted apps with {@link AppOpsManager#OP_RUN_ANY_IN_BACKGROUND}
*/
@NonNull @NonNull
public static List<AppInfo> getRestrictedAppsList(AppOpsManager appOpsManager, public static List<AppInfo> getRestrictedAppsList(
UserManager userManager) { AppOpsManager appOpsManager, UserManager userManager) {
final List<UserHandle> userHandles = userManager.getUserProfiles(); final List<UserHandle> userHandles = userManager.getUserProfiles();
final List<AppOpsManager.PackageOps> packageOpsList = appOpsManager.getPackagesForOps( final List<AppOpsManager.PackageOps> packageOpsList =
new int[]{AppOpsManager.OP_RUN_ANY_IN_BACKGROUND}); appOpsManager.getPackagesForOps(new int[] {AppOpsManager.OP_RUN_ANY_IN_BACKGROUND});
final List<AppInfo> appInfos = new ArrayList<>(); final List<AppInfo> appInfos = new ArrayList<>();
for (int i = 0, size = CollectionUtils.size(packageOpsList); i < size; i++) { for (int i = 0, size = CollectionUtils.size(packageOpsList); i < size; i++) {
@@ -69,11 +67,12 @@ public class BatteryTipUtils {
} }
if (entry.getMode() != AppOpsManager.MODE_ALLOWED if (entry.getMode() != AppOpsManager.MODE_ALLOWED
&& userHandles.contains( && userHandles.contains(
new UserHandle(UserHandle.getUserId(packageOps.getUid())))) { new UserHandle(UserHandle.getUserId(packageOps.getUid())))) {
appInfos.add(new AppInfo.Builder() appInfos.add(
.setPackageName(packageOps.getPackageName()) new AppInfo.Builder()
.setUid(packageOps.getUid()) .setPackageName(packageOps.getPackageName())
.build()); .setUid(packageOps.getUid())
.build());
} }
} }
} }
@@ -89,8 +88,10 @@ public class BatteryTipUtils {
* @param fragment used to populate {@link BatteryTipAction} * @param fragment used to populate {@link BatteryTipAction}
* @return an action for {@code batteryTip} * @return an action for {@code batteryTip}
*/ */
public static BatteryTipAction getActionForBatteryTip(BatteryTip batteryTip, public static BatteryTipAction getActionForBatteryTip(
SettingsActivity settingsActivity, InstrumentedPreferenceFragment fragment) { BatteryTip batteryTip,
SettingsActivity settingsActivity,
InstrumentedPreferenceFragment fragment) {
switch (batteryTip.getType()) { switch (batteryTip.getType()) {
case BatteryTip.TipType.SMART_BATTERY_MANAGER: case BatteryTip.TipType.SMART_BATTERY_MANAGER:
return new SmartBatteryAction(settingsActivity, fragment); return new SmartBatteryAction(settingsActivity, fragment);
@@ -112,26 +113,33 @@ public class BatteryTipUtils {
/** /**
* Upload the {@link PendingIntent} to {@link StatsManager} for anomaly detection * Upload the {@link PendingIntent} to {@link StatsManager} for anomaly detection
*
* @throws StatsManager.StatsUnavailableException if failed to communicate with stats service * @throws StatsManager.StatsUnavailableException if failed to communicate with stats service
*/ */
public static void uploadAnomalyPendingIntent(Context context, StatsManager statsManager) public static void uploadAnomalyPendingIntent(Context context, StatsManager statsManager)
throws StatsManager.StatsUnavailableException { throws StatsManager.StatsUnavailableException {
final Intent extraIntent = new Intent(context, AnomalyDetectionReceiver.class); final Intent extraIntent = new Intent(context, AnomalyDetectionReceiver.class);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, final PendingIntent pendingIntent =
extraIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); PendingIntent.getBroadcast(
statsManager.setBroadcastSubscriber(pendingIntent, context,
StatsManagerConfig.ANOMALY_CONFIG_KEY, StatsManagerConfig.SUBSCRIBER_ID); REQUEST_CODE,
extraIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
statsManager.setBroadcastSubscriber(
pendingIntent,
StatsManagerConfig.ANOMALY_CONFIG_KEY,
StatsManagerConfig.SUBSCRIBER_ID);
} }
/** /** Detect and return anomaly apps after {@code timeAfterMs} */
* Detect and return anomaly apps after {@code timeAfterMs}
*/
public static List<AppInfo> detectAnomalies(Context context, long timeAfterMs) { public static List<AppInfo> detectAnomalies(Context context, long timeAfterMs) {
final List<AppInfo> highUsageApps = BatteryDatabaseManager.getInstance(context) final List<AppInfo> highUsageApps =
.queryAllAnomalies(timeAfterMs, AnomalyDatabaseHelper.State.NEW); BatteryDatabaseManager.getInstance(context)
.queryAllAnomalies(timeAfterMs, AnomalyDatabaseHelper.State.NEW);
// Remove it if it doesn't have label or been restricted // Remove it if it doesn't have label or been restricted
highUsageApps.removeIf(AppLabelPredicate.getInstance(context) highUsageApps.removeIf(
.or(AppRestrictionPredicate.getInstance(context))); AppLabelPredicate.getInstance(context)
.or(AppRestrictionPredicate.getInstance(context)));
return highUsageApps; return highUsageApps;
} }

View File

@@ -33,9 +33,7 @@ import com.android.settings.Utils;
import java.util.List; import java.util.List;
/** /** Adapter for the high usage app list */
* Adapter for the high usage app list
*/
public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.ViewHolder> { public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.ViewHolder> {
private final Context mContext; private final Context mContext;
private final IconDrawableFactory mIconDrawableFactory; private final IconDrawableFactory mIconDrawableFactory;
@@ -66,8 +64,8 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
@Override @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.app_high_usage_item, final View view =
parent, false); LayoutInflater.from(mContext).inflate(R.layout.app_high_usage_item, parent, false);
return new ViewHolder(view); return new ViewHolder(view);
} }
@@ -75,7 +73,10 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
public void onBindViewHolder(ViewHolder holder, int position) { public void onBindViewHolder(ViewHolder holder, int position) {
final AppInfo app = mHighUsageAppList.get(position); final AppInfo app = mHighUsageAppList.get(position);
holder.appIcon.setImageDrawable( holder.appIcon.setImageDrawable(
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName, Utils.getBadgedIcon(
mIconDrawableFactory,
mPackageManager,
app.packageName,
UserHandle.getUserId(app.uid))); UserHandle.getUserId(app.uid)));
CharSequence label = Utils.getApplicationLabel(mContext, app.packageName); CharSequence label = Utils.getApplicationLabel(mContext, app.packageName);
if (label == null) { if (label == null) {
@@ -89,4 +90,4 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
public int getItemCount() { public int getItemCount() {
return mHighUsageAppList.size(); return mHighUsageAppList.size();
} }
} }

View File

@@ -20,20 +20,17 @@ import android.os.BatteryStats;
import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryInfo;
/** /** DataParser used to go through battery data and detect whether battery is heavily used. */
* DataParser used to go through battery data and detect whether battery is
* heavily used.
*/
public class HighUsageDataParser implements BatteryInfo.BatteryDataParser { public class HighUsageDataParser implements BatteryInfo.BatteryDataParser {
/** /** Time period to check the battery usage */
* time period to check the battery usage
*/
private final long mTimePeriodMs; private final long mTimePeriodMs;
/** /**
* treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1% * Treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1%
* battery usage. * battery usage.
*/ */
private int mThreshold; private int mThreshold;
private long mEndTimeMs; private long mEndTimeMs;
private byte mEndBatteryLevel; private byte mEndBatteryLevel;
private byte mLastPeriodBatteryLevel; private byte mLastPeriodBatteryLevel;
@@ -69,11 +66,8 @@ public class HighUsageDataParser implements BatteryInfo.BatteryDataParser {
mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel; mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel;
} }
/** /** Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much */
* Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much
*/
public boolean isDeviceHeavilyUsed() { public boolean isDeviceHeavilyUsed() {
return mBatteryDrain > mThreshold; return mBatteryDrain > mThreshold;
} }
} }

View File

@@ -21,244 +21,197 @@ import androidx.annotation.IntDef;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** /** This class provides all the configs needed if we want to use {@link android.app.StatsManager} */
* This class provides all the configs needed if we want to use {@link android.app.StatsManager}
*/
public class StatsManagerConfig { public class StatsManagerConfig {
/** /**
* The key that represents the anomaly config. * The key that represents the anomaly config. This value is used in {@link
* This value is used in {@link android.app.StatsManager#addConfig(long, byte[])} * android.app.StatsManager#addConfig(long, byte[])}
*/ */
public static final long ANOMALY_CONFIG_KEY = 1; public static final long ANOMALY_CONFIG_KEY = 1;
/** /** The key that represents subscriber, which is settings app. */
* The key that represents subscriber, which is settings app.
*/
public static final long SUBSCRIBER_ID = 1; public static final long SUBSCRIBER_ID = 1;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({AnomalyType.NULL, @IntDef({
AnomalyType.UNKNOWN_REASON, AnomalyType.NULL,
AnomalyType.EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF, AnomalyType.UNKNOWN_REASON,
AnomalyType.EXCESSIVE_WAKEUPS_IN_BACKGROUND, AnomalyType.EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF,
AnomalyType.EXCESSIVE_UNOPTIMIZED_BLE_SCAN, AnomalyType.EXCESSIVE_WAKEUPS_IN_BACKGROUND,
AnomalyType.EXCESSIVE_BACKGROUND_SERVICE, AnomalyType.EXCESSIVE_UNOPTIMIZED_BLE_SCAN,
AnomalyType.EXCESSIVE_WIFI_SCAN, AnomalyType.EXCESSIVE_BACKGROUND_SERVICE,
AnomalyType.EXCESSIVE_FLASH_WRITES, AnomalyType.EXCESSIVE_WIFI_SCAN,
AnomalyType.EXCESSIVE_MEMORY_IN_BACKGROUND, AnomalyType.EXCESSIVE_FLASH_WRITES,
AnomalyType.EXCESSIVE_DAVEY_RATE, AnomalyType.EXCESSIVE_MEMORY_IN_BACKGROUND,
AnomalyType.EXCESSIVE_JANKY_FRAMES, AnomalyType.EXCESSIVE_DAVEY_RATE,
AnomalyType.SLOW_COLD_START_TIME, AnomalyType.EXCESSIVE_JANKY_FRAMES,
AnomalyType.SLOW_HOT_START_TIME, AnomalyType.SLOW_COLD_START_TIME,
AnomalyType.SLOW_WARM_START_TIME, AnomalyType.SLOW_HOT_START_TIME,
AnomalyType.EXCESSIVE_BACKGROUND_SYNCS, AnomalyType.SLOW_WARM_START_TIME,
AnomalyType.EXCESSIVE_GPS_SCANS_IN_BACKGROUND, AnomalyType.EXCESSIVE_BACKGROUND_SYNCS,
AnomalyType.EXCESSIVE_JOB_SCHEDULING, AnomalyType.EXCESSIVE_GPS_SCANS_IN_BACKGROUND,
AnomalyType.EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND, AnomalyType.EXCESSIVE_JOB_SCHEDULING,
AnomalyType.EXCESSIVE_WIFI_LOCK_TIME, AnomalyType.EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND,
AnomalyType.JOB_TIMED_OUT, AnomalyType.EXCESSIVE_WIFI_LOCK_TIME,
AnomalyType.LONG_UNOPTIMIZED_BLE_SCAN, AnomalyType.JOB_TIMED_OUT,
AnomalyType.BACKGROUND_ANR, AnomalyType.LONG_UNOPTIMIZED_BLE_SCAN,
AnomalyType.BACKGROUND_CRASH_RATE, AnomalyType.BACKGROUND_ANR,
AnomalyType.EXCESSIVE_ANR_LOOPING, AnomalyType.BACKGROUND_CRASH_RATE,
AnomalyType.EXCESSIVE_ANRS, AnomalyType.EXCESSIVE_ANR_LOOPING,
AnomalyType.EXCESSIVE_CRASH_RATE, AnomalyType.EXCESSIVE_ANRS,
AnomalyType.EXCESSIVE_CRASH_LOOPING, AnomalyType.EXCESSIVE_CRASH_RATE,
AnomalyType.NUMBER_OF_OPEN_FILES, AnomalyType.EXCESSIVE_CRASH_LOOPING,
AnomalyType.EXCESSIVE_CAMERA_USAGE_IN_BACKGROUND, AnomalyType.NUMBER_OF_OPEN_FILES,
AnomalyType.EXCESSIVE_CONTACT_ACCESS, AnomalyType.EXCESSIVE_CAMERA_USAGE_IN_BACKGROUND,
AnomalyType.EXCESSIVE_AUDIO_IN_BACKGROUND, AnomalyType.EXCESSIVE_CONTACT_ACCESS,
AnomalyType.EXCESSIVE_CRASH_ANR_IN_BACKGROUND, AnomalyType.EXCESSIVE_AUDIO_IN_BACKGROUND,
AnomalyType.BATTERY_DRAIN_FROM_UNUSED_APP, AnomalyType.EXCESSIVE_CRASH_ANR_IN_BACKGROUND,
AnomalyType.BATTERY_DRAIN_FROM_UNUSED_APP,
}) })
public @interface AnomalyType { public @interface AnomalyType {
/** /** This represents an error condition in the anomaly detection. */
* This represents an error condition in the anomaly detection.
*/
int NULL = -1; int NULL = -1;
/** /** The anomaly type does not match any other defined type. */
* The anomaly type does not match any other defined type.
*/
int UNKNOWN_REASON = 0; int UNKNOWN_REASON = 0;
/** /**
* The application held a partial (screen off) wake lock for a period of time that * The application held a partial (screen off) wake lock for a period of time that exceeded
* exceeded the threshold with the screen off when not charging. * the threshold with the screen off when not charging.
*/ */
int EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF = 1; int EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF = 1;
/** /**
* The application exceeded the maximum number of wakeups while in the background * The application exceeded the maximum number of wakeups while in the background when not
* when not charging. * charging.
*/ */
int EXCESSIVE_WAKEUPS_IN_BACKGROUND = 2; int EXCESSIVE_WAKEUPS_IN_BACKGROUND = 2;
/** /** The application did unoptimized Bluetooth scans too frequently when not charging. */
* The application did unoptimized Bluetooth scans too frequently when not charging.
*/
int EXCESSIVE_UNOPTIMIZED_BLE_SCAN = 3; int EXCESSIVE_UNOPTIMIZED_BLE_SCAN = 3;
/** /**
* The application ran in the background for a period of time that exceeded the * The application ran in the background for a period of time that exceeded the threshold.
* threshold.
*/ */
int EXCESSIVE_BACKGROUND_SERVICE = 4; int EXCESSIVE_BACKGROUND_SERVICE = 4;
/** /** The application exceeded the maximum number of wifi scans when not charging. */
* The application exceeded the maximum number of wifi scans when not charging.
*/
int EXCESSIVE_WIFI_SCAN = 5; int EXCESSIVE_WIFI_SCAN = 5;
/** /** The application exceed the maximum number of flash writes */
* The application exceed the maximum number of flash writes
*/
int EXCESSIVE_FLASH_WRITES = 6; int EXCESSIVE_FLASH_WRITES = 6;
/** /**
* The application used more than the maximum memory, while not spending any time * The application used more than the maximum memory, while not spending any time in the
* in the foreground. * foreground.
*/ */
int EXCESSIVE_MEMORY_IN_BACKGROUND = 7; int EXCESSIVE_MEMORY_IN_BACKGROUND = 7;
/** /**
* The application exceeded the maximum percentage of frames with a render rate of * The application exceeded the maximum percentage of frames with a render rate of greater
* greater than 700ms. * than 700ms.
*/ */
int EXCESSIVE_DAVEY_RATE = 8; int EXCESSIVE_DAVEY_RATE = 8;
/** /**
* The application exceeded the maximum percentage of frames with a render rate * The application exceeded the maximum percentage of frames with a render rate greater than
* greater than 16ms. * 16ms.
*/ */
int EXCESSIVE_JANKY_FRAMES = 9; int EXCESSIVE_JANKY_FRAMES = 9;
/** /**
* The application exceeded the maximum cold start time - the app has not been * The application exceeded the maximum cold start time - the app has not been launched
* launched since last system start, died or was killed. * since last system start, died or was killed.
*/ */
int SLOW_COLD_START_TIME = 10; int SLOW_COLD_START_TIME = 10;
/** /**
* The application exceeded the maximum hot start time - the app and activity are * The application exceeded the maximum hot start time - the app and activity are already in
* already in memory. * memory.
*/ */
int SLOW_HOT_START_TIME = 11; int SLOW_HOT_START_TIME = 11;
/** /**
* The application exceeded the maximum warm start time - the app was already in * The application exceeded the maximum warm start time - the app was already in memory but
* memory but the activity wasnt created yet or was removed from memory. * the activity wasnt created yet or was removed from memory.
*/ */
int SLOW_WARM_START_TIME = 12; int SLOW_WARM_START_TIME = 12;
/** /** The application exceeded the maximum number of syncs while in the background. */
* The application exceeded the maximum number of syncs while in the background.
*/
int EXCESSIVE_BACKGROUND_SYNCS = 13; int EXCESSIVE_BACKGROUND_SYNCS = 13;
/** /** The application exceeded the maximum number of gps scans while in the background. */
* The application exceeded the maximum number of gps scans while in the background.
*/
int EXCESSIVE_GPS_SCANS_IN_BACKGROUND = 14; int EXCESSIVE_GPS_SCANS_IN_BACKGROUND = 14;
/** /** The application scheduled more than the maximum number of jobs while not charging. */
* The application scheduled more than the maximum number of jobs while not charging.
*/
int EXCESSIVE_JOB_SCHEDULING = 15; int EXCESSIVE_JOB_SCHEDULING = 15;
/** /**
* The application exceeded the maximum amount of mobile network traffic while in * The application exceeded the maximum amount of mobile network traffic while in the
* the background. * background.
*/ */
int EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND = 16; int EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND = 16;
/** /**
* The application held the WiFi lock for more than the maximum amount of time while * The application held the WiFi lock for more than the maximum amount of time while not
* not charging. * charging.
*/ */
int EXCESSIVE_WIFI_LOCK_TIME = 17; int EXCESSIVE_WIFI_LOCK_TIME = 17;
/** /** The application scheduled a job that ran longer than the maximum amount of time. */
* The application scheduled a job that ran longer than the maximum amount of time.
*/
int JOB_TIMED_OUT = 18; int JOB_TIMED_OUT = 18;
/** /**
* The application did an unoptimized Bluetooth scan that exceeded the maximum * The application did an unoptimized Bluetooth scan that exceeded the maximum time while in
* time while in the background. * the background.
*/ */
int LONG_UNOPTIMIZED_BLE_SCAN = 19; int LONG_UNOPTIMIZED_BLE_SCAN = 19;
/** /** The application exceeded the maximum ANR rate while in the background. */
* The application exceeded the maximum ANR rate while in the background.
*/
int BACKGROUND_ANR = 20; int BACKGROUND_ANR = 20;
/** /** The application exceeded the maximum crash rate while in the background. */
* The application exceeded the maximum crash rate while in the background.
*/
int BACKGROUND_CRASH_RATE = 21; int BACKGROUND_CRASH_RATE = 21;
/** /** The application exceeded the maximum ANR-looping rate. */
* The application exceeded the maximum ANR-looping rate.
*/
int EXCESSIVE_ANR_LOOPING = 22; int EXCESSIVE_ANR_LOOPING = 22;
/** /** The application exceeded the maximum ANR rate. */
* The application exceeded the maximum ANR rate.
*/
int EXCESSIVE_ANRS = 23; int EXCESSIVE_ANRS = 23;
/** /** The application exceeded the maximum crash rate. */
* The application exceeded the maximum crash rate.
*/
int EXCESSIVE_CRASH_RATE = 24; int EXCESSIVE_CRASH_RATE = 24;
/** /** The application exceeded the maximum crash-looping rate. */
* The application exceeded the maximum crash-looping rate.
*/
int EXCESSIVE_CRASH_LOOPING = 25; int EXCESSIVE_CRASH_LOOPING = 25;
/** /** The application crashed because no more file descriptors were available. */
* The application crashed because no more file descriptors were available.
*/
int NUMBER_OF_OPEN_FILES = 26; int NUMBER_OF_OPEN_FILES = 26;
/** /** The application used an excessive amount of CPU while in a background process state. */
* The application used an excessive amount of CPU while in a
* background process state.
*/
int EXCESSIVE_CPU_USAGE_IN_BACKGROUND = 27; int EXCESSIVE_CPU_USAGE_IN_BACKGROUND = 27;
/** /**
* The application kept the camera open for an excessive amount * The application kept the camera open for an excessive amount of time while in a bckground
* of time while in a bckground process state. * process state.
*/ */
int EXCESSIVE_CAMERA_USAGE_IN_BACKGROUND = 28; int EXCESSIVE_CAMERA_USAGE_IN_BACKGROUND = 28;
/** /** The application has accessed the contacts content provider an excessive amount. */
* The application has accessed the contacts content provider an
* excessive amount.
*/
int EXCESSIVE_CONTACT_ACCESS = 29; int EXCESSIVE_CONTACT_ACCESS = 29;
/** /** The application has played too much audio while in a background process state. */
* The application has played too much audio while in a background
* process state.
*/
int EXCESSIVE_AUDIO_IN_BACKGROUND = 30; int EXCESSIVE_AUDIO_IN_BACKGROUND = 30;
/** /**
* The application has crashed or ANRed too many times while in a * The application has crashed or ANRed too many times while in a background process state.
* background process state.
*/ */
int EXCESSIVE_CRASH_ANR_IN_BACKGROUND = 31; int EXCESSIVE_CRASH_ANR_IN_BACKGROUND = 31;
/** /**
* An application which has not been used by the user recently * An application which has not been used by the user recently was detected to cause an
* was detected to cause an excessive amount of battery drain. * excessive amount of battery drain.
*/ */
int BATTERY_DRAIN_FROM_UNUSED_APP = 32; int BATTERY_DRAIN_FROM_UNUSED_APP = 32;
} }
} }

View File

@@ -28,14 +28,12 @@ public class BatterySaverAction extends BatteryTipAction {
super(context); super(context);
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
@Override @Override
public void handlePositiveAction(int metricsKey) { public void handlePositiveAction(int metricsKey) {
BatterySaverUtils.setPowerSaveMode(mContext, true, /*needFirstTimeWarning*/ true, BatterySaverUtils.setPowerSaveMode(
SAVER_ENABLED_UNKNOWN); mContext, true, /*needFirstTimeWarning*/ true, SAVER_ENABLED_UNKNOWN);
mMetricsFeatureProvider.action(mContext, mMetricsFeatureProvider.action(
SettingsEnums.ACTION_TIP_TURN_ON_BATTERY_SAVER, metricsKey); mContext, SettingsEnums.ACTION_TIP_TURN_ON_BATTERY_SAVER, metricsKey);
} }
} }

View File

@@ -33,8 +33,6 @@ public abstract class BatteryTipAction {
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
public abstract void handlePositiveAction(int metricsKey); public abstract void handlePositiveAction(int metricsKey);
} }

View File

@@ -22,23 +22,18 @@ import android.content.Context;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings; import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
/** /** Action to open the {@link com.android.settings.fuelgauge.batterysaver.BatterySaverSettings} */
*
* Action to open the {@link com.android.settings.fuelgauge.batterysaver.BatterySaverSettings}
*/
public class OpenBatterySaverAction extends BatteryTipAction { public class OpenBatterySaverAction extends BatteryTipAction {
public OpenBatterySaverAction(Context context) { public OpenBatterySaverAction(Context context) {
super(context); super(context);
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
@Override @Override
public void handlePositiveAction(int metricsKey) { public void handlePositiveAction(int metricsKey) {
mMetricsFeatureProvider.action(mContext, mMetricsFeatureProvider.action(
SettingsEnums.ACTION_TIP_OPEN_BATTERY_SAVER_PAGE, metricsKey); mContext, SettingsEnums.ACTION_TIP_OPEN_BATTERY_SAVER_PAGE, metricsKey);
new SubSettingLauncher(mContext) new SubSettingLauncher(mContext)
.setDestination(BatterySaverSettings.class.getName()) .setDestination(BatterySaverSettings.class.getName())
.setSourceMetricsCategory(metricsKey) .setSourceMetricsCategory(metricsKey)

View File

@@ -30,35 +30,32 @@ import com.android.settingslib.utils.ThreadUtils;
import java.util.List; import java.util.List;
/** /** Action to open the {@link com.android.settings.fuelgauge.RestrictedAppDetails} */
* Action to open the {@link com.android.settings.fuelgauge.RestrictedAppDetails}
*/
public class OpenRestrictAppFragmentAction extends BatteryTipAction { public class OpenRestrictAppFragmentAction extends BatteryTipAction {
private final RestrictAppTip mRestrictAppTip; private final RestrictAppTip mRestrictAppTip;
private final InstrumentedPreferenceFragment mFragment; private final InstrumentedPreferenceFragment mFragment;
@VisibleForTesting @VisibleForTesting BatteryDatabaseManager mBatteryDatabaseManager;
BatteryDatabaseManager mBatteryDatabaseManager;
public OpenRestrictAppFragmentAction(InstrumentedPreferenceFragment fragment, public OpenRestrictAppFragmentAction(
RestrictAppTip tip) { InstrumentedPreferenceFragment fragment, RestrictAppTip tip) {
super(fragment.getContext()); super(fragment.getContext());
mFragment = fragment; mFragment = fragment;
mRestrictAppTip = tip; mRestrictAppTip = tip;
mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(mContext); mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(mContext);
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
@Override @Override
public void handlePositiveAction(int metricsKey) { public void handlePositiveAction(int metricsKey) {
mMetricsFeatureProvider.action(mContext, mMetricsFeatureProvider.action(
SettingsEnums.ACTION_TIP_OPEN_APP_RESTRICTION_PAGE, metricsKey); mContext, SettingsEnums.ACTION_TIP_OPEN_APP_RESTRICTION_PAGE, metricsKey);
final List<AppInfo> mAppInfos = mRestrictAppTip.getRestrictAppList(); final List<AppInfo> mAppInfos = mRestrictAppTip.getRestrictAppList();
RestrictedAppDetails.startRestrictedAppDetails(mFragment, mAppInfos); RestrictedAppDetails.startRestrictedAppDetails(mFragment, mAppInfos);
// Mark all the anomalies as handled, so it won't show up again. // Mark all the anomalies as handled, so it won't show up again.
ThreadUtils.postOnBackgroundThread(() -> mBatteryDatabaseManager.updateAnomalies(mAppInfos, ThreadUtils.postOnBackgroundThread(
AnomalyDatabaseHelper.State.HANDLED)); () ->
mBatteryDatabaseManager.updateAnomalies(
mAppInfos, AnomalyDatabaseHelper.State.HANDLED));
} }
} }

View File

@@ -31,15 +31,11 @@ import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
import java.util.List; import java.util.List;
/** /** Action to restrict the apps, then app is not allowed to run in the background. */
* Action to restrict the apps, then app is not allowed to run in the background.
*/
public class RestrictAppAction extends BatteryTipAction { public class RestrictAppAction extends BatteryTipAction {
private RestrictAppTip mRestrictAppTip; private RestrictAppTip mRestrictAppTip;
@VisibleForTesting @VisibleForTesting BatteryDatabaseManager mBatteryDatabaseManager;
BatteryDatabaseManager mBatteryDatabaseManager; @VisibleForTesting BatteryUtils mBatteryUtils;
@VisibleForTesting
BatteryUtils mBatteryUtils;
public RestrictAppAction(Context context, RestrictAppTip tip) { public RestrictAppAction(Context context, RestrictAppTip tip) {
super(context); super(context);
@@ -48,9 +44,7 @@ public class RestrictAppAction extends BatteryTipAction {
mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context); mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context);
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
@Override @Override
public void handlePositiveAction(int metricsKey) { public void handlePositiveAction(int metricsKey) {
final List<AppInfo> appInfos = mRestrictAppTip.getRestrictAppList(); final List<AppInfo> appInfos = mRestrictAppTip.getRestrictAppList();
@@ -59,18 +53,19 @@ public class RestrictAppAction extends BatteryTipAction {
final AppInfo appInfo = appInfos.get(i); final AppInfo appInfo = appInfos.get(i);
final String packageName = appInfo.packageName; final String packageName = appInfo.packageName;
// Force app standby, then app can't run in the background // Force app standby, then app can't run in the background
mBatteryUtils.setForceAppStandby(appInfo.uid, packageName, mBatteryUtils.setForceAppStandby(appInfo.uid, packageName, AppOpsManager.MODE_IGNORED);
AppOpsManager.MODE_IGNORED);
if (CollectionUtils.isEmpty(appInfo.anomalyTypes)) { if (CollectionUtils.isEmpty(appInfo.anomalyTypes)) {
// Only log context if there is no anomaly type // Only log context if there is no anomaly type
mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, mMetricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_TIP_RESTRICT_APP, SettingsEnums.ACTION_TIP_RESTRICT_APP,
metricsKey, metricsKey,
packageName, packageName,
0); 0);
} else { } else {
for (int type : appInfo.anomalyTypes) { for (int type : appInfo.anomalyTypes) {
mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, mMetricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_TIP_RESTRICT_APP, SettingsEnums.ACTION_TIP_RESTRICT_APP,
metricsKey, metricsKey,
packageName, packageName,

View File

@@ -36,20 +36,18 @@ public class SmartBatteryAction extends BatteryTipAction {
mFragment = fragment; mFragment = fragment;
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
@Override @Override
public void handlePositiveAction(int metricsKey) { public void handlePositiveAction(int metricsKey) {
mMetricsFeatureProvider.action(mContext, mMetricsFeatureProvider.action(
SettingsEnums.ACTION_TIP_OPEN_SMART_BATTERY, metricsKey); mContext, SettingsEnums.ACTION_TIP_OPEN_SMART_BATTERY, metricsKey);
new SubSettingLauncher(mSettingsActivity) new SubSettingLauncher(mSettingsActivity)
.setSourceMetricsCategory(mFragment instanceof Instrumentable .setSourceMetricsCategory(
? ((Instrumentable) mFragment).getMetricsCategory() mFragment instanceof Instrumentable
: Instrumentable.METRICS_CATEGORY_UNKNOWN) ? ((Instrumentable) mFragment).getMetricsCategory()
: Instrumentable.METRICS_CATEGORY_UNKNOWN)
.setDestination(SmartBatterySettings.class.getName()) .setDestination(SmartBatterySettings.class.getName())
.setTitleRes(R.string.smart_battery_manager_title) .setTitleRes(R.string.smart_battery_manager_title)
.launch(); .launch();
} }
} }

View File

@@ -26,13 +26,10 @@ import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.AppInfo; import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
/** /** Action to clear the restriction to the app */
* Action to clear the restriction to the app
*/
public class UnrestrictAppAction extends BatteryTipAction { public class UnrestrictAppAction extends BatteryTipAction {
private UnrestrictAppTip mUnRestrictAppTip; private UnrestrictAppTip mUnRestrictAppTip;
@VisibleForTesting @VisibleForTesting BatteryUtils mBatteryUtils;
BatteryUtils mBatteryUtils;
public UnrestrictAppAction(Context context, UnrestrictAppTip tip) { public UnrestrictAppAction(Context context, UnrestrictAppTip tip) {
super(context); super(context);
@@ -40,15 +37,13 @@ public class UnrestrictAppAction extends BatteryTipAction {
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
} }
/** /** Handle the action when user clicks positive button */
* Handle the action when user clicks positive button
*/
@Override @Override
public void handlePositiveAction(int metricsKey) { public void handlePositiveAction(int metricsKey) {
final AppInfo appInfo = mUnRestrictAppTip.getUnrestrictAppInfo(); final AppInfo appInfo = mUnRestrictAppTip.getUnrestrictAppInfo();
// Clear force app standby, then app can run in the background // Clear force app standby, then app can run in the background
mBatteryUtils.setForceAppStandby(appInfo.uid, appInfo.packageName, mBatteryUtils.setForceAppStandby(
AppOpsManager.MODE_ALLOWED); appInfo.uid, appInfo.packageName, AppOpsManager.MODE_ALLOWED);
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN, SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_TIP_UNRESTRICT_APP, SettingsEnums.ACTION_TIP_UNRESTRICT_APP,

View File

@@ -23,9 +23,7 @@ import com.android.settings.fuelgauge.batterytip.tips.BatteryDefenderTip;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
/** /** Detect whether the battery is overheated */
* Detect whether the battery is overheated
*/
public class BatteryDefenderDetector implements BatteryTipDetector { public class BatteryDefenderDetector implements BatteryTipDetector {
private final BatteryInfo mBatteryInfo; private final BatteryInfo mBatteryInfo;
private final Context mContext; private final Context mContext;
@@ -37,11 +35,13 @@ public class BatteryDefenderDetector implements BatteryTipDetector {
@Override @Override
public BatteryTip detect() { public BatteryTip detect() {
final boolean isBasicBatteryDefend = mBatteryInfo.isBatteryDefender final boolean isBasicBatteryDefend =
&& !FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider() mBatteryInfo.isBatteryDefender
.isExtraDefend(); && !FeatureFactory.getFeatureFactory()
final int state = isBasicBatteryDefend .getPowerUsageFeatureProvider()
? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE; .isExtraDefend();
final int state =
isBasicBatteryDefend ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE;
final boolean isPluggedIn = mBatteryInfo.pluggedStatus != 0; final boolean isPluggedIn = mBatteryInfo.pluggedStatus != 0;
return new BatteryDefenderTip(state, isPluggedIn); return new BatteryDefenderTip(state, isPluggedIn);
} }

View File

@@ -23,9 +23,7 @@ import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.DockDefenderTip; import com.android.settings.fuelgauge.batterytip.tips.DockDefenderTip;
/** /** Detect whether the dock defender mode is enabled. */
* Detect whether the dock defender mode is enabled.
*/
public class DockDefenderDetector implements BatteryTipDetector { public class DockDefenderDetector implements BatteryTipDetector {
private final BatteryInfo mBatteryInfo; private final BatteryInfo mBatteryInfo;
private final Context mContext; private final Context mContext;
@@ -44,5 +42,4 @@ public class DockDefenderDetector implements BatteryTipDetector {
: BatteryTip.StateType.INVISIBLE, : BatteryTip.StateType.INVISIBLE,
mode); mode);
} }
} }

View File

@@ -38,8 +38,8 @@ import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* Detector whether to show summary tip. This detector should be executed as the last * Detector whether to show summary tip. This detector should be executed as the last {@link
* {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips} * BatteryTipDetector} since it need the most up-to-date {@code visibleTips}
*/ */
public class HighUsageDetector implements BatteryTipDetector { public class HighUsageDetector implements BatteryTipDetector {
private static final String TAG = "HighUsageDetector"; private static final String TAG = "HighUsageDetector";
@@ -48,29 +48,31 @@ public class HighUsageDetector implements BatteryTipDetector {
private BatteryUsageStats mBatteryUsageStats; private BatteryUsageStats mBatteryUsageStats;
private final BatteryInfo mBatteryInfo; private final BatteryInfo mBatteryInfo;
private List<AppInfo> mHighUsageAppList; private List<AppInfo> mHighUsageAppList;
@VisibleForTesting @VisibleForTesting HighUsageDataParser mDataParser;
HighUsageDataParser mDataParser; @VisibleForTesting BatteryUtils mBatteryUtils;
@VisibleForTesting @VisibleForTesting boolean mDischarging;
BatteryUtils mBatteryUtils;
@VisibleForTesting
boolean mDischarging;
public HighUsageDetector(Context context, BatteryTipPolicy policy, public HighUsageDetector(
BatteryUsageStats batteryUsageStats, BatteryInfo batteryInfo) { Context context,
BatteryTipPolicy policy,
BatteryUsageStats batteryUsageStats,
BatteryInfo batteryInfo) {
mPolicy = policy; mPolicy = policy;
mBatteryUsageStats = batteryUsageStats; mBatteryUsageStats = batteryUsageStats;
mBatteryInfo = batteryInfo; mBatteryInfo = batteryInfo;
mHighUsageAppList = new ArrayList<>(); mHighUsageAppList = new ArrayList<>();
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
mDataParser = new HighUsageDataParser(mPolicy.highUsagePeriodMs, mDataParser =
mPolicy.highUsageBatteryDraining); new HighUsageDataParser(
mPolicy.highUsagePeriodMs, mPolicy.highUsageBatteryDraining);
mDischarging = batteryInfo.discharging; mDischarging = batteryInfo.discharging;
} }
@Override @Override
public BatteryTip detect() { public BatteryTip detect() {
final long lastFullChargeTimeMs = mBatteryUtils.calculateLastFullChargeTime( final long lastFullChargeTimeMs =
mBatteryUsageStats, System.currentTimeMillis()); mBatteryUtils.calculateLastFullChargeTime(
mBatteryUsageStats, System.currentTimeMillis());
if (mPolicy.highUsageEnabled && mDischarging) { if (mPolicy.highUsageEnabled && mDischarging) {
parseBatteryData(); parseBatteryData();
if (mDataParser.isDeviceHeavilyUsed() || mPolicy.testHighUsageTip) { if (mDataParser.isDeviceHeavilyUsed() || mPolicy.testHighUsageTip) {
@@ -80,22 +82,25 @@ public class HighUsageDetector implements BatteryTipDetector {
mBatteryUsageStats.getUidBatteryConsumers(); mBatteryUsageStats.getUidBatteryConsumers();
// Sort by descending power // Sort by descending power
uidBatteryConsumers.sort( uidBatteryConsumers.sort(
(consumer1, consumer2) -> Double.compare(consumer2.getConsumedPower(), (consumer1, consumer2) ->
consumer1.getConsumedPower())); Double.compare(
consumer2.getConsumedPower(),
consumer1.getConsumedPower()));
for (UidBatteryConsumer consumer : uidBatteryConsumers) { for (UidBatteryConsumer consumer : uidBatteryConsumers) {
final double percent = mBatteryUtils.calculateBatteryPercent( final double percent =
consumer.getConsumedPower(), totalPower, dischargeAmount); mBatteryUtils.calculateBatteryPercent(
consumer.getConsumedPower(), totalPower, dischargeAmount);
if ((percent + 0.5f < 1f) if ((percent + 0.5f < 1f)
|| mBatteryUtils.shouldHideUidBatteryConsumer(consumer)) { || mBatteryUtils.shouldHideUidBatteryConsumer(consumer)) {
// Don't show it if we should hide or usage percentage is lower than 1% // Don't show it if we should hide or usage percentage is lower than 1%
continue; continue;
} }
mHighUsageAppList.add(new AppInfo.Builder() mHighUsageAppList.add(
.setUid(consumer.getUid()) new AppInfo.Builder()
.setPackageName( .setUid(consumer.getUid())
mBatteryUtils.getPackageName(consumer.getUid())) .setPackageName(mBatteryUtils.getPackageName(consumer.getUid()))
.build()); .build());
if (mHighUsageAppList.size() >= mPolicy.highUsageAppCount) { if (mHighUsageAppList.size() >= mPolicy.highUsageAppCount) {
break; break;
} }
@@ -103,10 +108,11 @@ public class HighUsageDetector implements BatteryTipDetector {
// When in test mode, add an app if necessary // When in test mode, add an app if necessary
if (mPolicy.testHighUsageTip && mHighUsageAppList.isEmpty()) { if (mPolicy.testHighUsageTip && mHighUsageAppList.isEmpty()) {
mHighUsageAppList.add(new AppInfo.Builder() mHighUsageAppList.add(
.setPackageName(SETTINGS_PACKAGE_NAME) new AppInfo.Builder()
.setScreenOnTimeMs(TimeUnit.HOURS.toMillis(3)) .setPackageName(SETTINGS_PACKAGE_NAME)
.build()); .setScreenOnTimeMs(TimeUnit.HOURS.toMillis(3))
.build());
} }
} }
} }

View File

@@ -34,12 +34,12 @@ public final class IncompatibleChargerDetector implements BatteryTipDetector {
@Override @Override
public BatteryTip detect() { public BatteryTip detect() {
final boolean isIncompatibleCharging = final boolean isIncompatibleCharging = Utils.containsIncompatibleChargers(mContext, TAG);
Utils.containsIncompatibleChargers(mContext, TAG); final int state =
final int state = isIncompatibleCharging isIncompatibleCharging ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE;
? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE; Log.d(
Log.d(TAG, "detect() state= " + state + " isIncompatibleCharging: " TAG,
+ isIncompatibleCharging); "detect() state= " + state + " isIncompatibleCharging: " + isIncompatibleCharging);
return new IncompatibleChargerTip(state); return new IncompatibleChargerTip(state);
} }
} }

View File

@@ -23,22 +23,23 @@ import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip; import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
/** /** Detect whether the battery is too low */
* Detect whether the battery is too low
*/
public class LowBatteryDetector implements BatteryTipDetector { public class LowBatteryDetector implements BatteryTipDetector {
private final BatteryInfo mBatteryInfo; private final BatteryInfo mBatteryInfo;
private final BatteryTipPolicy mPolicy; private final BatteryTipPolicy mPolicy;
private final boolean mIsPowerSaveMode; private final boolean mIsPowerSaveMode;
private final int mWarningLevel; private final int mWarningLevel;
public LowBatteryDetector(
public LowBatteryDetector(Context context, BatteryTipPolicy policy, BatteryInfo batteryInfo, Context context,
BatteryTipPolicy policy,
BatteryInfo batteryInfo,
boolean isPowerSaveMode) { boolean isPowerSaveMode) {
mPolicy = policy; mPolicy = policy;
mBatteryInfo = batteryInfo; mBatteryInfo = batteryInfo;
mWarningLevel = context.getResources().getInteger( mWarningLevel =
com.android.internal.R.integer.config_lowBatteryWarningLevel); context.getResources()
.getInteger(com.android.internal.R.integer.config_lowBatteryWarningLevel);
mIsPowerSaveMode = isPowerSaveMode; mIsPowerSaveMode = isPowerSaveMode;
} }

View File

@@ -25,9 +25,7 @@ import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SmartBatteryTip; import com.android.settings.fuelgauge.batterytip.tips.SmartBatteryTip;
/** /** Detect whether to show smart battery tip. */
* Detect whether to show smart battery tip.
*/
public class SmartBatteryDetector implements BatteryTipDetector { public class SmartBatteryDetector implements BatteryTipDetector {
private static final int EXPECTED_BATTERY_LEVEL = 30; private static final int EXPECTED_BATTERY_LEVEL = 30;
@@ -36,8 +34,12 @@ public class SmartBatteryDetector implements BatteryTipDetector {
private final ContentResolver mContentResolver; private final ContentResolver mContentResolver;
private final boolean mIsPowerSaveMode; private final boolean mIsPowerSaveMode;
public SmartBatteryDetector(Context context, BatteryTipPolicy policy, BatteryInfo batteryInfo, public SmartBatteryDetector(
ContentResolver contentResolver, boolean isPowerSaveMode) { Context context,
BatteryTipPolicy policy,
BatteryInfo batteryInfo,
ContentResolver contentResolver,
boolean isPowerSaveMode) {
mPolicy = policy; mPolicy = policy;
mBatteryInfo = batteryInfo; mBatteryInfo = batteryInfo;
mContentResolver = contentResolver; mContentResolver = contentResolver;
@@ -46,14 +48,18 @@ public class SmartBatteryDetector implements BatteryTipDetector {
@Override @Override
public BatteryTip detect() { public BatteryTip detect() {
final boolean smartBatteryOff = Settings.Global.getInt(mContentResolver, final boolean smartBatteryOff =
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 0; Settings.Global.getInt(
mContentResolver,
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
1)
== 0;
final boolean isUnderExpectedBatteryLevel = final boolean isUnderExpectedBatteryLevel =
mBatteryInfo.batteryLevel <= EXPECTED_BATTERY_LEVEL; mBatteryInfo.batteryLevel <= EXPECTED_BATTERY_LEVEL;
// Show it if in test or smart battery is off. // Show it if in test or smart battery is off.
final boolean enableSmartBatteryTip = final boolean enableSmartBatteryTip =
smartBatteryOff && !mIsPowerSaveMode && isUnderExpectedBatteryLevel smartBatteryOff && !mIsPowerSaveMode && isUnderExpectedBatteryLevel
|| mPolicy.testSmartBatteryTip; || mPolicy.testSmartBatteryTip;
final int state = final int state =
enableSmartBatteryTip ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE; enableSmartBatteryTip ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE;
return new SmartBatteryTip(state); return new SmartBatteryTip(state);

View File

@@ -23,9 +23,7 @@ import com.android.settings.fuelgauge.batterytip.AppInfo;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /** {@link Predicate} for {@link AppInfo} to check whether it has label */
* {@link Predicate} for {@link AppInfo} to check whether it has label
*/
public class AppLabelPredicate implements Predicate<AppInfo> { public class AppLabelPredicate implements Predicate<AppInfo> {
private static AppLabelPredicate sInstance; private static AppLabelPredicate sInstance;

View File

@@ -23,9 +23,7 @@ import com.android.settings.fuelgauge.batterytip.AppInfo;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /** {@link Predicate} for {@link AppInfo} to check whether it is restricted. */
* {@link Predicate} for {@link AppInfo} to check whether it is restricted.
*/
public class AppRestrictionPredicate implements Predicate<AppInfo> { public class AppRestrictionPredicate implements Predicate<AppInfo> {
private static AppRestrictionPredicate sInstance; private static AppRestrictionPredicate sInstance;
@@ -46,7 +44,8 @@ public class AppRestrictionPredicate implements Predicate<AppInfo> {
@Override @Override
public boolean test(AppInfo appInfo) { public boolean test(AppInfo appInfo) {
// Return true if app already been restricted // Return true if app already been restricted
return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, return mAppOpsManager.checkOpNoThrow(
appInfo.uid, appInfo.packageName) == AppOpsManager.MODE_IGNORED; AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, appInfo.uid, appInfo.packageName)
== AppOpsManager.MODE_IGNORED;
} }
} }

View File

@@ -30,9 +30,7 @@ import com.android.settings.widget.CardPreference;
import com.android.settingslib.HelpUtils; import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/** /** Tip to show current battery is overheated */
* Tip to show current battery is overheated
*/
public class BatteryDefenderTip extends BatteryTip { public class BatteryDefenderTip extends BatteryTip {
private static final String TAG = "BatteryDefenderTip"; private static final String TAG = "BatteryDefenderTip";
@@ -70,8 +68,7 @@ public class BatteryDefenderTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_BATTERY_DEFENDER_TIP, metricsFeatureProvider.action(context, SettingsEnums.ACTION_BATTERY_DEFENDER_TIP, mState);
mState);
} }
@Override @Override
@@ -97,14 +94,17 @@ public class BatteryDefenderTip extends BatteryTip {
cardPreference.setSecondaryButtonText(context.getString(R.string.learn_more)); cardPreference.setSecondaryButtonText(context.getString(R.string.learn_more));
cardPreference.setSecondaryButtonClickListener( cardPreference.setSecondaryButtonClickListener(
button -> button.startActivityForResult( button ->
HelpUtils.getHelpIntent( button.startActivityForResult(
context, HelpUtils.getHelpIntent(
context.getString(R.string.help_url_battery_defender), context,
/* backupContext */ ""), /* requestCode */ 0)); context.getString(R.string.help_url_battery_defender),
/* backupContext */ ""), /* requestCode */
0));
cardPreference.setSecondaryButtonVisible(true); cardPreference.setSecondaryButtonVisible(true);
cardPreference.setSecondaryButtonContentDescription(context.getString( cardPreference.setSecondaryButtonContentDescription(
R.string.battery_tip_limited_temporarily_sec_button_content_description)); context.getString(
R.string.battery_tip_limited_temporarily_sec_button_content_description));
} }
private void resumeCharging(Context context) { private void resumeCharging(Context context) {
@@ -119,13 +119,14 @@ public class BatteryDefenderTip extends BatteryTip {
Log.i(TAG, "send resume charging broadcast intent=" + intent); Log.i(TAG, "send resume charging broadcast intent=" + intent);
} }
public static final Creator CREATOR = new Creator() { public static final Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Creator() {
return new BatteryDefenderTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new BatteryDefenderTip(in);
}
public BatteryTip[] newArray(int size) { public BatteryTip[] newArray(int size) {
return new BatteryDefenderTip[size]; return new BatteryDefenderTip[size];
} }
}; };
} }

View File

@@ -35,14 +35,12 @@ import java.lang.annotation.RetentionPolicy;
/** /**
* Base model for a battery tip(e.g. suggest user to turn on battery saver) * Base model for a battery tip(e.g. suggest user to turn on battery saver)
* *
* Each {@link BatteryTip} contains basic data(e.g. title, summary, icon) as well as the * <p>Each {@link BatteryTip} contains basic data(e.g. title, summary, icon) as well as the
* pre-defined action(e.g. turn on battery saver) * pre-defined action(e.g. turn on battery saver)
*/ */
public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable { public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({StateType.NEW, @IntDef({StateType.NEW, StateType.HANDLED, StateType.INVISIBLE})
StateType.HANDLED,
StateType.INVISIBLE})
public @interface StateType { public @interface StateType {
int NEW = 0; int NEW = 0;
int HANDLED = 1; int HANDLED = 1;
@@ -50,17 +48,19 @@ public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
} }
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({TipType.SUMMARY, @IntDef({
TipType.BATTERY_SAVER, TipType.SUMMARY,
TipType.HIGH_DEVICE_USAGE, TipType.BATTERY_SAVER,
TipType.SMART_BATTERY_MANAGER, TipType.HIGH_DEVICE_USAGE,
TipType.APP_RESTRICTION, TipType.SMART_BATTERY_MANAGER,
TipType.REDUCED_BATTERY, TipType.APP_RESTRICTION,
TipType.LOW_BATTERY, TipType.REDUCED_BATTERY,
TipType.REMOVE_APP_RESTRICTION, TipType.LOW_BATTERY,
TipType.BATTERY_DEFENDER, TipType.REMOVE_APP_RESTRICTION,
TipType.DOCK_DEFENDER, TipType.BATTERY_DEFENDER,
TipType.INCOMPATIBLE_CHARGER}) TipType.DOCK_DEFENDER,
TipType.INCOMPATIBLE_CHARGER
})
public @interface TipType { public @interface TipType {
int SMART_BATTERY_MANAGER = 0; int SMART_BATTERY_MANAGER = 0;
int APP_RESTRICTION = 1; int APP_RESTRICTION = 1;
@@ -75,8 +75,8 @@ public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
int INCOMPATIBLE_CHARGER = 10; int INCOMPATIBLE_CHARGER = 10;
} }
@VisibleForTesting @VisibleForTesting static final SparseIntArray TIP_ORDER;
static final SparseIntArray TIP_ORDER;
static { static {
TIP_ORDER = new SparseIntArray(); TIP_ORDER = new SparseIntArray();
TIP_ORDER.append(TipType.BATTERY_SAVER, 0); TIP_ORDER.append(TipType.BATTERY_SAVER, 0);
@@ -97,9 +97,8 @@ public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
protected int mType; protected int mType;
protected int mState; protected int mState;
protected boolean mShowDialog; protected boolean mShowDialog;
/**
* Whether we need to update battery tip when configuration change /** Whether we need to update battery tip when configuration change */
*/
protected boolean mNeedUpdate; protected boolean mNeedUpdate;
BatteryTip(Parcel in) { BatteryTip(Parcel in) {
@@ -146,15 +145,14 @@ public abstract class BatteryTip implements Comparable<BatteryTip>, Parcelable {
/** /**
* Check whether data is still make sense. If not, try recover. * Check whether data is still make sense. If not, try recover.
*
* @param context used to do validate check * @param context used to do validate check
*/ */
public void validateCheck(Context context) { public void validateCheck(Context context) {
// do nothing // do nothing
} }
/** /** Log the battery tip */
* Log the battery tip
*/
public abstract void log(Context context, MetricsFeatureProvider metricsFeatureProvider); public abstract void log(Context context, MetricsFeatureProvider metricsFeatureProvider);
public void updatePreference(Preference preference) { public void updatePreference(Preference preference) {

View File

@@ -32,9 +32,7 @@ import com.android.settings.widget.CardPreference;
import com.android.settingslib.HelpUtils; import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/** /** Tip to show dock defender status */
* Tip to show dock defender status
*/
public class DockDefenderTip extends BatteryTip { public class DockDefenderTip extends BatteryTip {
private static final String TAG = "DockDefenderTip"; private static final String TAG = "DockDefenderTip";
private int mMode; private int mMode;
@@ -84,8 +82,9 @@ public class DockDefenderTip extends BatteryTip {
@Override @Override
public int getIconId() { public int getIconId() {
return mMode == DockDefenderMode.ACTIVE ? R.drawable.ic_battery_status_protected_24dp : return mMode == DockDefenderMode.ACTIVE
R.drawable.ic_battery_dock_defender_untriggered_24dp; ? R.drawable.ic_battery_status_protected_24dp
: R.drawable.ic_battery_dock_defender_untriggered_24dp;
} }
@Override @Override
@@ -98,8 +97,7 @@ public class DockDefenderTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_DOCK_DEFENDER_TIP, metricsFeatureProvider.action(context, SettingsEnums.ACTION_DOCK_DEFENDER_TIP, mState);
mState);
} }
@Override @Override
@@ -119,15 +117,19 @@ public class DockDefenderTip extends BatteryTip {
case DockDefenderMode.ACTIVE: case DockDefenderMode.ACTIVE:
cardPreference.setPrimaryButtonText( cardPreference.setPrimaryButtonText(
context.getString(R.string.battery_tip_charge_to_full_button)); context.getString(R.string.battery_tip_charge_to_full_button));
cardPreference.setPrimaryButtonClickListener(unused -> { cardPreference.setPrimaryButtonClickListener(
resumeCharging(context); unused -> {
mMode = DockDefenderMode.TEMPORARILY_BYPASSED; resumeCharging(context);
context.sendBroadcast(new Intent().setAction( mMode = DockDefenderMode.TEMPORARILY_BYPASSED;
BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION).setPackage( context.sendBroadcast(
context.getPackageName()).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY new Intent()
| Intent.FLAG_RECEIVER_FOREGROUND)); .setAction(BatteryUtils.BYPASS_DOCK_DEFENDER_ACTION)
updatePreference(preference); .setPackage(context.getPackageName())
}); .addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND));
updatePreference(preference);
});
cardPreference.setPrimaryButtonVisible(true); cardPreference.setPrimaryButtonVisible(true);
break; break;
case DockDefenderMode.TEMPORARILY_BYPASSED: case DockDefenderMode.TEMPORARILY_BYPASSED:
@@ -140,15 +142,17 @@ public class DockDefenderTip extends BatteryTip {
cardPreference.setSecondaryButtonText(context.getString(R.string.learn_more)); cardPreference.setSecondaryButtonText(context.getString(R.string.learn_more));
cardPreference.setSecondaryButtonClickListener( cardPreference.setSecondaryButtonClickListener(
button -> button.startActivityForResult( button ->
HelpUtils.getHelpIntent( button.startActivityForResult(
context, HelpUtils.getHelpIntent(
context.getString(R.string.help_url_dock_defender), context,
/* backupContext */ ""), /* requestCode */ 0)); context.getString(R.string.help_url_dock_defender),
/* backupContext */ ""), /* requestCode */
0));
cardPreference.setSecondaryButtonVisible(true); cardPreference.setSecondaryButtonVisible(true);
cardPreference.setSecondaryButtonContentDescription(context.getString( cardPreference.setSecondaryButtonContentDescription(
R.string.battery_tip_limited_temporarily_sec_button_content_description)); context.getString(
R.string.battery_tip_limited_temporarily_sec_button_content_description));
} }
private void resumeCharging(Context context) { private void resumeCharging(Context context) {
@@ -163,13 +167,14 @@ public class DockDefenderTip extends BatteryTip {
Log.i(TAG, "send resume charging broadcast intent=" + intent); Log.i(TAG, "send resume charging broadcast intent=" + intent);
} }
public static final Creator CREATOR = new Creator() { public static final Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Creator() {
return new DockDefenderTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new DockDefenderTip(in);
}
public BatteryTip[] newArray(int size) { public BatteryTip[] newArray(int size) {
return new DockDefenderTip[size]; return new DockDefenderTip[size];
} }
}; };
} }

View File

@@ -29,17 +29,16 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.List; import java.util.List;
/** /** Tip to show general summary about battery life */
* Tip to show general summary about battery life
*/
public class HighUsageTip extends BatteryTip { public class HighUsageTip extends BatteryTip {
private final long mLastFullChargeTimeMs; private final long mLastFullChargeTimeMs;
@VisibleForTesting @VisibleForTesting final List<AppInfo> mHighUsageAppList;
final List<AppInfo> mHighUsageAppList;
public HighUsageTip(long lastFullChargeTimeMs, List<AppInfo> appList) { public HighUsageTip(long lastFullChargeTimeMs, List<AppInfo> appList) {
super(TipType.HIGH_DEVICE_USAGE, appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW, super(
TipType.HIGH_DEVICE_USAGE,
appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW,
true /* showDialog */); true /* showDialog */);
mLastFullChargeTimeMs = lastFullChargeTimeMs; mLastFullChargeTimeMs = lastFullChargeTimeMs;
mHighUsageAppList = appList; mHighUsageAppList = appList;
@@ -81,13 +80,11 @@ public class HighUsageTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_HIGH_USAGE_TIP, metricsFeatureProvider.action(context, SettingsEnums.ACTION_HIGH_USAGE_TIP, mState);
mState);
for (int i = 0, size = mHighUsageAppList.size(); i < size; i++) { for (int i = 0, size = mHighUsageAppList.size(); i < size; i++) {
final AppInfo appInfo = mHighUsageAppList.get(i); final AppInfo appInfo = mHighUsageAppList.get(i);
metricsFeatureProvider.action(context, metricsFeatureProvider.action(
SettingsEnums.ACTION_HIGH_USAGE_TIP_LIST, context, SettingsEnums.ACTION_HIGH_USAGE_TIP_LIST, appInfo.packageName);
appInfo.packageName);
} }
} }
@@ -112,14 +109,14 @@ public class HighUsageTip extends BatteryTip {
return stringBuilder.toString(); return stringBuilder.toString();
} }
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public static final Parcelable.Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Parcelable.Creator() {
return new HighUsageTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new HighUsageTip(in);
}
public BatteryTip[] newArray(int size) {
return new HighUsageTip[size];
}
};
public BatteryTip[] newArray(int size) {
return new HighUsageTip[size];
}
};
} }

View File

@@ -62,8 +62,8 @@ public final class IncompatibleChargerTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_INCOMPATIBLE_CHARGING_TIP, metricsFeatureProvider.action(
mState); context, SettingsEnums.ACTION_INCOMPATIBLE_CHARGING_TIP, mState);
} }
@Override @Override
@@ -79,23 +79,26 @@ public final class IncompatibleChargerTip extends BatteryTip {
cardPreference.setSelectable(false); cardPreference.setSelectable(false);
cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more)); cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more));
cardPreference.setPrimaryButtonClickListener( cardPreference.setPrimaryButtonClickListener(
button -> button.startActivityForResult( button ->
HelpUtils.getHelpIntent( button.startActivityForResult(
context, HelpUtils.getHelpIntent(
context.getString(R.string.help_url_incompatible_charging), context,
/* backupContext */ ""), /* requestCode */ 0)); context.getString(R.string.help_url_incompatible_charging),
/* backupContext */ ""), /* requestCode */
0));
cardPreference.setPrimaryButtonVisible(true); cardPreference.setPrimaryButtonVisible(true);
cardPreference.setPrimaryButtonContentDescription(context.getString( cardPreference.setPrimaryButtonContentDescription(
R.string.battery_tip_incompatible_charging_content_description)); context.getString(R.string.battery_tip_incompatible_charging_content_description));
} }
public static final Creator CREATOR = new Creator() { public static final Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Creator() {
return new IncompatibleChargerTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new IncompatibleChargerTip(in);
}
public BatteryTip[] newArray(int size) { public BatteryTip[] newArray(int size) {
return new IncompatibleChargerTip[size]; return new IncompatibleChargerTip[size];
} }
}; };
} }

View File

@@ -62,28 +62,27 @@ public class LowBatteryTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_LOW_BATTERY_TIP, metricsFeatureProvider.action(context, SettingsEnums.ACTION_LOW_BATTERY_TIP, mState);
mState);
} }
@Override @Override
public void updateState(BatteryTip tip) { public void updateState(BatteryTip tip) {
final LowBatteryTip lowBatteryTip = (LowBatteryTip) tip; final LowBatteryTip lowBatteryTip = (LowBatteryTip) tip;
mState = lowBatteryTip.mPowerSaveModeOn mState = lowBatteryTip.mPowerSaveModeOn ? StateType.INVISIBLE : lowBatteryTip.getState();
? StateType.INVISIBLE : lowBatteryTip.getState();
} }
boolean isPowerSaveModeOn() { boolean isPowerSaveModeOn() {
return mPowerSaveModeOn; return mPowerSaveModeOn;
} }
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public static final Parcelable.Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Parcelable.Creator() {
return new LowBatteryTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new LowBatteryTip(in);
}
public BatteryTip[] newArray(int size) { public BatteryTip[] newArray(int size) {
return new LowBatteryTip[size]; return new LowBatteryTip[size];
} }
}; };
} }

View File

@@ -34,9 +34,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /** Tip to suggest user to restrict some bad apps */
* Tip to suggest user to restrict some bad apps
*/
public class RestrictAppTip extends BatteryTip { public class RestrictAppTip extends BatteryTip {
private List<AppInfo> mRestrictAppList; private List<AppInfo> mRestrictAppList;
@@ -62,27 +60,32 @@ public class RestrictAppTip extends BatteryTip {
@Override @Override
public CharSequence getTitle(Context context) { public CharSequence getTitle(Context context) {
final int num = mRestrictAppList.size(); final int num = mRestrictAppList.size();
final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context, final CharSequence appLabel =
mRestrictAppList.get(0).packageName) : ""; num > 0
? Utils.getApplicationLabel(context, mRestrictAppList.get(0).packageName)
: "";
Map<String, Object> arguments = new ArrayMap<>(); Map<String, Object> arguments = new ArrayMap<>();
arguments.put("count", num); arguments.put("count", num);
arguments.put("label", appLabel); arguments.put("label", appLabel);
return mState == StateType.HANDLED return mState == StateType.HANDLED
? StringUtil.getIcuPluralsString(context, arguments, ? StringUtil.getIcuPluralsString(
R.string.battery_tip_restrict_handled_title) context, arguments, R.string.battery_tip_restrict_handled_title)
: StringUtil.getIcuPluralsString(context, arguments, : StringUtil.getIcuPluralsString(
R.string.battery_tip_restrict_title); context, arguments, R.string.battery_tip_restrict_title);
} }
@Override @Override
public CharSequence getSummary(Context context) { public CharSequence getSummary(Context context) {
final int num = mRestrictAppList.size(); final int num = mRestrictAppList.size();
final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context, final CharSequence appLabel =
mRestrictAppList.get(0).packageName) : ""; num > 0
final int resId = mState == StateType.HANDLED ? Utils.getApplicationLabel(context, mRestrictAppList.get(0).packageName)
? R.string.battery_tip_restrict_handled_summary : "";
: R.string.battery_tip_restrict_summary; final int resId =
mState == StateType.HANDLED
? R.string.battery_tip_restrict_handled_summary
: R.string.battery_tip_restrict_summary;
Map<String, Object> arguments = new ArrayMap<>(); Map<String, Object> arguments = new ArrayMap<>();
arguments.put("count", num); arguments.put("count", num);
arguments.put("label", appLabel); arguments.put("label", appLabel);
@@ -127,13 +130,13 @@ public class RestrictAppTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_APP_RESTRICTION_TIP, metricsFeatureProvider.action(context, SettingsEnums.ACTION_APP_RESTRICTION_TIP, mState);
mState);
if (mState == StateType.NEW) { if (mState == StateType.NEW) {
for (int i = 0, size = mRestrictAppList.size(); i < size; i++) { for (int i = 0, size = mRestrictAppList.size(); i < size; i++) {
final AppInfo appInfo = mRestrictAppList.get(i); final AppInfo appInfo = mRestrictAppList.get(i);
for (Integer anomalyType : appInfo.anomalyTypes) { for (Integer anomalyType : appInfo.anomalyTypes) {
metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN, metricsFeatureProvider.action(
SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_APP_RESTRICTION_TIP_LIST, SettingsEnums.ACTION_APP_RESTRICTION_TIP_LIST,
SettingsEnums.PAGE_UNKNOWN, SettingsEnums.PAGE_UNKNOWN,
appInfo.packageName, appInfo.packageName,
@@ -147,14 +150,11 @@ public class RestrictAppTip extends BatteryTip {
return mRestrictAppList; return mRestrictAppList;
} }
/** /** Construct the app list string(e.g. app1, app2, and app3) */
* Construct the app list string(e.g. app1, app2, and app3)
*/
public CharSequence getRestrictAppsString(Context context) { public CharSequence getRestrictAppsString(Context context) {
final List<CharSequence> appLabels = new ArrayList<>(); final List<CharSequence> appLabels = new ArrayList<>();
for (int i = 0, size = mRestrictAppList.size(); i < size; i++) { for (int i = 0, size = mRestrictAppList.size(); i < size; i++) {
appLabels.add(Utils.getApplicationLabel(context, appLabels.add(Utils.getApplicationLabel(context, mRestrictAppList.get(i).packageName));
mRestrictAppList.get(i).packageName));
} }
return ListFormatter.getInstance().format(appLabels); return ListFormatter.getInstance().format(appLabels);
@@ -179,13 +179,14 @@ public class RestrictAppTip extends BatteryTip {
dest.writeTypedList(mRestrictAppList); dest.writeTypedList(mRestrictAppList);
} }
public static final Creator CREATOR = new Creator() { public static final Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Creator() {
return new RestrictAppTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new RestrictAppTip(in);
}
public BatteryTip[] newArray(int size) { public BatteryTip[] newArray(int size) {
return new RestrictAppTip[size]; return new RestrictAppTip[size];
} }
}; };
} }

View File

@@ -23,9 +23,7 @@ import android.os.Parcel;
import com.android.settings.R; import com.android.settings.R;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/** /** Tip to suggest turn on smart battery if it is not on */
* Tip to suggest turn on smart battery if it is not on
*/
public class SmartBatteryTip extends BatteryTip { public class SmartBatteryTip extends BatteryTip {
public SmartBatteryTip(@StateType int state) { public SmartBatteryTip(@StateType int state) {
@@ -58,18 +56,17 @@ public class SmartBatteryTip extends BatteryTip {
@Override @Override
public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
metricsFeatureProvider.action(context, SettingsEnums.ACTION_SMART_BATTERY_TIP, metricsFeatureProvider.action(context, SettingsEnums.ACTION_SMART_BATTERY_TIP, mState);
mState);
} }
public static final Creator CREATOR = new Creator() { public static final Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Creator() {
return new SmartBatteryTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new SmartBatteryTip(in);
}
public BatteryTip[] newArray(int size) {
return new SmartBatteryTip[size];
}
};
public BatteryTip[] newArray(int size) {
return new SmartBatteryTip[size];
}
};
} }

View File

@@ -84,13 +84,14 @@ public class UnrestrictAppTip extends BatteryTip {
dest.writeParcelable(mAppInfo, flags); dest.writeParcelable(mAppInfo, flags);
} }
public static final Creator CREATOR = new Creator() { public static final Creator CREATOR =
public BatteryTip createFromParcel(Parcel in) { new Creator() {
return new UnrestrictAppTip(in); public BatteryTip createFromParcel(Parcel in) {
} return new UnrestrictAppTip(in);
}
public BatteryTip[] newArray(int size) { public BatteryTip[] newArray(int size) {
return new UnrestrictAppTip[size]; return new UnrestrictAppTip[size];
} }
}; };
} }

View File

@@ -48,7 +48,8 @@ final class AnomalyEventWrapper {
mResourceIndex = mPowerAnomalyEvent.getKey().getNumber(); mResourceIndex = mPowerAnomalyEvent.getKey().getNumber();
} }
private <T> T getInfo(Function<WarningBannerInfo, T> warningBannerInfoSupplier, private <T> T getInfo(
Function<WarningBannerInfo, T> warningBannerInfoSupplier,
Function<WarningItemInfo, T> warningItemInfoSupplier) { Function<WarningItemInfo, T> warningItemInfoSupplier) {
if (warningBannerInfoSupplier != null && mPowerAnomalyEvent.hasWarningBannerInfo()) { if (warningBannerInfoSupplier != null && mPowerAnomalyEvent.hasWarningBannerInfo()) {
return warningBannerInfoSupplier.apply(mPowerAnomalyEvent.getWarningBannerInfo()); return warningBannerInfoSupplier.apply(mPowerAnomalyEvent.getWarningBannerInfo());
@@ -60,15 +61,19 @@ final class AnomalyEventWrapper {
private int getResourceId(int resourceId, int resourceIndex, String defType) { private int getResourceId(int resourceId, int resourceIndex, String defType) {
final String key = getStringFromArrayResource(resourceId, resourceIndex); final String key = getStringFromArrayResource(resourceId, resourceIndex);
return TextUtils.isEmpty(key) ? 0 return TextUtils.isEmpty(key)
? 0
: mContext.getResources().getIdentifier(key, defType, mContext.getPackageName()); : mContext.getResources().getIdentifier(key, defType, mContext.getPackageName());
} }
private String getString(Function<WarningBannerInfo, String> warningBannerInfoSupplier, private String getString(
Function<WarningBannerInfo, String> warningBannerInfoSupplier,
Function<WarningItemInfo, String> warningItemInfoSupplier, Function<WarningItemInfo, String> warningItemInfoSupplier,
int resourceId, int resourceIndex) { int resourceId,
int resourceIndex) {
final String string = getInfo(warningBannerInfoSupplier, warningItemInfoSupplier); final String string = getInfo(warningBannerInfoSupplier, warningItemInfoSupplier);
return (!TextUtils.isEmpty(string) || resourceId <= 0) ? string return (!TextUtils.isEmpty(string) || resourceId <= 0)
? string
: getStringFromArrayResource(resourceId, resourceIndex); : getStringFromArrayResource(resourceId, resourceIndex);
} }
@@ -78,7 +83,8 @@ final class AnomalyEventWrapper {
} }
final String[] stringArray = mContext.getResources().getStringArray(resourceId); final String[] stringArray = mContext.getResources().getStringArray(resourceId);
return (resourceIndex >= 0 && resourceIndex < stringArray.length) return (resourceIndex >= 0 && resourceIndex < stringArray.length)
? stringArray[resourceIndex] : null; ? stringArray[resourceIndex]
: null;
} }
void setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry) { void setRelatedBatteryDiffEntry(BatteryDiffEntry batteryDiffEntry) {
@@ -98,13 +104,13 @@ final class AnomalyEventWrapper {
} }
String getTitleString() { String getTitleString() {
final String titleStringFromProto = getInfo(WarningBannerInfo::getTitleString, final String titleStringFromProto =
WarningItemInfo::getTitleString); getInfo(WarningBannerInfo::getTitleString, WarningItemInfo::getTitleString);
if (!TextUtils.isEmpty(titleStringFromProto)) { if (!TextUtils.isEmpty(titleStringFromProto)) {
return titleStringFromProto; return titleStringFromProto;
} }
final int titleFormatResId = getResourceId(R.array.power_anomaly_title_ids, final int titleFormatResId =
mResourceIndex, "string"); getResourceId(R.array.power_anomaly_title_ids, mResourceIndex, "string");
if (mPowerAnomalyEvent.hasWarningBannerInfo()) { if (mPowerAnomalyEvent.hasWarningBannerInfo()) {
return mContext.getString(titleFormatResId); return mContext.getString(titleFormatResId);
} else if (mPowerAnomalyEvent.hasWarningItemInfo() && mRelatedBatteryDiffEntry != null) { } else if (mPowerAnomalyEvent.hasWarningItemInfo() && mRelatedBatteryDiffEntry != null) {
@@ -115,20 +121,24 @@ final class AnomalyEventWrapper {
} }
String getMainBtnString() { String getMainBtnString() {
return getString(WarningBannerInfo::getMainButtonString, return getString(
WarningBannerInfo::getMainButtonString,
WarningItemInfo::getMainButtonString, WarningItemInfo::getMainButtonString,
R.array.power_anomaly_main_btn_strings, mResourceIndex); R.array.power_anomaly_main_btn_strings,
mResourceIndex);
} }
String getDismissBtnString() { String getDismissBtnString() {
return getString(WarningBannerInfo::getCancelButtonString, return getString(
WarningBannerInfo::getCancelButtonString,
WarningItemInfo::getCancelButtonString, WarningItemInfo::getCancelButtonString,
R.array.power_anomaly_dismiss_btn_strings, mResourceIndex); R.array.power_anomaly_dismiss_btn_strings,
mResourceIndex);
} }
String getAnomalyHintString() { String getAnomalyHintString() {
final String anomalyHintStringFromProto = getInfo(null, final String anomalyHintStringFromProto =
WarningItemInfo::getWarningInfoString); getInfo(null, WarningItemInfo::getWarningInfoString);
return TextUtils.isEmpty(anomalyHintStringFromProto) return TextUtils.isEmpty(anomalyHintStringFromProto)
? getStringFromArrayResource(R.array.power_anomaly_hint_messages, mResourceIndex) ? getStringFromArrayResource(R.array.power_anomaly_hint_messages, mResourceIndex)
: anomalyHintStringFromProto; : anomalyHintStringFromProto;
@@ -148,8 +158,9 @@ final class AnomalyEventWrapper {
String getAnomalyEntryKey() { String getAnomalyEntryKey() {
return mPowerAnomalyEvent.hasWarningItemInfo() return mPowerAnomalyEvent.hasWarningItemInfo()
&& mPowerAnomalyEvent.getWarningItemInfo().hasItemKey() && mPowerAnomalyEvent.getWarningItemInfo().hasItemKey()
? mPowerAnomalyEvent.getWarningItemInfo().getItemKey() : null; ? mPowerAnomalyEvent.getWarningItemInfo().getItemKey()
: null;
} }
boolean hasSubSettingLauncher() { boolean hasSubSettingLauncher() {
@@ -163,23 +174,24 @@ final class AnomalyEventWrapper {
if (mSubSettingLauncher != null) { if (mSubSettingLauncher != null) {
return mSubSettingLauncher; return mSubSettingLauncher;
} }
final String destinationClassName = getInfo( final String destinationClassName =
WarningBannerInfo::getMainButtonDestination, null); getInfo(WarningBannerInfo::getMainButtonDestination, null);
if (!TextUtils.isEmpty(destinationClassName)) { if (!TextUtils.isEmpty(destinationClassName)) {
final Integer sourceMetricsCategory = getInfo( final Integer sourceMetricsCategory =
WarningBannerInfo::getMainButtonSourceMetricsCategory, null); getInfo(WarningBannerInfo::getMainButtonSourceMetricsCategory, null);
final String preferenceHighlightKey = getInfo( final String preferenceHighlightKey =
WarningBannerInfo::getMainButtonSourceHighlightKey, null); getInfo(WarningBannerInfo::getMainButtonSourceHighlightKey, null);
Bundle arguments = Bundle.EMPTY; Bundle arguments = Bundle.EMPTY;
if (!TextUtils.isEmpty(preferenceHighlightKey)) { if (!TextUtils.isEmpty(preferenceHighlightKey)) {
arguments = new Bundle(1); arguments = new Bundle(1);
arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, arguments.putString(
preferenceHighlightKey); SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, preferenceHighlightKey);
} }
mSubSettingLauncher = new SubSettingLauncher(mContext) mSubSettingLauncher =
.setDestination(destinationClassName) new SubSettingLauncher(mContext)
.setSourceMetricsCategory(sourceMetricsCategory) .setDestination(destinationClassName)
.setArguments(arguments); .setSourceMetricsCategory(sourceMetricsCategory)
.setArguments(arguments);
} }
return mSubSettingLauncher; return mSubSettingLauncher;
} }
@@ -199,13 +211,13 @@ final class AnomalyEventWrapper {
return null; return null;
} }
final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo(); final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo();
final Long startTimestamp = warningItemInfo.hasStartTimestamp() final Long startTimestamp =
? warningItemInfo.getStartTimestamp() : null; warningItemInfo.hasStartTimestamp() ? warningItemInfo.getStartTimestamp() : null;
final Long endTimestamp = warningItemInfo.hasEndTimestamp() final Long endTimestamp =
? warningItemInfo.getEndTimestamp() : null; warningItemInfo.hasEndTimestamp() ? warningItemInfo.getEndTimestamp() : null;
if (startTimestamp != null && endTimestamp != null) { if (startTimestamp != null && endTimestamp != null) {
mHighlightSlotPair = batteryLevelData mHighlightSlotPair =
.getIndexByTimestamps(startTimestamp, endTimestamp); batteryLevelData.getIndexByTimestamps(startTimestamp, endTimestamp);
if (mHighlightSlotPair.first == BatteryChartViewModel.SELECTED_INDEX_INVALID if (mHighlightSlotPair.first == BatteryChartViewModel.SELECTED_INDEX_INVALID
|| mHighlightSlotPair.second == BatteryChartViewModel.SELECTED_INDEX_INVALID) { || mHighlightSlotPair.second == BatteryChartViewModel.SELECTED_INDEX_INVALID) {
// Drop invalid mHighlightSlotPair index // Drop invalid mHighlightSlotPair index

View File

@@ -55,8 +55,12 @@ import java.util.Map;
/** Controls the update for chart graph and the list items. */ /** Controls the update for chart graph and the list items. */
public class BatteryChartPreferenceController extends AbstractPreferenceController public class BatteryChartPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy, implements PreferenceControllerMixin,
OnSaveInstanceState, OnResume { LifecycleObserver,
OnCreate,
OnDestroy,
OnSaveInstanceState,
OnResume {
private static final String TAG = "BatteryChartPreferenceController"; private static final String TAG = "BatteryChartPreferenceController";
private static final String PREFERENCE_KEY = "battery_chart"; private static final String PREFERENCE_KEY = "battery_chart";
@@ -73,22 +77,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
void onSelectedIndexUpdated(); void onSelectedIndexUpdated();
} }
@VisibleForTesting @VisibleForTesting Context mPrefContext;
Context mPrefContext; @VisibleForTesting TextView mChartSummaryTextView;
@VisibleForTesting @VisibleForTesting BatteryChartView mDailyChartView;
TextView mChartSummaryTextView; @VisibleForTesting BatteryChartView mHourlyChartView;
@VisibleForTesting @VisibleForTesting int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
BatteryChartView mDailyChartView; @VisibleForTesting int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting @VisibleForTesting int mDailyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
BatteryChartView mHourlyChartView; @VisibleForTesting int mHourlyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
@VisibleForTesting
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
int mDailyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
@VisibleForTesting
int mHourlyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
private boolean mIs24HourFormat; private boolean mIs24HourFormat;
private View mBatteryChartViewGroup; private View mBatteryChartViewGroup;
@@ -100,13 +96,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private final MetricsFeatureProvider mMetricsFeatureProvider; private final MetricsFeatureProvider mMetricsFeatureProvider;
private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Handler mHandler = new Handler(Looper.getMainLooper());
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter = private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
createHourlyChartAnimatorListenerAdapter(/*visible=*/ true); createHourlyChartAnimatorListenerAdapter(/* visible= */ true);
private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter = private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
createHourlyChartAnimatorListenerAdapter(/*visible=*/ false); createHourlyChartAnimatorListenerAdapter(/* visible= */ false);
@VisibleForTesting @VisibleForTesting
final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator = final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
new DailyChartLabelTextGenerator(); new DailyChartLabelTextGenerator();
@VisibleForTesting @VisibleForTesting
final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator = final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator =
new HourlyChartLabelTextGenerator(); new HourlyChartLabelTextGenerator();
@@ -116,8 +113,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
super(context); super(context);
mActivity = activity; mActivity = activity;
mIs24HourFormat = DateFormat.is24HourFormat(context); mIs24HourFormat = DateFormat.is24HourFormat(context);
mMetricsFeatureProvider = mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
if (lifecycle != null) { if (lifecycle != null) {
lifecycle.addObserver(this); lifecycle.addObserver(this);
} }
@@ -128,12 +124,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (savedInstanceState == null) { if (savedInstanceState == null) {
return; return;
} }
mDailyChartIndex = mDailyChartIndex = savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex); mHourlyChartIndex = savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
mHourlyChartIndex = Log.d(
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex); TAG,
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d", String.format(
mDailyChartIndex, mHourlyChartIndex)); "onCreate() dailyIndex=%d hourlyIndex=%d",
mDailyChartIndex, mHourlyChartIndex));
} }
@Override @Override
@@ -149,8 +146,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex); savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex); savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d", Log.d(
mDailyChartIndex, mHourlyChartIndex)); TAG,
String.format(
"onSaveInstanceState() dailyIndex=%d hourlyIndex=%d",
mDailyChartIndex, mHourlyChartIndex));
} }
@Override @Override
@@ -158,7 +158,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (mActivity == null || mActivity.isChangingConfigurations()) { if (mActivity == null || mActivity.isChangingConfigurations()) {
BatteryDiffEntry.clearCache(); BatteryDiffEntry.clearCache();
} }
mHandler.removeCallbacksAndMessages(/*token=*/ null); mHandler.removeCallbacksAndMessages(/* token= */ null);
} }
@Override @Override
@@ -204,19 +204,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
refreshUi(); refreshUi();
return; return;
} }
mDailyViewModel = new BatteryChartViewModel( mDailyViewModel =
batteryLevelData.getDailyBatteryLevels().getLevels(), new BatteryChartViewModel(
batteryLevelData.getDailyBatteryLevels().getTimestamps(), batteryLevelData.getDailyBatteryLevels().getLevels(),
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
mDailyChartLabelTextGenerator); BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
mDailyChartLabelTextGenerator);
mHourlyViewModels = new ArrayList<>(); mHourlyViewModels = new ArrayList<>();
for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay : for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay :
batteryLevelData.getHourlyBatteryLevelsPerDay()) { batteryLevelData.getHourlyBatteryLevelsPerDay()) {
mHourlyViewModels.add(new BatteryChartViewModel( mHourlyViewModels.add(
hourlyBatteryLevelsPerDay.getLevels(), new BatteryChartViewModel(
hourlyBatteryLevelsPerDay.getTimestamps(), hourlyBatteryLevelsPerDay.getLevels(),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, hourlyBatteryLevelsPerDay.getTimestamps(),
mHourlyChartLabelTextGenerator.updateSpecialCaseContext(batteryLevelData))); BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
mHourlyChartLabelTextGenerator.updateSpecialCaseContext(
batteryLevelData)));
} }
refreshUi(); refreshUi();
} }
@@ -248,17 +251,21 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
mDailyChartIndex = mDailyHighlightSlotIndex; mDailyChartIndex = mDailyHighlightSlotIndex;
mHourlyChartIndex = mHourlyHighlightSlotIndex; mHourlyChartIndex = mHourlyHighlightSlotIndex;
Log.d(TAG, String.format("onDailyChartSelect:%d, onHourlyChartSelect:%d", Log.d(
mDailyChartIndex, mHourlyChartIndex)); TAG,
String.format(
"onDailyChartSelect:%d, onHourlyChartSelect:%d",
mDailyChartIndex, mHourlyChartIndex));
refreshUi(); refreshUi();
mHandler.post(() -> mDailyChartView.announceForAccessibility( mHandler.post(
getAccessibilityAnnounceMessage())); () -> mDailyChartView.announceForAccessibility(getAccessibilityAnnounceMessage()));
if (mOnSelectedIndexUpdatedListener != null) { if (mOnSelectedIndexUpdatedListener != null) {
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated(); mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
} }
} }
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView, void setBatteryChartView(
@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) { @NonNull final BatteryChartView hourlyChartView) {
final View parentView = (View) dailyChartView.getParent(); final View parentView = (View) dailyChartView.getParent();
if (parentView != null && parentView.getId() == R.id.battery_chart_group) { if (parentView != null && parentView.getId() == R.id.battery_chart_group) {
@@ -270,58 +277,67 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
if (mBatteryChartViewGroup != null) { if (mBatteryChartViewGroup != null) {
final View grandparentView = (View) mBatteryChartViewGroup.getParent(); final View grandparentView = (View) mBatteryChartViewGroup.getParent();
mChartSummaryTextView = grandparentView != null mChartSummaryTextView =
? grandparentView.findViewById(R.id.chart_summary) : null; grandparentView != null
? grandparentView.findViewById(R.id.chart_summary)
: null;
} }
} }
private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView, private void setBatteryChartViewInner(
@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) { @NonNull final BatteryChartView hourlyChartView) {
mDailyChartView = dailyChartView; mDailyChartView = dailyChartView;
mDailyChartView.setOnSelectListener(trapezoidIndex -> { mDailyChartView.setOnSelectListener(
if (mDailyChartIndex == trapezoidIndex) { trapezoidIndex -> {
return; if (mDailyChartIndex == trapezoidIndex) {
} return;
Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex); }
mDailyChartIndex = trapezoidIndex; Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; mDailyChartIndex = trapezoidIndex;
refreshUi(); mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
mHandler.post(() -> mDailyChartView.announceForAccessibility( refreshUi();
getAccessibilityAnnounceMessage())); mHandler.post(
mMetricsFeatureProvider.action( () ->
mPrefContext, mDailyChartView.announceForAccessibility(
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL getAccessibilityAnnounceMessage()));
? SettingsEnums.ACTION_BATTERY_USAGE_DAILY_SHOW_ALL mMetricsFeatureProvider.action(
: SettingsEnums.ACTION_BATTERY_USAGE_DAILY_TIME_SLOT, mPrefContext,
mDailyChartIndex); trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
if (mOnSelectedIndexUpdatedListener != null) { ? SettingsEnums.ACTION_BATTERY_USAGE_DAILY_SHOW_ALL
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated(); : SettingsEnums.ACTION_BATTERY_USAGE_DAILY_TIME_SLOT,
} mDailyChartIndex);
}); if (mOnSelectedIndexUpdatedListener != null) {
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
}
});
mHourlyChartView = hourlyChartView; mHourlyChartView = hourlyChartView;
mHourlyChartView.setOnSelectListener(trapezoidIndex -> { mHourlyChartView.setOnSelectListener(
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { trapezoidIndex -> {
// This will happen when a daily slot and an hour slot are clicked together. if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
return; // This will happen when a daily slot and an hour slot are clicked together.
} return;
if (mHourlyChartIndex == trapezoidIndex) { }
return; if (mHourlyChartIndex == trapezoidIndex) {
} return;
Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex); }
mHourlyChartIndex = trapezoidIndex; Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
refreshUi(); mHourlyChartIndex = trapezoidIndex;
mHandler.post(() -> mHourlyChartView.announceForAccessibility( refreshUi();
getAccessibilityAnnounceMessage())); mHandler.post(
mMetricsFeatureProvider.action( () ->
mPrefContext, mHourlyChartView.announceForAccessibility(
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL getAccessibilityAnnounceMessage()));
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL mMetricsFeatureProvider.action(
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT, mPrefContext,
mHourlyChartIndex); trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
if (mOnSelectedIndexUpdatedListener != null) { ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated(); : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT,
} mHourlyChartIndex);
}); if (mOnSelectedIndexUpdatedListener != null) {
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
}
});
refreshUi(); refreshUi();
} }
@@ -371,18 +387,18 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
// Multiple days are selected, hide the hourly chart view. // Multiple days are selected, hide the hourly chart view.
animateBatteryHourlyChartView(/*visible=*/ false); animateBatteryHourlyChartView(/* visible= */ false);
} else { } else {
animateBatteryHourlyChartView(/*visible=*/ true); animateBatteryHourlyChartView(/* visible= */ true);
final BatteryChartViewModel hourlyViewModel = final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
mHourlyViewModels.get(mDailyChartIndex);
if (mHourlyChartIndex >= hourlyViewModel.size()) { if (mHourlyChartIndex >= hourlyViewModel.size()) {
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
} }
hourlyViewModel.setSelectedIndex(mHourlyChartIndex); hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
hourlyViewModel.setHighlightSlotIndex((mDailyChartIndex == mDailyHighlightSlotIndex) hourlyViewModel.setHighlightSlotIndex(
? mHourlyHighlightSlotIndex (mDailyChartIndex == mDailyHighlightSlotIndex)
: BatteryChartViewModel.SELECTED_INDEX_INVALID); ? mHourlyHighlightSlotIndex
: BatteryChartViewModel.SELECTED_INDEX_INVALID);
mHourlyChartView.setViewModel(hourlyViewModel); mHourlyChartView.setViewModel(hourlyViewModel);
} }
} }
@@ -401,8 +417,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return selectedDayText; return selectedDayText;
} }
final String selectedHourText = mHourlyViewModels.get(mDailyChartIndex).getFullText( final String selectedHourText =
mHourlyChartIndex); mHourlyViewModels.get(mDailyChartIndex).getFullText(mHourlyChartIndex);
if (isBatteryLevelDataInOneDay()) { if (isBatteryLevelDataInOneDay()) {
return selectedHourText; return selectedHourText;
} }
@@ -422,7 +438,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private void animateBatteryChartViewGroup() { private void animateBatteryChartViewGroup() {
if (mBatteryChartViewGroup != null && mBatteryChartViewGroup.getAlpha() == 0) { if (mBatteryChartViewGroup != null && mBatteryChartViewGroup.getAlpha() == 0) {
mBatteryChartViewGroup.animate().alpha(1f).setDuration(FADE_IN_ANIMATION_DURATION) mBatteryChartViewGroup
.animate()
.alpha(1f)
.setDuration(FADE_IN_ANIMATION_DURATION)
.start(); .start();
} }
} }
@@ -435,13 +454,15 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (visible) { if (visible) {
mHourlyChartView.setVisibility(View.VISIBLE); mHourlyChartView.setVisibility(View.VISIBLE);
mHourlyChartView.animate() mHourlyChartView
.animate()
.alpha(1f) .alpha(1f)
.setDuration(FADE_IN_ANIMATION_DURATION) .setDuration(FADE_IN_ANIMATION_DURATION)
.setListener(mHourlyChartFadeInAdapter) .setListener(mHourlyChartFadeInAdapter)
.start(); .start();
} else { } else {
mHourlyChartView.animate() mHourlyChartView
.animate()
.alpha(0f) .alpha(0f)
.setDuration(FADE_OUT_ANIMATION_DURATION) .setDuration(FADE_OUT_ANIMATION_DURATION)
.setListener(mHourlyChartFadeOutAdapter) .setListener(mHourlyChartFadeOutAdapter)
@@ -467,6 +488,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mHourlyChartView.setVisibility(visibility); mHourlyChartView.setVisibility(visibility);
} }
} }
@Override @Override
public void onAnimationCancel(Animator animation) { public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation); super.onAnimationCancel(animation);
@@ -483,7 +505,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private boolean isAllSelected() { private boolean isAllSelected() {
return (isBatteryLevelDataInOneDay() return (isBatteryLevelDataInOneDay()
|| mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) || mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL; && mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
} }
@@ -493,8 +515,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return 0; return 0;
} }
List<Long> dailyTimestamps = batteryLevelData.getDailyBatteryLevels().getTimestamps(); List<Long> dailyTimestamps = batteryLevelData.getDailyBatteryLevels().getTimestamps();
return (int) ((dailyTimestamps.get(dailyTimestamps.size() - 1) - dailyTimestamps.get(0)) return (int)
/ DateUtils.HOUR_IN_MILLIS); ((dailyTimestamps.get(dailyTimestamps.size() - 1) - dailyTimestamps.get(0))
/ DateUtils.HOUR_IN_MILLIS);
} }
/** Used for {@link AppBatteryPreferenceController}. */ /** Used for {@link AppBatteryPreferenceController}. */
@@ -505,17 +528,21 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) { if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
return null; return null;
} }
Log.d(TAG, String.format("getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms", Log.d(
batteryHistoryMap.size(), (System.currentTimeMillis() - start))); TAG,
String.format(
"getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms",
batteryHistoryMap.size(), (System.currentTimeMillis() - start)));
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageData = final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageData =
DataProcessor.getBatteryUsageData(context, batteryHistoryMap); DataProcessor.getBatteryUsageData(context, batteryHistoryMap);
if (batteryUsageData == null) { if (batteryUsageData == null) {
return null; return null;
} }
BatteryDiffData allBatteryDiffData = batteryUsageData.get( BatteryDiffData allBatteryDiffData =
BatteryChartViewModel.SELECTED_INDEX_ALL).get( batteryUsageData
BatteryChartViewModel.SELECTED_INDEX_ALL); .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
return allBatteryDiffData == null ? null : allBatteryDiffData.getAppDiffEntryList(); return allBatteryDiffData == null ? null : allBatteryDiffData.getAppDiffEntryList();
} }
@@ -546,23 +573,23 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return null; return null;
} }
private final class DailyChartLabelTextGenerator implements private final class DailyChartLabelTextGenerator
BatteryChartViewModel.LabelTextGenerator { implements BatteryChartViewModel.LabelTextGenerator {
@Override @Override
public String generateText(List<Long> timestamps, int index) { public String generateText(List<Long> timestamps, int index) {
return ConvertUtils.utcToLocalTimeDayOfWeek(mContext, return ConvertUtils.utcToLocalTimeDayOfWeek(
timestamps.get(index), /* isAbbreviation= */ true); mContext, timestamps.get(index), /* isAbbreviation= */ true);
} }
@Override @Override
public String generateFullText(List<Long> timestamps, int index) { public String generateFullText(List<Long> timestamps, int index) {
return ConvertUtils.utcToLocalTimeDayOfWeek(mContext, return ConvertUtils.utcToLocalTimeDayOfWeek(
timestamps.get(index), /* isAbbreviation= */ false); mContext, timestamps.get(index), /* isAbbreviation= */ false);
} }
} }
private final class HourlyChartLabelTextGenerator implements private final class HourlyChartLabelTextGenerator
BatteryChartViewModel.LabelTextGenerator { implements BatteryChartViewModel.LabelTextGenerator {
private static final int FULL_CHARGE_BATTERY_LEVEL = 100; private static final int FULL_CHARGE_BATTERY_LEVEL = 100;
private boolean mIsFromFullCharge; private boolean mIsFromFullCharge;
@@ -593,8 +620,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
public String generateFullText(List<Long> timestamps, int index) { public String generateFullText(List<Long> timestamps, int index) {
return index == timestamps.size() - 1 return index == timestamps.size() - 1
? generateText(timestamps, index) ? generateText(timestamps, index)
: mContext.getString(R.string.battery_usage_timestamps_hyphen, : mContext.getString(
generateText(timestamps, index), generateText(timestamps, index + 1)); R.string.battery_usage_timestamps_hyphen,
generateText(timestamps, index),
generateText(timestamps, index + 1));
} }
HourlyChartLabelTextGenerator updateSpecialCaseContext( HourlyChartLabelTextGenerator updateSpecialCaseContext(
@@ -604,8 +633,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
this.mIsFromFullCharge = this.mIsFromFullCharge =
firstDayLevelData.getLevels().get(0) == FULL_CHARGE_BATTERY_LEVEL; firstDayLevelData.getLevels().get(0) == FULL_CHARGE_BATTERY_LEVEL;
this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0); this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0);
this.mLatestTimestamp = getLast(getLast( this.mLatestTimestamp =
batteryLevelData.getHourlyBatteryLevelsPerDay()).getTimestamps()); getLast(
getLast(batteryLevelData.getHourlyBatteryLevelsPerDay())
.getTimestamps());
return this; return this;
} }
} }

View File

@@ -74,7 +74,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private final String[] mPercentages = getPercentages(); private final String[] mPercentages = getPercentages();
private final Rect mIndent = new Rect(); private final Rect mIndent = new Rect();
private final Rect[] mPercentageBounds = new Rect[]{new Rect(), new Rect(), new Rect()}; private final Rect[] mPercentageBounds = new Rect[] {new Rect(), new Rect(), new Rect()};
private final List<Rect> mAxisLabelsBounds = new ArrayList<>(); private final List<Rect> mAxisLabelsBounds = new ArrayList<>();
private final Set<Integer> mLabelDrawnIndexes = new ArraySet<>(); private final Set<Integer> mLabelDrawnIndexes = new ArraySet<>();
private final int mLayoutDirection = private final int mLayoutDirection =
@@ -106,11 +106,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private AccessibilityNodeProvider mAccessibilityNodeProvider; private AccessibilityNodeProvider mAccessibilityNodeProvider;
private BatteryChartView.OnSelectListener mOnSelectListener; private BatteryChartView.OnSelectListener mOnSelectListener;
@VisibleForTesting @VisibleForTesting TrapezoidSlot[] mTrapezoidSlots;
TrapezoidSlot[] mTrapezoidSlots;
// Records the location to calculate selected index. // Records the location to calculate selected index.
@VisibleForTesting @VisibleForTesting float mTouchUpEventX = Float.MIN_VALUE;
float mTouchUpEventX = Float.MIN_VALUE;
public BatteryChartView(Context context) { public BatteryChartView(Context context) {
super(context, null); super(context, null);
@@ -133,9 +131,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
return; return;
} }
Log.d(TAG, String.format( Log.d(
"setViewModel(): size: %d, selectedIndex: %d, getHighlightSlotIndex: %d", TAG,
viewModel.size(), viewModel.selectedIndex(), viewModel.getHighlightSlotIndex())); String.format(
"setViewModel(): size: %d, selectedIndex: %d, getHighlightSlotIndex: %d",
viewModel.size(),
viewModel.selectedIndex(),
viewModel.getHighlightSlotIndex()));
mViewModel = viewModel; mViewModel = viewModel;
initializeAxisLabelsBounds(); initializeAxisLabelsBounds();
initializeTrapezoidSlots(viewModel.size() - 1); initializeTrapezoidSlots(viewModel.size() - 1);
@@ -169,7 +171,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mTextPaint.setTextAlign(Paint.Align.LEFT); mTextPaint.setTextAlign(Paint.Align.LEFT);
for (int index = 0; index < mPercentages.length; index++) { for (int index = 0; index < mPercentages.length; index++) {
mTextPaint.getTextBounds( mTextPaint.getTextBounds(
mPercentages[index], 0, mPercentages[index].length(), mPercentages[index],
0,
mPercentages[index].length(),
mPercentageBounds[index]); mPercentageBounds[index]);
} }
// Updates the indent configurations. // Updates the indent configurations.
@@ -292,7 +296,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
// Selects all if users click the same trapezoid item two times. // Selects all if users click the same trapezoid item two times.
mOnSelectListener.onSelect( mOnSelectListener.onSelect(
index == mViewModel.selectedIndex() index == mViewModel.selectedIndex()
? BatteryChartViewModel.SELECTED_INDEX_ALL : index); ? BatteryChartViewModel.SELECTED_INDEX_ALL
: index);
} }
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
} }
@@ -327,8 +332,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
setBackgroundColor(Color.TRANSPARENT); setBackgroundColor(Color.TRANSPARENT);
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context); mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
mTrapezoidColor = Utils.getDisabled(context, mTrapezoidSolidColor); mTrapezoidColor = Utils.getDisabled(context, mTrapezoidSolidColor);
mTrapezoidHoverColor = Utils.getColorAttrDefaultColor(context, mTrapezoidHoverColor =
com.android.internal.R.attr.materialColorSecondaryContainer); Utils.getColorAttrDefaultColor(
context, com.android.internal.R.attr.materialColorSecondaryContainer);
// Initializes the divider line paint. // Initializes the divider line paint.
final Resources resources = getContext().getResources(); final Resources resources = getContext().getResources();
mDividerWidth = resources.getDimensionPixelSize(R.dimen.chartview_divider_width); mDividerWidth = resources.getDimensionPixelSize(R.dimen.chartview_divider_width);
@@ -353,25 +359,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
// Initializes for drawing text information. // Initializes for drawing text information.
mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding); mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding);
// Initializes the padding top for drawing text information. // Initializes the padding top for drawing text information.
mTransomViewHeight = resources.getDimensionPixelSize( mTransomViewHeight =
R.dimen.chartview_transom_layout_height); resources.getDimensionPixelSize(R.dimen.chartview_transom_layout_height);
} }
private void initializeTransomPaint() { private void initializeTransomPaint() {
if (mTransomLinePaint != null && mTransomSelectedSlotPaint != null if (mTransomLinePaint != null
&& mTransomSelectedSlotPaint != null
&& mTransomIcon != null) { && mTransomIcon != null) {
return; return;
} }
// Initializes the transom line paint. // Initializes the transom line paint.
final Resources resources = getContext().getResources(); final Resources resources = getContext().getResources();
final int transomLineWidth = resources.getDimensionPixelSize( final int transomLineWidth =
R.dimen.chartview_transom_width); resources.getDimensionPixelSize(R.dimen.chartview_transom_width);
final int transomRadius = resources.getDimensionPixelSize(R.dimen.chartview_transom_radius); final int transomRadius = resources.getDimensionPixelSize(R.dimen.chartview_transom_radius);
mTransomPadding = transomRadius * .5f; mTransomPadding = transomRadius * .5f;
mTransomTop = resources.getDimensionPixelSize(R.dimen.chartview_transom_padding_top); mTransomTop = resources.getDimensionPixelSize(R.dimen.chartview_transom_padding_top);
mTransomLineDefaultColor = Utils.getDisabled(mContext, DIVIDER_COLOR); mTransomLineDefaultColor = Utils.getDisabled(mContext, DIVIDER_COLOR);
mTransomLineSelectedColor = resources.getColor( mTransomLineSelectedColor =
R.color.color_battery_anomaly_yellow_selector); resources.getColor(R.color.color_battery_anomaly_yellow_selector);
final int slotHighlightColor = Utils.getDisabled(mContext, mTransomLineSelectedColor); final int slotHighlightColor = Utils.getDisabled(mContext, mTransomLineSelectedColor);
mTransomIconSize = resources.getDimensionPixelSize(R.dimen.chartview_transom_icon_size); mTransomIconSize = resources.getDimensionPixelSize(R.dimen.chartview_transom_icon_size);
mTransomLinePaint = new Paint(); mTransomLinePaint = new Paint();
@@ -396,18 +403,16 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
final float availableSpace = bottomOffsetY - topOffsetY; final float availableSpace = bottomOffsetY - topOffsetY;
mDividerPaint.setColor(DIVIDER_COLOR); mDividerPaint.setColor(DIVIDER_COLOR);
final float dividerOffsetUnit = final float dividerOffsetUnit = availableSpace / (float) (HORIZONTAL_DIVIDER_COUNT - 1);
availableSpace / (float) (HORIZONTAL_DIVIDER_COUNT - 1);
// Draws 5 divider lines. // Draws 5 divider lines.
for (int index = 0; index < HORIZONTAL_DIVIDER_COUNT; index++) { for (int index = 0; index < HORIZONTAL_DIVIDER_COUNT; index++) {
float offsetY = topOffsetY + dividerOffsetUnit * index; float offsetY = topOffsetY + dividerOffsetUnit * index;
canvas.drawLine(mIndent.left, offsetY, canvas.drawLine(mIndent.left, offsetY, mIndent.left + width, offsetY, mDividerPaint);
mIndent.left + width, offsetY, mDividerPaint);
// Draws percentage text only for 100% / 50% / 0% // Draws percentage text only for 100% / 50% / 0%
if (index % 2 == 0) { if (index % 2 == 0) {
drawPercentage(canvas, /*index=*/ (index + 1) / 2, offsetY); drawPercentage(canvas, /* index= */ (index + 1) / 2, offsetY);
} }
} }
} }
@@ -438,21 +443,23 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
Rect[] axisLabelDisplayAreas; Rect[] axisLabelDisplayAreas;
switch (mViewModel.axisLabelPosition()) { switch (mViewModel.axisLabelPosition()) {
case CENTER_OF_TRAPEZOIDS: case CENTER_OF_TRAPEZOIDS:
axisLabelDisplayAreas = getAxisLabelDisplayAreas( axisLabelDisplayAreas =
/* size= */ mViewModel.size() - 1, getAxisLabelDisplayAreas(
/* baselineX= */ mIndent.left + mDividerWidth + unitWidth * .5f, /* size= */ mViewModel.size() - 1,
/* offsetX= */ mDividerWidth + unitWidth, /* baselineX= */ mIndent.left + mDividerWidth + unitWidth * .5f,
baselineY, /* offsetX= */ mDividerWidth + unitWidth,
/* shiftFirstAndLast= */ false); baselineY,
/* shiftFirstAndLast= */ false);
break; break;
case BETWEEN_TRAPEZOIDS: case BETWEEN_TRAPEZOIDS:
default: default:
axisLabelDisplayAreas = getAxisLabelDisplayAreas( axisLabelDisplayAreas =
/* size= */ mViewModel.size(), getAxisLabelDisplayAreas(
/* baselineX= */ mIndent.left + mDividerWidth * .5f, /* size= */ mViewModel.size(),
/* offsetX= */ mDividerWidth + unitWidth, /* baselineX= */ mIndent.left + mDividerWidth * .5f,
baselineY, /* offsetX= */ mDividerWidth + unitWidth,
/* shiftFirstAndLast= */ true); baselineY,
/* shiftFirstAndLast= */ true);
break; break;
} }
drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY); drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY);
@@ -481,8 +488,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
/** Gets all the axis label texts displaying area positions if they are shown. */ /** Gets all the axis label texts displaying area positions if they are shown. */
private Rect[] getAxisLabelDisplayAreas(final int size, final float baselineX, private Rect[] getAxisLabelDisplayAreas(
final float offsetX, final float baselineY, final boolean shiftFirstAndLast) { final int size,
final float baselineX,
final float offsetX,
final float baselineY,
final boolean shiftFirstAndLast) {
final Rect[] result = new Rect[size]; final Rect[] result = new Rect[size];
for (int index = 0; index < result.length; index++) { for (int index = 0; index < result.length; index++) {
final float width = mAxisLabelsBounds.get(index).width(); final float width = mAxisLabelsBounds.get(index).width();
@@ -522,8 +533,11 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
* labels and then recursively draw the 3 parts. If there are any overlaps, skip drawing and go * labels and then recursively draw the 3 parts. If there are any overlaps, skip drawing and go
* back to the uplevel of the recursion. * back to the uplevel of the recursion.
*/ */
private void drawAxisLabelsBetweenStartIndexAndEndIndex(Canvas canvas, private void drawAxisLabelsBetweenStartIndexAndEndIndex(
final Rect[] displayAreas, final int startIndex, final int endIndex, Canvas canvas,
final Rect[] displayAreas,
final int startIndex,
final int endIndex,
final float baselineY) { final float baselineY) {
if (endIndex - startIndex <= 1) { if (endIndex - startIndex <= 1) {
return; return;
@@ -576,15 +590,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextAlign(Paint.Align.CENTER);
// Reverse the sort of axis labels for RTL // Reverse the sort of axis labels for RTL
if (isRTL()) { if (isRTL()) {
index = mViewModel.axisLabelPosition() == BETWEEN_TRAPEZOIDS index =
? mViewModel.size() - index - 1 // for hourly mViewModel.axisLabelPosition() == BETWEEN_TRAPEZOIDS
: mViewModel.size() - index - 2; // for daily ? mViewModel.size() - index - 1 // for hourly
: mViewModel.size() - index - 2; // for daily
} }
canvas.drawText( canvas.drawText(mViewModel.getText(index), displayArea.centerX(), baselineY, mTextPaint);
mViewModel.getText(index),
displayArea.centerX(),
baselineY,
mTextPaint);
mLabelDrawnIndexes.add(index); mLabelDrawnIndexes.add(index);
} }
@@ -594,8 +605,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
return; return;
} }
final float trapezoidBottom = final float trapezoidBottom =
getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth - mTrapezoidVOffset;
- mTrapezoidVOffset;
final float availableSpace = final float availableSpace =
trapezoidBottom - mDividerWidth * .5f - mIndent.top - mTrapezoidVOffset; trapezoidBottom - mDividerWidth * .5f - mIndent.top - mTrapezoidVOffset;
final float unitHeight = availableSpace / 100f; final float unitHeight = availableSpace / 100f;
@@ -608,17 +618,24 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
continue; continue;
} }
// Configures the trapezoid paint color. // Configures the trapezoid paint color.
final int trapezoidColor = (mViewModel.selectedIndex() == index final int trapezoidColor =
|| mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL) (mViewModel.selectedIndex() == index
? mTrapezoidSolidColor : mTrapezoidColor; || mViewModel.selectedIndex()
final boolean isHoverState = mHoveredIndex == index && isValidToDraw(mViewModel, == BatteryChartViewModel.SELECTED_INDEX_ALL)
mHoveredIndex); ? mTrapezoidSolidColor
: mTrapezoidColor;
final boolean isHoverState =
mHoveredIndex == index && isValidToDraw(mViewModel, mHoveredIndex);
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor); mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
float leftTop = round( float leftTop =
trapezoidBottom - requireNonNull(mViewModel.getLevel(index)) * unitHeight); round(
float rightTop = round(trapezoidBottom trapezoidBottom
- requireNonNull(mViewModel.getLevel(index + 1)) * unitHeight); - requireNonNull(mViewModel.getLevel(index)) * unitHeight);
float rightTop =
round(
trapezoidBottom
- requireNonNull(mViewModel.getLevel(index + 1)) * unitHeight);
// Mirror the shape of the trapezoid for RTL // Mirror the shape of the trapezoid for RTL
if (isRTL()) { if (isRTL()) {
float temp = leftTop; float temp = leftTop;
@@ -639,8 +656,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
private boolean isHighlightSlotValid() { private boolean isHighlightSlotValid() {
return mViewModel != null && mViewModel.getHighlightSlotIndex() return mViewModel != null
!= BatteryChartViewModel.SELECTED_INDEX_INVALID; && mViewModel.getHighlightSlotIndex()
!= BatteryChartViewModel.SELECTED_INDEX_INVALID;
} }
private void drawTransomLine(Canvas canvas) { private void drawTransomLine(Canvas canvas) {
@@ -652,10 +670,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mTransomLinePaint.setColor(mTransomLineDefaultColor); mTransomLinePaint.setColor(mTransomLineDefaultColor);
final int width = getWidth() - abs(mIndent.width()); final int width = getWidth() - abs(mIndent.width());
final float transomOffset = mTrapezoidHOffset + mDividerWidth * .5f + mTransomPadding; final float transomOffset = mTrapezoidHOffset + mDividerWidth * .5f + mTransomPadding;
final float trapezoidBottom = getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth final float trapezoidBottom =
- mTrapezoidVOffset; getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth - mTrapezoidVOffset;
canvas.drawLine(mIndent.left + transomOffset, mTransomTop, canvas.drawLine(
mIndent.left + width - transomOffset, mTransomTop, mIndent.left + transomOffset,
mTransomTop,
mIndent.left + width - transomOffset,
mTransomTop,
mTransomLinePaint); mTransomLinePaint);
drawTransomIcon(canvas); drawTransomIcon(canvas);
// Draw selected segment of transom line and a highlight slot // Draw selected segment of transom line and a highlight slot
@@ -663,22 +684,28 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
final int index = mViewModel.getHighlightSlotIndex(); final int index = mViewModel.getHighlightSlotIndex();
final float startX = mTrapezoidSlots[index].mLeft; final float startX = mTrapezoidSlots[index].mLeft;
final float endX = mTrapezoidSlots[index].mRight; final float endX = mTrapezoidSlots[index].mRight;
canvas.drawLine(startX + mTransomPadding, mTransomTop, canvas.drawLine(
endX - mTransomPadding, mTransomTop, startX + mTransomPadding,
mTransomTop,
endX - mTransomPadding,
mTransomTop,
mTransomLinePaint); mTransomLinePaint);
canvas.drawRect(startX, mTransomTop, endX, trapezoidBottom, canvas.drawRect(startX, mTransomTop, endX, trapezoidBottom, mTransomSelectedSlotPaint);
mTransomSelectedSlotPaint);
} }
private void drawTransomIcon(Canvas canvas) { private void drawTransomIcon(Canvas canvas) {
if (mTransomIcon == null) { if (mTransomIcon == null) {
return; return;
} }
final int left = isRTL() final int left =
? mIndent.left - mTextPadding - mTransomIconSize isRTL()
: getWidth() - abs(mIndent.width()) + mTextPadding; ? mIndent.left - mTextPadding - mTransomIconSize
mTransomIcon.setBounds(left, mTransomTop - mTransomIconSize / 2, : getWidth() - abs(mIndent.width()) + mTextPadding;
left + mTransomIconSize, mTransomTop + mTransomIconSize / 2); mTransomIcon.setBounds(
left,
mTransomTop - mTransomIconSize / 2,
left + mTransomIconSize,
mTransomTop + mTransomIconSize / 2);
mTransomIcon.draw(canvas); mTransomIcon.draw(canvas);
} }
@@ -689,8 +716,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
for (int index = 0; index < mTrapezoidSlots.length; index++) { for (int index = 0; index < mTrapezoidSlots.length; index++) {
final TrapezoidSlot slot = mTrapezoidSlots[index]; final TrapezoidSlot slot = mTrapezoidSlots[index];
if (x >= slot.mLeft - mTrapezoidHOffset if (x >= slot.mLeft - mTrapezoidHOffset && x <= slot.mRight + mTrapezoidHOffset) {
&& x <= slot.mRight + mTrapezoidHOffset) {
return index; return index;
} }
} }
@@ -712,9 +738,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private static boolean isTrapezoidIndexValid( private static boolean isTrapezoidIndexValid(
@NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) {
return viewModel != null return viewModel != null && trapezoidIndex >= 0 && trapezoidIndex < viewModel.size() - 1;
&& trapezoidIndex >= 0
&& trapezoidIndex < viewModel.size() - 1;
} }
private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) { private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) {
@@ -733,10 +757,11 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
private static String[] getPercentages() { private static String[] getPercentages() {
return new String[]{ return new String[] {
formatPercentage(/*percentage=*/ 100, /*round=*/ true), formatPercentage(/* percentage= */ 100, /* round= */ true),
formatPercentage(/*percentage=*/ 50, /*round=*/ true), formatPercentage(/* percentage= */ 50, /* round= */ true),
formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; formatPercentage(/* percentage= */ 0, /* round= */ true)
};
} }
private class BatteryChartAccessibilityNodeProvider extends AccessibilityNodeProvider { private class BatteryChartAccessibilityNodeProvider extends AccessibilityNodeProvider {
@@ -772,8 +797,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
@Override @Override
public boolean performAction(int virtualViewId, int action, public boolean performAction(int virtualViewId, int action, @Nullable Bundle arguments) {
@Nullable Bundle arguments) {
if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) { if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) {
return performAccessibilityAction(action, arguments); return performAccessibilityAction(action, arguments);
} }
@@ -783,11 +807,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
return true; return true;
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
return sendAccessibilityEvent(virtualViewId, return sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
return sendAccessibilityEvent(virtualViewId, return sendAccessibilityEvent(
virtualViewId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
default: default:

View File

@@ -57,14 +57,18 @@ class BatteryChartViewModel {
private int mSelectedIndex = SELECTED_INDEX_ALL; private int mSelectedIndex = SELECTED_INDEX_ALL;
private int mHighlightSlotIndex = SELECTED_INDEX_INVALID; private int mHighlightSlotIndex = SELECTED_INDEX_INVALID;
BatteryChartViewModel(@NonNull List<Integer> levels, @NonNull List<Long> timestamps, BatteryChartViewModel(
@NonNull List<Integer> levels,
@NonNull List<Long> timestamps,
@NonNull AxisLabelPosition axisLabelPosition, @NonNull AxisLabelPosition axisLabelPosition,
@NonNull LabelTextGenerator labelTextGenerator) { @NonNull LabelTextGenerator labelTextGenerator) {
Preconditions.checkArgument( Preconditions.checkArgument(
levels.size() == timestamps.size() && levels.size() >= MIN_LEVELS_DATA_SIZE, levels.size() == timestamps.size() && levels.size() >= MIN_LEVELS_DATA_SIZE,
String.format(Locale.ENGLISH, String.format(
Locale.ENGLISH,
"Invalid BatteryChartViewModel levels.size: %d, timestamps.size: %d.", "Invalid BatteryChartViewModel levels.size: %d, timestamps.size: %d.",
levels.size(), timestamps.size())); levels.size(),
timestamps.size()));
mLevels = levels; mLevels = levels;
mTimestamps = timestamps; mTimestamps = timestamps;
mAxisLabelPosition = axisLabelPosition; mAxisLabelPosition = axisLabelPosition;

View File

@@ -64,8 +64,7 @@ public class BatteryDiffEntry {
private static final Map<String, Pair<Integer, Integer>> SPECIAL_ENTRY_MAP = private static final Map<String, Pair<Integer, Integer>> SPECIAL_ENTRY_MAP =
Map.of( Map.of(
SYSTEM_APPS_KEY, SYSTEM_APPS_KEY,
Pair.create( Pair.create(R.string.battery_usage_system_apps, R.drawable.ic_power_system),
R.string.battery_usage_system_apps, R.drawable.ic_power_system),
UNINSTALLED_APPS_KEY, UNINSTALLED_APPS_KEY,
Pair.create( Pair.create(
R.string.battery_usage_uninstalled_apps, R.string.battery_usage_uninstalled_apps,
@@ -100,16 +99,11 @@ public class BatteryDiffEntry {
private UserManager mUserManager; private UserManager mUserManager;
private String mDefaultPackageName = null; private String mDefaultPackageName = null;
@VisibleForTesting @VisibleForTesting int mAppIconId;
int mAppIconId; @VisibleForTesting String mAppLabel = null;
@VisibleForTesting @VisibleForTesting Drawable mAppIcon = null;
String mAppLabel = null; @VisibleForTesting boolean mIsLoaded = false;
@VisibleForTesting @VisibleForTesting boolean mValidForRestriction = true;
Drawable mAppIcon = null;
@VisibleForTesting
boolean mIsLoaded = false;
@VisibleForTesting
boolean mValidForRestriction = true;
public BatteryDiffEntry( public BatteryDiffEntry(
Context context, Context context,
@@ -150,12 +144,24 @@ public class BatteryDiffEntry {
} }
public BatteryDiffEntry(Context context, String key, String legacyLabel, int consumerType) { public BatteryDiffEntry(Context context, String key, String legacyLabel, int consumerType) {
this(context, /*uid=*/ 0, /*userId=*/ 0, key, /*isHidden=*/ false, /*componentId=*/ -1, this(
/*legacyPackageName=*/ null, legacyLabel, consumerType, context,
/*foregroundUsageTimeInMs=*/ 0, /*backgroundUsageTimeInMs=*/ 0, /* uid= */ 0,
/*screenOnTimeInMs=*/ 0, /*consumePower=*/ 0, /*foregroundUsageConsumePower=*/ 0, /* userId= */ 0,
/*foregroundServiceUsageConsumePower=*/ 0, /*backgroundUsageConsumePower=*/ 0, key,
/*cachedUsageConsumePower=*/ 0); /* isHidden= */ false,
/* componentId= */ -1,
/* legacyPackageName= */ null,
legacyLabel,
consumerType,
/* foregroundUsageTimeInMs= */ 0,
/* backgroundUsageTimeInMs= */ 0,
/* screenOnTimeInMs= */ 0,
/* consumePower= */ 0,
/* foregroundUsageConsumePower= */ 0,
/* foregroundServiceUsageConsumePower= */ 0,
/* backgroundUsageConsumePower= */ 0,
/* cachedUsageConsumePower= */ 0);
} }
/** Sets the total consumed power in a specific time slot. */ /** Sets the total consumed power in a specific time slot. */
@@ -286,7 +292,8 @@ public class BatteryDiffEntry {
/** Whether the current BatteryDiffEntry is uninstalled app or not. */ /** Whether the current BatteryDiffEntry is uninstalled app or not. */
public boolean isUninstalledEntry() { public boolean isUninstalledEntry() {
final String packageName = getPackageName(); final String packageName = getPackageName();
if (TextUtils.isEmpty(packageName) || isSystemEntry() if (TextUtils.isEmpty(packageName)
|| isSystemEntry()
// Some special package UIDs could be 0. Those packages are not installed by users. // Some special package UIDs could be 0. Those packages are not installed by users.
|| mUid == BatteryUtils.UID_ZERO) { || mUid == BatteryUtils.UID_ZERO) {
return false; return false;
@@ -468,8 +475,9 @@ public class BatteryDiffEntry {
mAppIcon = nameAndIcon.mIcon; mAppIcon = nameAndIcon.mIcon;
} }
final BatteryEntry.NameAndIcon nameAndIcon = BatteryEntry.loadNameAndIcon( final BatteryEntry.NameAndIcon nameAndIcon =
mContext, uid, /*batteryEntry=*/ null, packageName, mAppLabel, mAppIcon); BatteryEntry.loadNameAndIcon(
mContext, uid, /* batteryEntry= */ null, packageName, mAppLabel, mAppIcon);
// Clears BatteryEntry internal cache since we will have another one. // Clears BatteryEntry internal cache since we will have another one.
BatteryEntry.clearUidCache(); BatteryEntry.clearUidCache();
if (nameAndIcon != null) { if (nameAndIcon != null) {
@@ -489,25 +497,48 @@ public class BatteryDiffEntry {
@Override @Override
public String toString() { public String toString() {
final StringBuilder builder = new StringBuilder() final StringBuilder builder =
.append("BatteryDiffEntry{") new StringBuilder()
.append(String.format("\n\tname=%s restrictable=%b", .append("BatteryDiffEntry{")
mAppLabel, mValidForRestriction)) .append(
.append(String.format("\n\tconsume=%.2f%% %f/%f", String.format(
mPercentage, mConsumePower, mTotalConsumePower)) "\n\tname=%s restrictable=%b",
.append(String.format("\n\tconsume power= foreground:%f foregroundService:%f", mAppLabel, mValidForRestriction))
mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower)) .append(
.append(String.format("\n\tconsume power= background:%f cached:%f", String.format(
mBackgroundUsageConsumePower, mCachedUsageConsumePower)) "\n\tconsume=%.2f%% %f/%f",
.append(String.format("\n\ttime= foreground:%s background:%s screen-on:%s", mPercentage, mConsumePower, mTotalConsumePower))
StringUtil.formatElapsedTime(mContext, (double) mForegroundUsageTimeInMs, .append(
/*withSeconds=*/ true, /*collapseTimeUnit=*/ false), String.format(
StringUtil.formatElapsedTime(mContext, (double) mBackgroundUsageTimeInMs, "\n\tconsume power= foreground:%f foregroundService:%f",
/*withSeconds=*/ true, /*collapseTimeUnit=*/ false), mForegroundUsageConsumePower,
StringUtil.formatElapsedTime(mContext, (double) mScreenOnTimeInMs, mForegroundServiceUsageConsumePower))
/*withSeconds=*/ true, /*collapseTimeUnit=*/ false))) .append(
.append(String.format("\n\tpackage:%s|%s uid:%d userId:%d", String.format(
mLegacyPackageName, getPackageName(), mUid, mUserId)); "\n\tconsume power= background:%f cached:%f",
mBackgroundUsageConsumePower, mCachedUsageConsumePower))
.append(
String.format(
"\n\ttime= foreground:%s background:%s screen-on:%s",
StringUtil.formatElapsedTime(
mContext,
(double) mForegroundUsageTimeInMs,
/* withSeconds= */ true,
/* collapseTimeUnit= */ false),
StringUtil.formatElapsedTime(
mContext,
(double) mBackgroundUsageTimeInMs,
/* withSeconds= */ true,
/* collapseTimeUnit= */ false),
StringUtil.formatElapsedTime(
mContext,
(double) mScreenOnTimeInMs,
/* withSeconds= */ true,
/* collapseTimeUnit= */ false)))
.append(
String.format(
"\n\tpackage:%s|%s uid:%d userId:%d",
mLegacyPackageName, getPackageName(), mUid, mUserId));
return builder.toString(); return builder.toString();
} }

View File

@@ -45,8 +45,7 @@ import java.util.Comparator;
import java.util.Locale; import java.util.Locale;
/** /**
* Wraps the power usage data of a BatterySipper with information about package name * Wraps the power usage data of a BatterySipper with information about package name and icon image.
* and icon image.
*/ */
public class BatteryEntry { public class BatteryEntry {
@@ -58,11 +57,10 @@ public class BatteryEntry {
public final int mIconId; public final int mIconId;
public NameAndIcon(String name, Drawable icon, int iconId) { public NameAndIcon(String name, Drawable icon, int iconId) {
this(name, /*packageName=*/ null, icon, iconId); this(name, /* packageName= */ null, icon, iconId);
} }
public NameAndIcon( public NameAndIcon(String name, String packageName, Drawable icon, int iconId) {
String name, String packageName, Drawable icon, int iconId) {
this.mName = name; this.mName = name;
this.mIcon = icon; this.mIcon = icon;
this.mIconId = iconId; this.mIconId = iconId;
@@ -78,17 +76,20 @@ public class BatteryEntry {
static final int BATTERY_USAGE_INDEX_BACKGROUND = 2; static final int BATTERY_USAGE_INDEX_BACKGROUND = 2;
static final int BATTERY_USAGE_INDEX_CACHED = 3; static final int BATTERY_USAGE_INDEX_CACHED = 3;
static final Dimensions[] BATTERY_DIMENSIONS = new Dimensions[] { static final Dimensions[] BATTERY_DIMENSIONS =
new Dimensions( new Dimensions[] {
BatteryConsumer.POWER_COMPONENT_ANY, BatteryConsumer.PROCESS_STATE_FOREGROUND), new Dimensions(
new Dimensions( BatteryConsumer.POWER_COMPONENT_ANY,
BatteryConsumer.POWER_COMPONENT_ANY, BatteryConsumer.PROCESS_STATE_FOREGROUND),
BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE), new Dimensions(
new Dimensions( BatteryConsumer.POWER_COMPONENT_ANY,
BatteryConsumer.POWER_COMPONENT_ANY, BatteryConsumer.PROCESS_STATE_BACKGROUND), BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE),
new Dimensions( new Dimensions(
BatteryConsumer.POWER_COMPONENT_ANY, BatteryConsumer.PROCESS_STATE_CACHED), BatteryConsumer.POWER_COMPONENT_ANY,
}; BatteryConsumer.PROCESS_STATE_BACKGROUND),
new Dimensions(
BatteryConsumer.POWER_COMPONENT_ANY, BatteryConsumer.PROCESS_STATE_CACHED),
};
static final ArrayMap<String, UidToDetail> sUidCache = new ArrayMap<>(); static final ArrayMap<String, UidToDetail> sUidCache = new ArrayMap<>();
@@ -106,10 +107,8 @@ public class BatteryEntry {
private final BatteryConsumer mBatteryConsumer; private final BatteryConsumer mBatteryConsumer;
private final int mUid; private final int mUid;
private final boolean mIsHidden; private final boolean mIsHidden;
@ConvertUtils.ConsumerType @ConvertUtils.ConsumerType private final int mConsumerType;
private final int mConsumerType; @BatteryConsumer.PowerComponent private final int mPowerComponentId;
@BatteryConsumer.PowerComponent
private final int mPowerComponentId;
private long mUsageDurationMs; private long mUsageDurationMs;
private long mTimeInForegroundMs; private long mTimeInForegroundMs;
private long mTimeInBackgroundMs; private long mTimeInBackgroundMs;
@@ -131,13 +130,25 @@ public class BatteryEntry {
Drawable mIcon; Drawable mIcon;
} }
public BatteryEntry(Context context, UserManager um, BatteryConsumer batteryConsumer, public BatteryEntry(
boolean isHidden, int uid, String[] packages, String packageName) { Context context,
UserManager um,
BatteryConsumer batteryConsumer,
boolean isHidden,
int uid,
String[] packages,
String packageName) {
this(context, um, batteryConsumer, isHidden, uid, packages, packageName, true); this(context, um, batteryConsumer, isHidden, uid, packages, packageName, true);
} }
public BatteryEntry(Context context, UserManager um, BatteryConsumer batteryConsumer, public BatteryEntry(
boolean isHidden, int uid, String[] packages, String packageName, Context context,
UserManager um,
BatteryConsumer batteryConsumer,
boolean isHidden,
int uid,
String[] packages,
String packageName,
boolean loadDataInBackground) { boolean loadDataInBackground) {
mContext = context; mContext = context;
mBatteryConsumer = batteryConsumer; mBatteryConsumer = batteryConsumer;
@@ -156,8 +167,10 @@ public class BatteryEntry {
if (packages != null && packages.length == 1) { if (packages != null && packages.length == 1) {
mDefaultPackageName = packages[0]; mDefaultPackageName = packages[0];
} else { } else {
mDefaultPackageName = isSystemUid(uid) mDefaultPackageName =
? PACKAGE_SYSTEM : uidBatteryConsumer.getPackageWithHighestDrain(); isSystemUid(uid)
? PACKAGE_SYSTEM
: uidBatteryConsumer.getPackageWithHighestDrain();
} }
} }
if (mDefaultPackageName != null) { if (mDefaultPackageName != null) {
@@ -167,8 +180,10 @@ public class BatteryEntry {
pm.getApplicationInfo(mDefaultPackageName, 0 /* no flags */); pm.getApplicationInfo(mDefaultPackageName, 0 /* no flags */);
mName = pm.getApplicationLabel(appInfo).toString(); mName = pm.getApplicationLabel(appInfo).toString();
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: " Log.d(
+ mDefaultPackageName); TAG,
"PackageManager failed to retrieve ApplicationInfo for: "
+ mDefaultPackageName);
mName = mDefaultPackageName; mName = mDefaultPackageName;
} }
} }
@@ -176,20 +191,26 @@ public class BatteryEntry {
uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND); uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
mTimeInBackgroundMs = mTimeInBackgroundMs =
uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND); uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
mConsumedPowerInForeground = safeGetConsumedPower( mConsumedPowerInForeground =
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]); safeGetConsumedPower(
mConsumedPowerInForegroundService = safeGetConsumedPower( uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]);
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]); mConsumedPowerInForegroundService =
mConsumedPowerInBackground = safeGetConsumedPower( safeGetConsumedPower(
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_BACKGROUND]); uidBatteryConsumer,
mConsumedPowerInCached = safeGetConsumedPower( BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]);
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_CACHED]); mConsumedPowerInBackground =
safeGetConsumedPower(
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_BACKGROUND]);
mConsumedPowerInCached =
safeGetConsumedPower(
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_CACHED]);
} else if (batteryConsumer instanceof UserBatteryConsumer) { } else if (batteryConsumer instanceof UserBatteryConsumer) {
mUid = Process.INVALID_UID; mUid = Process.INVALID_UID;
mConsumerType = ConvertUtils.CONSUMER_TYPE_USER_BATTERY; mConsumerType = ConvertUtils.CONSUMER_TYPE_USER_BATTERY;
mConsumedPower = batteryConsumer.getConsumedPower(); mConsumedPower = batteryConsumer.getConsumedPower();
final NameAndIcon nameAndIcon = getNameAndIconFromUserId( final NameAndIcon nameAndIcon =
context, ((UserBatteryConsumer) batteryConsumer).getUserId()); getNameAndIconFromUserId(
context, ((UserBatteryConsumer) batteryConsumer).getUserId());
mIcon = nameAndIcon.mIcon; mIcon = nameAndIcon.mIcon;
mName = nameAndIcon.mName; mName = nameAndIcon.mName;
} else { } else {
@@ -198,8 +219,12 @@ public class BatteryEntry {
} }
/** Battery entry for a power component of AggregateBatteryConsumer */ /** Battery entry for a power component of AggregateBatteryConsumer */
public BatteryEntry(Context context, int powerComponentId, double devicePowerMah, public BatteryEntry(
long usageDurationMs, boolean isHidden) { Context context,
int powerComponentId,
double devicePowerMah,
long usageDurationMs,
boolean isHidden) {
mContext = context; mContext = context;
mBatteryConsumer = null; mBatteryConsumer = null;
mUid = Process.INVALID_UID; mUid = Process.INVALID_UID;
@@ -209,8 +234,7 @@ public class BatteryEntry {
mUsageDurationMs = usageDurationMs; mUsageDurationMs = usageDurationMs;
mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY; mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
final NameAndIcon nameAndIcon = final NameAndIcon nameAndIcon = getNameAndIconFromPowerComponent(context, powerComponentId);
getNameAndIconFromPowerComponent(context, powerComponentId);
mIconId = nameAndIcon.mIconId; mIconId = nameAndIcon.mIconId;
mName = nameAndIcon.mName; mName = nameAndIcon.mName;
if (mIconId != 0) { if (mIconId != 0) {
@@ -219,7 +243,10 @@ public class BatteryEntry {
} }
/** Battery entry for a custom power component of AggregateBatteryConsumer */ /** Battery entry for a custom power component of AggregateBatteryConsumer */
public BatteryEntry(Context context, int powerComponentId, String powerComponentName, public BatteryEntry(
Context context,
int powerComponentId,
String powerComponentName,
double devicePowerMah) { double devicePowerMah) {
mContext = context; mContext = context;
mBatteryConsumer = null; mBatteryConsumer = null;
@@ -266,8 +293,8 @@ public class BatteryEntry {
} }
final PackageManager pm = context.getPackageManager(); final PackageManager pm = context.getPackageManager();
final String[] packages = isSystemUid(uid) final String[] packages =
? new String[]{PACKAGE_SYSTEM} : pm.getPackagesForUid(uid); isSystemUid(uid) ? new String[] {PACKAGE_SYSTEM} : pm.getPackagesForUid(uid);
if (packages != null) { if (packages != null) {
final String[] packageLabels = new String[packages.length]; final String[] packageLabels = new String[packages.length];
System.arraycopy(packages, 0, packageLabels, 0, packages.length); System.arraycopy(packages, 0, packageLabels, 0, packages.length);
@@ -277,11 +304,15 @@ public class BatteryEntry {
final int userId = UserHandle.getUserId(uid); final int userId = UserHandle.getUserId(uid);
for (int i = 0; i < packageLabels.length; i++) { for (int i = 0; i < packageLabels.length; i++) {
try { try {
final ApplicationInfo ai = ipm.getApplicationInfo(packageLabels[i], final ApplicationInfo ai =
0 /* no flags */, userId); ipm.getApplicationInfo(packageLabels[i], 0 /* no flags */, userId);
if (ai == null) { if (ai == null) {
Log.d(TAG, "Retrieving null app info for package " Log.d(
+ packageLabels[i] + ", user " + userId); TAG,
"Retrieving null app info for package "
+ packageLabels[i]
+ ", user "
+ userId);
continue; continue;
} }
final CharSequence label = ai.loadLabel(pm); final CharSequence label = ai.loadLabel(pm);
@@ -294,8 +325,13 @@ public class BatteryEntry {
break; break;
} }
} catch (RemoteException e) { } catch (RemoteException e) {
Log.d(TAG, "Error while retrieving app info for package " Log.d(
+ packageLabels[i] + ", user " + userId, e); TAG,
"Error while retrieving app info for package "
+ packageLabels[i]
+ ", user "
+ userId,
e);
} }
} }
@@ -307,13 +343,17 @@ public class BatteryEntry {
try { try {
final PackageInfo pi = ipm.getPackageInfo(pkgName, 0, userId); final PackageInfo pi = ipm.getPackageInfo(pkgName, 0, userId);
if (pi == null) { if (pi == null) {
Log.d(TAG, "Retrieving null package info for package " Log.d(
+ pkgName + ", user " + userId); TAG,
"Retrieving null package info for package "
+ pkgName
+ ", user "
+ userId);
continue; continue;
} }
if (pi.sharedUserLabel != 0) { if (pi.sharedUserLabel != 0) {
final CharSequence nm = pm.getText(pkgName, final CharSequence nm =
pi.sharedUserLabel, pi.applicationInfo); pm.getText(pkgName, pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) { if (nm != null) {
name = nm.toString(); name = nm.toString();
if (pi.applicationInfo.icon != 0) { if (pi.applicationInfo.icon != 0) {
@@ -324,8 +364,13 @@ public class BatteryEntry {
} }
} }
} catch (RemoteException e) { } catch (RemoteException e) {
Log.d(TAG, "Error while retrieving package info for package " Log.d(
+ pkgName + ", user " + userId, e); TAG,
"Error while retrieving package info for package "
+ pkgName
+ ", user "
+ userId,
e);
} }
} }
} }
@@ -342,7 +387,7 @@ public class BatteryEntry {
utd.mPackageName = defaultPackageName; utd.mPackageName = defaultPackageName;
sUidCache.put(uidString, utd); sUidCache.put(uidString, utd);
return new NameAndIcon(name, defaultPackageName, icon, /*iconId=*/ 0); return new NameAndIcon(name, defaultPackageName, icon, /* iconId= */ 0);
} }
/** Returns a string that uniquely identifies this battery consumer. */ /** Returns a string that uniquely identifies this battery consumer. */
@@ -375,16 +420,13 @@ public class BatteryEntry {
} }
/** /**
* Returns the package name that should be used to represent the UID described * Returns the package name that should be used to represent the UID described by this entry.
* by this entry.
*/ */
public String getDefaultPackageName() { public String getDefaultPackageName() {
return mDefaultPackageName; return mDefaultPackageName;
} }
/** /** Returns the UID of the app described by this entry. */
* Returns the UID of the app described by this entry.
*/
public int getUid() { public int getUid() {
return mUid; return mUid;
} }
@@ -407,9 +449,7 @@ public class BatteryEntry {
} }
} }
/** /** Returns total amount of power (in milli-amp-hours) that is attributed to this entry. */
* Returns total amount of power (in milli-amp-hours) that is attributed to this entry.
*/
public double getConsumedPower() { public double getConsumedPower() {
return mConsumedPower; return mConsumedPower;
} }
@@ -462,25 +502,30 @@ public class BatteryEntry {
} }
/** /**
* Adds the consumed power of the supplied BatteryConsumer to this entry. Also * Adds the consumed power of the supplied BatteryConsumer to this entry. Also uses its package
* uses its package with highest drain, if necessary. * with highest drain, if necessary.
*/ */
public void add(BatteryConsumer batteryConsumer) { public void add(BatteryConsumer batteryConsumer) {
mConsumedPower += batteryConsumer.getConsumedPower(); mConsumedPower += batteryConsumer.getConsumedPower();
if (batteryConsumer instanceof UidBatteryConsumer) { if (batteryConsumer instanceof UidBatteryConsumer) {
UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer; UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
mTimeInForegroundMs += uidBatteryConsumer.getTimeInStateMs( mTimeInForegroundMs +=
UidBatteryConsumer.STATE_FOREGROUND); uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
mTimeInBackgroundMs += uidBatteryConsumer.getTimeInStateMs( mTimeInBackgroundMs +=
UidBatteryConsumer.STATE_BACKGROUND); uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
mConsumedPowerInForeground += safeGetConsumedPower( mConsumedPowerInForeground +=
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]); safeGetConsumedPower(
mConsumedPowerInForegroundService += safeGetConsumedPower( uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]);
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]); mConsumedPowerInForegroundService +=
mConsumedPowerInBackground += safeGetConsumedPower( safeGetConsumedPower(
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_BACKGROUND]); uidBatteryConsumer,
mConsumedPowerInCached += safeGetConsumedPower( BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]);
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_CACHED]); mConsumedPowerInBackground +=
safeGetConsumedPower(
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_BACKGROUND]);
mConsumedPowerInCached +=
safeGetConsumedPower(
uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_CACHED]);
if (mDefaultPackageName == null) { if (mDefaultPackageName == null) {
mDefaultPackageName = uidBatteryConsumer.getPackageWithHighestDrain(); mDefaultPackageName = uidBatteryConsumer.getPackageWithHighestDrain();
} }
@@ -488,8 +533,7 @@ public class BatteryEntry {
} }
/** Gets name and icon resource from UserBatteryConsumer userId. */ /** Gets name and icon resource from UserBatteryConsumer userId. */
public static NameAndIcon getNameAndIconFromUserId( public static NameAndIcon getNameAndIconFromUserId(Context context, final int userId) {
Context context, final int userId) {
UserManager um = context.getSystemService(UserManager.class); UserManager um = context.getSystemService(UserManager.class);
UserInfo info = um.getUserInfo(userId); UserInfo info = um.getUserInfo(userId);
@@ -499,27 +543,27 @@ public class BatteryEntry {
icon = Utils.getUserIcon(context, um, info); icon = Utils.getUserIcon(context, um, info);
name = Utils.getUserLabel(context, info); name = Utils.getUserLabel(context, info);
} else { } else {
name = context.getResources().getString( name =
R.string.running_process_item_removed_user_label); context.getResources()
.getString(R.string.running_process_item_removed_user_label);
} }
return new NameAndIcon(name, icon, 0 /* iconId */); return new NameAndIcon(name, icon, 0 /* iconId */);
} }
/** Gets name and icon resource from UidBatteryConsumer uid. */ /** Gets name and icon resource from UidBatteryConsumer uid. */
public static NameAndIcon getNameAndIconFromUid( public static NameAndIcon getNameAndIconFromUid(Context context, String name, final int uid) {
Context context, String name, final int uid) {
Drawable icon = context.getDrawable(R.drawable.ic_power_system); Drawable icon = context.getDrawable(R.drawable.ic_power_system);
if (uid == 0) { if (uid == 0) {
name = context.getResources() name =
.getString(com.android.settingslib.R.string.process_kernel_label); context.getResources()
.getString(com.android.settingslib.R.string.process_kernel_label);
} else if (uid == BatteryUtils.UID_REMOVED_APPS) { } else if (uid == BatteryUtils.UID_REMOVED_APPS) {
name = context.getResources().getString(R.string.process_removed_apps); name = context.getResources().getString(R.string.process_removed_apps);
} else if (uid == BatteryUtils.UID_TETHERING) { } else if (uid == BatteryUtils.UID_TETHERING) {
name = context.getResources().getString(R.string.process_network_tethering); name = context.getResources().getString(R.string.process_network_tethering);
} else if ("mediaserver".equals(name)) { } else if ("mediaserver".equals(name)) {
name = context.getResources().getString(R.string.process_mediaserver_label); name = context.getResources().getString(R.string.process_mediaserver_label);
} else if ("dex2oat".equals(name) || "dex2oat32".equals(name) } else if ("dex2oat".equals(name) || "dex2oat32".equals(name) || "dex2oat64".equals(name)) {
|| "dex2oat64".equals(name)) {
name = context.getResources().getString(R.string.process_dex2oat_label); name = context.getResources().getString(R.string.process_dex2oat_label);
} }
return new NameAndIcon(name, icon, 0 /* iconId */); return new NameAndIcon(name, icon, 0 /* iconId */);
@@ -531,12 +575,12 @@ public class BatteryEntry {
String name; String name;
int iconId; int iconId;
switch (powerComponentId) { switch (powerComponentId) {
// Please see go/battery-usage-system-component-map // Please see go/battery-usage-system-component-map
case BatteryConsumer.POWER_COMPONENT_SCREEN: // id: 0 case BatteryConsumer.POWER_COMPONENT_SCREEN: // id: 0
name = context.getResources().getString(R.string.power_screen); name = context.getResources().getString(R.string.power_screen);
iconId = R.drawable.ic_settings_display; iconId = R.drawable.ic_settings_display;
break; break;
case BatteryConsumer.POWER_COMPONENT_CPU: // id: 1 case BatteryConsumer.POWER_COMPONENT_CPU: // id: 1
name = context.getResources().getString(R.string.power_cpu); name = context.getResources().getString(R.string.power_cpu);
iconId = R.drawable.ic_settings_cpu; iconId = R.drawable.ic_settings_cpu;
break; break;
@@ -544,11 +588,11 @@ public class BatteryEntry {
name = context.getResources().getString(R.string.power_bluetooth); name = context.getResources().getString(R.string.power_bluetooth);
iconId = R.drawable.ic_settings_bluetooth; iconId = R.drawable.ic_settings_bluetooth;
break; break;
case BatteryConsumer.POWER_COMPONENT_CAMERA: // id: 3 case BatteryConsumer.POWER_COMPONENT_CAMERA: // id: 3
name = context.getResources().getString(R.string.power_camera); name = context.getResources().getString(R.string.power_camera);
iconId = R.drawable.ic_settings_camera; iconId = R.drawable.ic_settings_camera;
break; break;
case BatteryConsumer.POWER_COMPONENT_FLASHLIGHT: // id: 6 case BatteryConsumer.POWER_COMPONENT_FLASHLIGHT: // id: 6
name = context.getResources().getString(R.string.power_flashlight); name = context.getResources().getString(R.string.power_flashlight);
iconId = R.drawable.ic_settings_flashlight; iconId = R.drawable.ic_settings_flashlight;
break; break;
@@ -556,25 +600,30 @@ public class BatteryEntry {
name = context.getResources().getString(R.string.power_cell); name = context.getResources().getString(R.string.power_cell);
iconId = R.drawable.ic_settings_cellular; iconId = R.drawable.ic_settings_cellular;
break; break;
case BatteryConsumer.POWER_COMPONENT_GNSS: // id: 10 case BatteryConsumer.POWER_COMPONENT_GNSS: // id: 10
name = context.getResources().getString(R.string.power_gps); name = context.getResources().getString(R.string.power_gps);
iconId = R.drawable.ic_settings_gps; iconId = R.drawable.ic_settings_gps;
break; break;
case BatteryConsumer.POWER_COMPONENT_WIFI: // id: 11 case BatteryConsumer.POWER_COMPONENT_WIFI: // id: 11
name = context.getResources().getString(R.string.power_wifi); name = context.getResources().getString(R.string.power_wifi);
iconId = R.drawable.ic_settings_wireless_no_theme; iconId = R.drawable.ic_settings_wireless_no_theme;
break; break;
case BatteryConsumer.POWER_COMPONENT_PHONE: // id: 14 case BatteryConsumer.POWER_COMPONENT_PHONE: // id: 14
name = context.getResources().getString(R.string.power_phone); name = context.getResources().getString(R.string.power_phone);
iconId = R.drawable.ic_settings_voice_calls; iconId = R.drawable.ic_settings_voice_calls;
break; break;
case BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY: // id :15 case BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY: // id :15
name = context.getResources().getString(R.string.ambient_display_screen_title); name = context.getResources().getString(R.string.ambient_display_screen_title);
iconId = R.drawable.ic_settings_aod; iconId = R.drawable.ic_settings_aod;
break; break;
default: default:
Log.w(TAG, "unknown attribute:" + DebugUtils.constantToString( Log.w(
BatteryConsumer.class, "POWER_COMPONENT_", powerComponentId)); TAG,
"unknown attribute:"
+ DebugUtils.constantToString(
BatteryConsumer.class,
"POWER_COMPONENT_",
powerComponentId));
name = null; name = null;
iconId = R.drawable.ic_power_system; iconId = R.drawable.ic_power_system;
break; break;

View File

@@ -29,6 +29,7 @@ public class BatteryHistEntry {
/** Keys for accessing {@link ContentValues} or {@link Cursor}. */ /** Keys for accessing {@link ContentValues} or {@link Cursor}. */
public static final String KEY_UID = "uid"; public static final String KEY_UID = "uid";
public static final String KEY_USER_ID = "userId"; public static final String KEY_USER_ID = "userId";
public static final String KEY_PACKAGE_NAME = "packageName"; public static final String KEY_PACKAGE_NAME = "packageName";
public static final String KEY_TIMESTAMP = "timestamp"; public static final String KEY_TIMESTAMP = "timestamp";
@@ -57,10 +58,8 @@ public class BatteryHistEntry {
public final double mPercentOfTotal; public final double mPercentOfTotal;
public final long mForegroundUsageTimeInMs; public final long mForegroundUsageTimeInMs;
public final long mBackgroundUsageTimeInMs; public final long mBackgroundUsageTimeInMs;
@BatteryConsumer.PowerComponent @BatteryConsumer.PowerComponent public final int mDrainType;
public final int mDrainType; @ConvertUtils.ConsumerType public final int mConsumerType;
@ConvertUtils.ConsumerType
public final int mConsumerType;
// Records the battery intent relative information. // Records the battery intent relative information.
public final int mBatteryLevel; public final int mBatteryLevel;
public final int mBatteryStatus; public final int mBatteryStatus;
@@ -190,25 +189,45 @@ public class BatteryHistEntry {
@Override @Override
public String toString() { public String toString() {
final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(mTimestamp); final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(mTimestamp);
final StringBuilder builder = new StringBuilder() final StringBuilder builder =
.append("\nBatteryHistEntry{") new StringBuilder()
.append(String.format("\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b", .append("\nBatteryHistEntry{")
mPackageName, mAppLabel, mUid, mUserId, mIsHidden)) .append(
.append(String.format("\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d", String.format(
recordAtDateTime, mZoneId, Duration.ofMillis(mBootTimestamp).getSeconds())) "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
.append(String.format("\n\tusage=%f|total=%f|consume=%f", mPackageName, mAppLabel, mUid, mUserId, mIsHidden))
mPercentOfTotal, mTotalPower, mConsumePower)) .append(
.append(String.format("\n\tforeground=%f|foregroundService=%f", String.format(
mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower)) "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
.append(String.format("\n\tbackground=%f|cached=%f", recordAtDateTime,
mBackgroundUsageConsumePower, mCachedUsageConsumePower)) mZoneId,
.append(String.format("\n\telapsedTime=%d|%d", Duration.ofMillis(mBootTimestamp).getSeconds()))
Duration.ofMillis(mForegroundUsageTimeInMs).getSeconds(), .append(
Duration.ofMillis(mBackgroundUsageTimeInMs).getSeconds())) String.format(
.append(String.format("\n\tdrainType=%d|consumerType=%d", "\n\tusage=%f|total=%f|consume=%f",
mDrainType, mConsumerType)) mPercentOfTotal, mTotalPower, mConsumePower))
.append(String.format("\n\tbattery=%d|status=%d|health=%d\n}", .append(
mBatteryLevel, mBatteryStatus, mBatteryHealth)); String.format(
"\n\tforeground=%f|foregroundService=%f",
mForegroundUsageConsumePower,
mForegroundServiceUsageConsumePower))
.append(
String.format(
"\n\tbackground=%f|cached=%f",
mBackgroundUsageConsumePower, mCachedUsageConsumePower))
.append(
String.format(
"\n\telapsedTime=%d|%d",
Duration.ofMillis(mForegroundUsageTimeInMs).getSeconds(),
Duration.ofMillis(mBackgroundUsageTimeInMs).getSeconds()))
.append(
String.format(
"\n\tdrainType=%d|consumerType=%d",
mDrainType, mConsumerType))
.append(
String.format(
"\n\tbattery=%d|status=%d|health=%d\n}",
mBatteryLevel, mBatteryStatus, mBatteryHealth));
return builder.toString(); return builder.toString();
} }
@@ -270,65 +289,81 @@ public class BatteryHistEntry {
double ratio, double ratio,
BatteryHistEntry lowerHistEntry, BatteryHistEntry lowerHistEntry,
BatteryHistEntry upperHistEntry) { BatteryHistEntry upperHistEntry) {
final double totalPower = interpolate( final double totalPower =
lowerHistEntry == null ? 0 : lowerHistEntry.mTotalPower, interpolate(
upperHistEntry.mTotalPower, lowerHistEntry == null ? 0 : lowerHistEntry.mTotalPower,
ratio); upperHistEntry.mTotalPower,
final double consumePower = interpolate( ratio);
lowerHistEntry == null ? 0 : lowerHistEntry.mConsumePower, final double consumePower =
upperHistEntry.mConsumePower, interpolate(
ratio); lowerHistEntry == null ? 0 : lowerHistEntry.mConsumePower,
final double foregroundUsageConsumePower = interpolate( upperHistEntry.mConsumePower,
lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageConsumePower, ratio);
upperHistEntry.mForegroundUsageConsumePower, final double foregroundUsageConsumePower =
ratio); interpolate(
final double foregroundServiceUsageConsumePower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageConsumePower,
lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundServiceUsageConsumePower, upperHistEntry.mForegroundUsageConsumePower,
upperHistEntry.mForegroundServiceUsageConsumePower, ratio);
ratio); final double foregroundServiceUsageConsumePower =
final double backgroundUsageConsumePower = interpolate( interpolate(
lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageConsumePower, lowerHistEntry == null
upperHistEntry.mBackgroundUsageConsumePower, ? 0
ratio); : lowerHistEntry.mForegroundServiceUsageConsumePower,
final double cachedUsageConsumePower = interpolate( upperHistEntry.mForegroundServiceUsageConsumePower,
lowerHistEntry == null ? 0 : lowerHistEntry.mCachedUsageConsumePower, ratio);
upperHistEntry.mCachedUsageConsumePower, final double backgroundUsageConsumePower =
ratio); interpolate(
final double foregroundUsageTimeInMs = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageConsumePower,
(double) (lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageTimeInMs), upperHistEntry.mBackgroundUsageConsumePower,
(double) upperHistEntry.mForegroundUsageTimeInMs, ratio);
ratio); final double cachedUsageConsumePower =
final double backgroundUsageTimeInMs = interpolate( interpolate(
(double) (lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageTimeInMs), lowerHistEntry == null ? 0 : lowerHistEntry.mCachedUsageConsumePower,
(double) upperHistEntry.mBackgroundUsageTimeInMs, upperHistEntry.mCachedUsageConsumePower,
ratio); ratio);
final double foregroundUsageTimeInMs =
interpolate(
(double)
(lowerHistEntry == null
? 0
: lowerHistEntry.mForegroundUsageTimeInMs),
(double) upperHistEntry.mForegroundUsageTimeInMs,
ratio);
final double backgroundUsageTimeInMs =
interpolate(
(double)
(lowerHistEntry == null
? 0
: lowerHistEntry.mBackgroundUsageTimeInMs),
(double) upperHistEntry.mBackgroundUsageTimeInMs,
ratio);
// Checks whether there is any abnormal cases! // Checks whether there is any abnormal cases!
if (upperHistEntry.mConsumePower < consumePower if (upperHistEntry.mConsumePower < consumePower
|| upperHistEntry.mForegroundUsageConsumePower < foregroundUsageConsumePower || upperHistEntry.mForegroundUsageConsumePower < foregroundUsageConsumePower
|| upperHistEntry.mForegroundServiceUsageConsumePower || upperHistEntry.mForegroundServiceUsageConsumePower
< foregroundServiceUsageConsumePower < foregroundServiceUsageConsumePower
|| upperHistEntry.mBackgroundUsageConsumePower < backgroundUsageConsumePower || upperHistEntry.mBackgroundUsageConsumePower < backgroundUsageConsumePower
|| upperHistEntry.mCachedUsageConsumePower < cachedUsageConsumePower || upperHistEntry.mCachedUsageConsumePower < cachedUsageConsumePower
|| upperHistEntry.mForegroundUsageTimeInMs < foregroundUsageTimeInMs || upperHistEntry.mForegroundUsageTimeInMs < foregroundUsageTimeInMs
|| upperHistEntry.mBackgroundUsageTimeInMs < backgroundUsageTimeInMs) { || upperHistEntry.mBackgroundUsageTimeInMs < backgroundUsageTimeInMs) {
if (DEBUG) { if (DEBUG) {
Log.w(TAG, String.format( Log.w(
"abnormal interpolation:\nupper:%s\nlower:%s", TAG,
upperHistEntry, lowerHistEntry)); String.format(
"abnormal interpolation:\nupper:%s\nlower:%s",
upperHistEntry, lowerHistEntry));
} }
} }
final double batteryLevel = final double batteryLevel =
lowerHistEntry == null lowerHistEntry == null
? upperHistEntry.mBatteryLevel ? upperHistEntry.mBatteryLevel
: interpolate( : interpolate(
lowerHistEntry.mBatteryLevel, lowerHistEntry.mBatteryLevel, upperHistEntry.mBatteryLevel, ratio);
upperHistEntry.mBatteryLevel,
ratio);
return new BatteryHistEntry( return new BatteryHistEntry(
upperHistEntry, upperHistEntry,
/*bootTimestamp=*/ upperHistEntry.mBootTimestamp /* bootTimestamp= */ upperHistEntry.mBootTimestamp
- (upperTimestamp - slotTimestamp), - (upperTimestamp - slotTimestamp),
/*timestamp=*/ slotTimestamp, /* timestamp= */ slotTimestamp,
totalPower, totalPower,
consumePower, consumePower,
foregroundUsageConsumePower, foregroundUsageConsumePower,

View File

@@ -26,9 +26,7 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
/** /** Custom preference for displaying the battery level as chart graph. */
* Custom preference for displaying the battery level as chart graph.
*/
public class BatteryHistoryPreference extends Preference { public class BatteryHistoryPreference extends Preference {
private static final String TAG = "BatteryHistoryPreference"; private static final String TAG = "BatteryHistoryPreference";

View File

@@ -47,13 +47,14 @@ public final class BatteryLevelData {
private final List<Integer> mLevels; private final List<Integer> mLevels;
public PeriodBatteryLevelData( public PeriodBatteryLevelData(
@NonNull Map<Long, Integer> batteryLevelMap, @NonNull Map<Long, Integer> batteryLevelMap, @NonNull List<Long> timestamps) {
@NonNull List<Long> timestamps) {
mTimestamps = timestamps; mTimestamps = timestamps;
mLevels = new ArrayList<>(timestamps.size()); mLevels = new ArrayList<>(timestamps.size());
for (Long timestamp : timestamps) { for (Long timestamp : timestamps) {
mLevels.add(batteryLevelMap.containsKey(timestamp) mLevels.add(
? batteryLevelMap.get(timestamp) : BATTERY_LEVEL_UNKNOWN); batteryLevelMap.containsKey(timestamp)
? batteryLevelMap.get(timestamp)
: BATTERY_LEVEL_UNKNOWN);
} }
} }
@@ -67,8 +68,11 @@ public final class BatteryLevelData {
@Override @Override
public String toString() { public String toString() {
return String.format(Locale.ENGLISH, "timestamps: %s; levels: %s", return String.format(
Objects.toString(mTimestamps), Objects.toString(mLevels)); Locale.ENGLISH,
"timestamps: %s; levels: %s",
Objects.toString(mTimestamps),
Objects.toString(mLevels));
} }
private int getIndexByTimestamps(long startTimestamp, long endTimestamp) { private int getIndexByTimestamps(long startTimestamp, long endTimestamp) {
@@ -83,14 +87,15 @@ public final class BatteryLevelData {
} }
/** /**
* There could be 2 cases for the daily battery levels: * There could be 2 cases for the daily battery levels: <br>
* 1) length is 2: The usage data is within 1 day. Only contains start and end data, such as * 1) length is 2: The usage data is within 1 day. Only contains start and end data, such as
* data of 2022-01-01 06:00 and 2022-01-01 16:00. * data of 2022-01-01 06:00 and 2022-01-01 16:00. <br>
* 2) length > 2: The usage data is more than 1 days. The data should be the start, end and 0am * 2) length > 2: The usage data is more than 1 days. The data should be the start, end and 0am
* data of every day between the start and end, such as data of 2022-01-01 06:00, * data of every day between the start and end, such as data of 2022-01-01 06:00, 2022-01-02
* 2022-01-02 00:00, 2022-01-03 00:00 and 2022-01-03 08:00. * 00:00, 2022-01-03 00:00 and 2022-01-03 08:00.
*/ */
private final PeriodBatteryLevelData mDailyBatteryLevels; private final PeriodBatteryLevelData mDailyBatteryLevels;
// The size of hourly data must be the size of daily data - 1. // The size of hourly data must be the size of daily data - 1.
private final List<PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay; private final List<PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
@@ -118,8 +123,9 @@ public final class BatteryLevelData {
final int hourlyHighlightIndex = final int hourlyHighlightIndex =
(dailyHighlightIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID) (dailyHighlightIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID)
? BatteryChartViewModel.SELECTED_INDEX_INVALID ? BatteryChartViewModel.SELECTED_INDEX_INVALID
: mHourlyBatteryLevelsPerDay.get(dailyHighlightIndex) : mHourlyBatteryLevelsPerDay
.getIndexByTimestamps(startTimestamp, endTimestamp); .get(dailyHighlightIndex)
.getIndexByTimestamps(startTimestamp, endTimestamp);
return Pair.create(dailyHighlightIndex, hourlyHighlightIndex); return Pair.create(dailyHighlightIndex, hourlyHighlightIndex);
} }
@@ -133,14 +139,16 @@ public final class BatteryLevelData {
@Override @Override
public String toString() { public String toString() {
return String.format(Locale.ENGLISH, return String.format(
Locale.ENGLISH,
"dailyBatteryLevels: %s; hourlyBatteryLevelsPerDay: %s", "dailyBatteryLevels: %s; hourlyBatteryLevelsPerDay: %s",
Objects.toString(mDailyBatteryLevels), Objects.toString(mDailyBatteryLevels),
Objects.toString(mHourlyBatteryLevelsPerDay)); Objects.toString(mHourlyBatteryLevelsPerDay));
} }
@Nullable @Nullable
static BatteryLevelData combine(@Nullable BatteryLevelData existingBatteryLevelData, static BatteryLevelData combine(
@Nullable BatteryLevelData existingBatteryLevelData,
List<BatteryEvent> batteryLevelRecordEvents) { List<BatteryEvent> batteryLevelRecordEvents) {
final Map<Long, Integer> batteryLevelMap = new ArrayMap<>(batteryLevelRecordEvents.size()); final Map<Long, Integer> batteryLevelMap = new ArrayMap<>(batteryLevelRecordEvents.size());
for (BatteryEvent event : batteryLevelRecordEvents) { for (BatteryEvent event : batteryLevelRecordEvents) {
@@ -152,7 +160,8 @@ public final class BatteryLevelData {
for (int dayIndex = 0; dayIndex < multiDaysData.size(); dayIndex++) { for (int dayIndex = 0; dayIndex < multiDaysData.size(); dayIndex++) {
PeriodBatteryLevelData oneDayData = multiDaysData.get(dayIndex); PeriodBatteryLevelData oneDayData = multiDaysData.get(dayIndex);
for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size(); hourIndex++) { for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size(); hourIndex++) {
batteryLevelMap.put(oneDayData.getTimestamps().get(hourIndex), batteryLevelMap.put(
oneDayData.getTimestamps().get(hourIndex),
oneDayData.getLevels().get(hourIndex)); oneDayData.getLevels().get(hourIndex));
} }
} }
@@ -163,11 +172,10 @@ public final class BatteryLevelData {
/** /**
* Computes expected daily timestamp slots. * Computes expected daily timestamp slots.
* *
* The valid result should be composed of 3 parts: * <p>The valid result should be composed of 3 parts: <br>
* 1) start timestamp * 1) start timestamp <br>
* 2) every 00:00 timestamp (default timezone) between the start and end * 2) every 00:00 timestamp (default timezone) between the start and end <br>
* 3) end timestamp * 3) end timestamp Otherwise, returns an empty list.
* Otherwise, returns an empty list.
*/ */
@VisibleForTesting @VisibleForTesting
static List<Long> getDailyTimestamps(final List<Long> timestampList) { static List<Long> getDailyTimestamps(final List<Long> timestampList) {
@@ -176,7 +184,8 @@ public final class BatteryLevelData {
final List<Long> dailyTimestampList = new ArrayList<>(); final List<Long> dailyTimestampList = new ArrayList<>();
final long startTimestamp = timestampList.get(0); final long startTimestamp = timestampList.get(0);
final long endTimestamp = timestampList.get(timestampList.size() - 1); final long endTimestamp = timestampList.get(timestampList.size() - 1);
for (long timestamp = startTimestamp; timestamp < endTimestamp; for (long timestamp = startTimestamp;
timestamp < endTimestamp;
timestamp = TimestampUtils.getNextDayTimestamp(timestamp)) { timestamp = TimestampUtils.getNextDayTimestamp(timestamp)) {
dailyTimestampList.add(timestamp); dailyTimestampList.add(timestamp);
} }
@@ -193,7 +202,8 @@ public final class BatteryLevelData {
hourlyTimestampsPerDay.add(startTime); hourlyTimestampsPerDay.add(startTime);
for (long timestamp = TimestampUtils.getNextEvenHourTimestamp(startTime); for (long timestamp = TimestampUtils.getNextEvenHourTimestamp(startTime);
timestamp < endTime; timestamp += TIME_SLOT) { timestamp < endTime;
timestamp += TIME_SLOT) {
hourlyTimestampsPerDay.add(timestamp); hourlyTimestampsPerDay.add(timestamp);
} }
hourlyTimestampsPerDay.add(endTime); hourlyTimestampsPerDay.add(endTime);
@@ -203,4 +213,3 @@ public final class BatteryLevelData {
return hourlyTimestamps; return hourlyTimestamps;
} }
} }

View File

@@ -34,9 +34,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
/** /** A preference for displaying the battery tips card view. */
* A preference for displaying the battery tips card view.
*/
public class BatteryTipsCardPreference extends Preference implements View.OnClickListener { public class BatteryTipsCardPreference extends Preference implements View.OnClickListener {
private static final String TAG = "BatteryTipsCardPreference"; private static final String TAG = "BatteryTipsCardPreference";
@@ -55,10 +53,8 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
private int mIconResourceId = 0; private int mIconResourceId = 0;
private int mMainButtonStrokeColorResourceId = 0; private int mMainButtonStrokeColorResourceId = 0;
@VisibleForTesting @VisibleForTesting CharSequence mMainButtonLabel;
CharSequence mMainButtonLabel; @VisibleForTesting CharSequence mDismissButtonLabel;
@VisibleForTesting
CharSequence mDismissButtonLabel;
public BatteryTipsCardPreference(Context context, AttributeSet attrs) { public BatteryTipsCardPreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@@ -76,9 +72,7 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
mOnRejectListener = listener; mOnRejectListener = listener;
} }
/** /** Sets the icon in tips card. */
* Sets the icon in tips card.
*/
public void setIconResourceId(int resourceId) { public void setIconResourceId(int resourceId) {
if (mIconResourceId != resourceId) { if (mIconResourceId != resourceId) {
mIconResourceId = resourceId; mIconResourceId = resourceId;
@@ -86,9 +80,7 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
} }
} }
/** /** Sets the stroke color of main button in tips card. */
* Sets the stroke color of main button in tips card.
*/
public void setMainButtonStrokeColorResourceId(int resourceId) { public void setMainButtonStrokeColorResourceId(int resourceId) {
if (mMainButtonStrokeColorResourceId != resourceId) { if (mMainButtonStrokeColorResourceId != resourceId) {
mMainButtonStrokeColorResourceId = resourceId; mMainButtonStrokeColorResourceId = resourceId;
@@ -96,9 +88,7 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
} }
} }
/** /** Sets the label of main button in tips card. */
* Sets the label of main button in tips card.
*/
public void setMainButtonLabel(CharSequence label) { public void setMainButtonLabel(CharSequence label) {
if (!TextUtils.equals(mMainButtonLabel, label)) { if (!TextUtils.equals(mMainButtonLabel, label)) {
mMainButtonLabel = label; mMainButtonLabel = label;
@@ -106,9 +96,7 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
} }
} }
/** /** Sets the label of dismiss button in tips card. */
* Sets the label of dismiss button in tips card.
*/
public void setDismissButtonLabel(CharSequence label) { public void setDismissButtonLabel(CharSequence label) {
if (!TextUtils.equals(mDismissButtonLabel, label)) { if (!TextUtils.equals(mDismissButtonLabel, label)) {
mDismissButtonLabel = label; mDismissButtonLabel = label;

View File

@@ -51,12 +51,9 @@ public class BatteryTipsController extends BasePreferenceController {
private OnAnomalyConfirmListener mOnAnomalyConfirmListener; private OnAnomalyConfirmListener mOnAnomalyConfirmListener;
private OnAnomalyRejectListener mOnAnomalyRejectListener; private OnAnomalyRejectListener mOnAnomalyRejectListener;
@VisibleForTesting @VisibleForTesting BatteryTipsCardPreference mCardPreference;
BatteryTipsCardPreference mCardPreference; @VisibleForTesting AnomalyEventWrapper mAnomalyEventWrapper = null;
@VisibleForTesting @VisibleForTesting Boolean mIsAcceptable = false;
AnomalyEventWrapper mAnomalyEventWrapper = null;
@VisibleForTesting
Boolean mIsAcceptable = false;
public BatteryTipsController(Context context) { public BatteryTipsController(Context context) {
super(context, ROOT_PREFERENCE_KEY); super(context, ROOT_PREFERENCE_KEY);
@@ -94,7 +91,8 @@ public class BatteryTipsController extends BasePreferenceController {
} }
mCardPreference.setVisible(false); mCardPreference.setVisible(false);
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, mContext,
SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT,
mAnomalyEventWrapper.getEventId()); mAnomalyEventWrapper.getEventId());
} }
@@ -117,28 +115,31 @@ public class BatteryTipsController extends BasePreferenceController {
} }
// Set battery tips card listener // Set battery tips card listener
mCardPreference.setOnConfirmListener(() -> { mCardPreference.setOnConfirmListener(
mCardPreference.setVisible(false); () -> {
if (mOnAnomalyConfirmListener != null) { mCardPreference.setVisible(false);
mOnAnomalyConfirmListener.onAnomalyConfirm(); if (mOnAnomalyConfirmListener != null) {
} else if (mAnomalyEventWrapper.launchSubSetting()) { mOnAnomalyConfirmListener.onAnomalyConfirm();
mMetricsFeatureProvider.action( } else if (mAnomalyEventWrapper.launchSubSetting()) {
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId); mMetricsFeatureProvider.action(
} mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId);
}); }
mCardPreference.setOnRejectListener(() -> { });
mCardPreference.setVisible(false); mCardPreference.setOnRejectListener(
if (mOnAnomalyRejectListener != null) { () -> {
mOnAnomalyRejectListener.onAnomalyReject(); mCardPreference.setVisible(false);
} if (mOnAnomalyRejectListener != null) {
// For anomaly events with same record key, dismissed until next time full charged. mOnAnomalyRejectListener.onAnomalyReject();
final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey(); }
if (!TextUtils.isEmpty(dismissRecordKey)) { // For anomaly events with same record key, dismissed until next time full
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey); // charged.
} final String dismissRecordKey = mAnomalyEventWrapper.getDismissRecordKey();
mMetricsFeatureProvider.action( if (!TextUtils.isEmpty(dismissRecordKey)) {
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, eventId); DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
}); }
mMetricsFeatureProvider.action(
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, eventId);
});
mCardPreference.setVisible(true); mCardPreference.setVisible(true);
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(

View File

@@ -58,7 +58,7 @@ import java.util.Set;
/** Controller for battery usage breakdown preference group. */ /** Controller for battery usage breakdown preference group. */
public class BatteryUsageBreakdownController extends BasePreferenceController public class BatteryUsageBreakdownController extends BasePreferenceController
implements LifecycleObserver, OnResume, OnDestroy { implements LifecycleObserver, OnResume, OnDestroy {
private static final String TAG = "BatteryUsageBreakdownController"; private static final String TAG = "BatteryUsageBreakdownController";
private static final String ROOT_PREFERENCE_KEY = "battery_usage_breakdown"; private static final String ROOT_PREFERENCE_KEY = "battery_usage_breakdown";
private static final String FOOTER_PREFERENCE_KEY = "battery_usage_footer"; private static final String FOOTER_PREFERENCE_KEY = "battery_usage_footer";
@@ -74,45 +74,33 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
private final MetricsFeatureProvider mMetricsFeatureProvider; private final MetricsFeatureProvider mMetricsFeatureProvider;
private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Handler mHandler = new Handler(Looper.getMainLooper());
@VisibleForTesting @VisibleForTesting final Map<String, Preference> mPreferenceCache = new ArrayMap<>();
final Map<String, Preference> mPreferenceCache = new ArrayMap<>();
private int mSpinnerPosition; private int mSpinnerPosition;
private String mSlotTimestamp; private String mSlotTimestamp;
@VisibleForTesting @VisibleForTesting Context mPrefContext;
Context mPrefContext; @VisibleForTesting PreferenceCategory mRootPreference;
@VisibleForTesting @VisibleForTesting SpinnerPreference mSpinnerPreference;
PreferenceCategory mRootPreference; @VisibleForTesting PreferenceGroup mAppListPreferenceGroup;
@VisibleForTesting @VisibleForTesting FooterPreference mFooterPreference;
SpinnerPreference mSpinnerPreference; @VisibleForTesting BatteryDiffData mBatteryDiffData;
@VisibleForTesting @VisibleForTesting String mPercentLessThanThresholdText;
PreferenceGroup mAppListPreferenceGroup; @VisibleForTesting boolean mIsHighlightSlot;
@VisibleForTesting @VisibleForTesting String mAnomalyEventId;
FooterPreference mFooterPreference; @VisibleForTesting String mAnomalyEntryKey;
@VisibleForTesting @VisibleForTesting String mAnomalyHintString;
BatteryDiffData mBatteryDiffData; @VisibleForTesting String mAnomalyHintPrefKey;
@VisibleForTesting
String mPercentLessThanThresholdText;
@VisibleForTesting
boolean mIsHighlightSlot;
@VisibleForTesting
String mAnomalyEventId;
@VisibleForTesting
String mAnomalyEntryKey;
@VisibleForTesting
String mAnomalyHintString;
@VisibleForTesting
String mAnomalyHintPrefKey;
public BatteryUsageBreakdownController( public BatteryUsageBreakdownController(
Context context, Lifecycle lifecycle, SettingsActivity activity, Context context,
Lifecycle lifecycle,
SettingsActivity activity,
InstrumentedPreferenceFragment fragment) { InstrumentedPreferenceFragment fragment) {
super(context, ROOT_PREFERENCE_KEY); super(context, ROOT_PREFERENCE_KEY);
mActivity = activity; mActivity = activity;
mFragment = fragment; mFragment = fragment;
mMetricsFeatureProvider = mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
if (lifecycle != null) { if (lifecycle != null) {
lifecycle.addObserver(this); lifecycle.addObserver(this);
} }
@@ -133,7 +121,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
@Override @Override
public void onDestroy() { public void onDestroy() {
mHandler.removeCallbacksAndMessages(/*token=*/ null); mHandler.removeCallbacksAndMessages(/* token= */ null);
mPreferenceCache.clear(); mPreferenceCache.clear();
mAppListPreferenceGroup.removeAll(); mAppListPreferenceGroup.removeAll();
} }
@@ -149,14 +137,17 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
} }
private boolean isAnomalyBatteryDiffEntry(BatteryDiffEntry entry) { private boolean isAnomalyBatteryDiffEntry(BatteryDiffEntry entry) {
return mIsHighlightSlot && mAnomalyEntryKey != null return mIsHighlightSlot
&& mAnomalyEntryKey != null
&& mAnomalyEntryKey.equals(entry.getKey()); && mAnomalyEntryKey.equals(entry.getKey());
} }
private String getActionKey(BatteryDiffEntry entry) { private String getActionKey(BatteryDiffEntry entry) {
final String actionKey = TextUtils.isEmpty(entry.getPackageName()) final String actionKey =
? PACKAGE_NAME_NONE : entry.getPackageName(); TextUtils.isEmpty(entry.getPackageName())
return !isAnomalyBatteryDiffEntry(entry) ? actionKey : actionKey + "|" + mAnomalyEventId; ? PACKAGE_NAME_NONE
: entry.getPackageName();
return !isAnomalyBatteryDiffEntry(entry) ? actionKey : actionKey + "|" + mAnomalyEventId;
} }
@Override @Override
@@ -174,15 +165,24 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE, /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
getActionKey(diffEntry), getActionKey(diffEntry),
(int) Math.round(diffEntry.getPercentage())); (int) Math.round(diffEntry.getPercentage()));
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s", Log.d(
diffEntry.getAppLabel(), diffEntry.getKey(), diffEntry.getPackageName())); TAG,
final String anomalyHintPrefKey = isAnomalyBatteryDiffEntry(diffEntry) String.format(
? mAnomalyHintPrefKey : null; "handleClick() label=%s key=%s package=%s",
final String anomalyHintText = isAnomalyBatteryDiffEntry(diffEntry) diffEntry.getAppLabel(), diffEntry.getKey(), diffEntry.getPackageName()));
? mAnomalyHintString : null; final String anomalyHintPrefKey =
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment.getMetricsCategory(), isAnomalyBatteryDiffEntry(diffEntry) ? mAnomalyHintPrefKey : null;
diffEntry, powerPref.getPercentage(), mSlotTimestamp, final String anomalyHintText =
/*showTimeInformation=*/ true, anomalyHintPrefKey, anomalyHintText); isAnomalyBatteryDiffEntry(diffEntry) ? mAnomalyHintString : null;
AdvancedPowerUsageDetail.startBatteryDetailPage(
mActivity,
mFragment.getMetricsCategory(),
diffEntry,
powerPref.getPercentage(),
mSlotTimestamp,
/* showTimeInformation= */ true,
anomalyHintPrefKey,
anomalyHintText);
return true; return true;
} }
@@ -194,15 +194,16 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
mSpinnerPreference = screen.findPreference(SPINNER_PREFERENCE_KEY); mSpinnerPreference = screen.findPreference(SPINNER_PREFERENCE_KEY);
mAppListPreferenceGroup = screen.findPreference(APP_LIST_PREFERENCE_KEY); mAppListPreferenceGroup = screen.findPreference(APP_LIST_PREFERENCE_KEY);
mFooterPreference = screen.findPreference(FOOTER_PREFERENCE_KEY); mFooterPreference = screen.findPreference(FOOTER_PREFERENCE_KEY);
mPercentLessThanThresholdText = mPrefContext.getString( mPercentLessThanThresholdText =
R.string.battery_usage_less_than_percent, mPrefContext.getString(
Utils.formatPercentage(BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD, false)); R.string.battery_usage_less_than_percent,
Utils.formatPercentage(BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD, false));
mAppListPreferenceGroup.setOrderingAsAdded(false); mAppListPreferenceGroup.setOrderingAsAdded(false);
mSpinnerPreference.initializeSpinner( mSpinnerPreference.initializeSpinner(
new String[]{ new String[] {
mPrefContext.getString(R.string.battery_usage_spinner_view_by_apps), mPrefContext.getString(R.string.battery_usage_spinner_view_by_apps),
mPrefContext.getString(R.string.battery_usage_spinner_view_by_systems) mPrefContext.getString(R.string.battery_usage_spinner_view_by_systems)
}, },
new AdapterView.OnItemSelectedListener() { new AdapterView.OnItemSelectedListener() {
@Override @Override
@@ -210,35 +211,38 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
AdapterView<?> parent, View view, int position, long id) { AdapterView<?> parent, View view, int position, long id) {
if (mSpinnerPosition != position) { if (mSpinnerPosition != position) {
mSpinnerPosition = position; mSpinnerPosition = position;
mHandler.post(() -> { mHandler.post(
removeAndCacheAllUnusedPreferences(); () -> {
addAllPreferences(); removeAndCacheAllUnusedPreferences();
mMetricsFeatureProvider.action( addAllPreferences();
mPrefContext, mMetricsFeatureProvider.action(
SettingsEnums.ACTION_BATTERY_USAGE_SPINNER, mPrefContext,
mSpinnerPosition); SettingsEnums.ACTION_BATTERY_USAGE_SPINNER,
}); mSpinnerPosition);
});
} }
} }
@Override @Override
public void onNothingSelected(AdapterView<?> parent) { public void onNothingSelected(AdapterView<?> parent) {}
}
}); });
} }
/** /**
* Updates UI when the battery usage is updated. * Updates UI when the battery usage is updated.
* @param slotUsageData The battery usage diff data for the selected slot. This is used in *
* the app list. * @param slotUsageData The battery usage diff data for the selected slot. This is used in the
* app list.
* @param slotTimestamp The selected slot timestamp information. This is used in the battery * @param slotTimestamp The selected slot timestamp information. This is used in the battery
* usage breakdown category. * usage breakdown category.
* @param isAllUsageDataEmpty Whether all the battery usage data is null or empty. This is * @param isAllUsageDataEmpty Whether all the battery usage data is null or empty. This is used
* used when showing the footer. * when showing the footer.
*/ */
void handleBatteryUsageUpdated( void handleBatteryUsageUpdated(
BatteryDiffData slotUsageData, String slotTimestamp, BatteryDiffData slotUsageData,
boolean isAllUsageDataEmpty, boolean isHighlightSlot, String slotTimestamp,
boolean isAllUsageDataEmpty,
boolean isHighlightSlot,
Optional<AnomalyEventWrapper> optionalAnomalyEventWrapper) { Optional<AnomalyEventWrapper> optionalAnomalyEventWrapper) {
mBatteryDiffData = slotUsageData; mBatteryDiffData = slotUsageData;
mSlotTimestamp = slotTimestamp; mSlotTimestamp = slotTimestamp;
@@ -247,14 +251,15 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
if (optionalAnomalyEventWrapper != null) { if (optionalAnomalyEventWrapper != null) {
final AnomalyEventWrapper anomalyEventWrapper = final AnomalyEventWrapper anomalyEventWrapper =
optionalAnomalyEventWrapper.orElse(null); optionalAnomalyEventWrapper.orElse(null);
mAnomalyEventId = anomalyEventWrapper != null mAnomalyEventId = anomalyEventWrapper != null ? anomalyEventWrapper.getEventId() : null;
? anomalyEventWrapper.getEventId() : null; mAnomalyEntryKey =
mAnomalyEntryKey = anomalyEventWrapper != null anomalyEventWrapper != null ? anomalyEventWrapper.getAnomalyEntryKey() : null;
? anomalyEventWrapper.getAnomalyEntryKey() : null; mAnomalyHintString =
mAnomalyHintString = anomalyEventWrapper != null anomalyEventWrapper != null ? anomalyEventWrapper.getAnomalyHintString() : null;
? anomalyEventWrapper.getAnomalyHintString() : null; mAnomalyHintPrefKey =
mAnomalyHintPrefKey = anomalyEventWrapper != null anomalyEventWrapper != null
? anomalyEventWrapper.getAnomalyHintPrefKey() : null; ? anomalyEventWrapper.getAnomalyHintPrefKey()
: null;
} }
showCategoryTitle(slotTimestamp); showCategoryTitle(slotTimestamp);
@@ -263,35 +268,39 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
} }
private void showCategoryTitle(String slotTimestamp) { private void showCategoryTitle(String slotTimestamp) {
mRootPreference.setTitle(slotTimestamp == null mRootPreference.setTitle(
? mPrefContext.getString( slotTimestamp == null
R.string.battery_usage_breakdown_title_since_last_full_charge) ? mPrefContext.getString(
: mPrefContext.getString( R.string.battery_usage_breakdown_title_since_last_full_charge)
R.string.battery_usage_breakdown_title_for_slot, slotTimestamp)); : mPrefContext.getString(
R.string.battery_usage_breakdown_title_for_slot, slotTimestamp));
mRootPreference.setVisible(true); mRootPreference.setVisible(true);
} }
private void showFooterPreference(boolean isAllBatteryUsageEmpty) { private void showFooterPreference(boolean isAllBatteryUsageEmpty) {
mFooterPreference.setTitle(mPrefContext.getString( mFooterPreference.setTitle(
isAllBatteryUsageEmpty mPrefContext.getString(
? R.string.battery_usage_screen_footer_empty isAllBatteryUsageEmpty
: R.string.battery_usage_screen_footer)); ? R.string.battery_usage_screen_footer_empty
: R.string.battery_usage_screen_footer));
mFooterPreference.setVisible(true); mFooterPreference.setVisible(true);
} }
private void showSpinnerAndAppList() { private void showSpinnerAndAppList() {
if (mBatteryDiffData == null) { if (mBatteryDiffData == null) {
mHandler.post(() -> { mHandler.post(
removeAndCacheAllUnusedPreferences(); () -> {
}); removeAndCacheAllUnusedPreferences();
});
return; return;
} }
mSpinnerPreference.setVisible(true); mSpinnerPreference.setVisible(true);
mAppListPreferenceGroup.setVisible(true); mAppListPreferenceGroup.setVisible(true);
mHandler.post(() -> { mHandler.post(
removeAndCacheAllUnusedPreferences(); () -> {
addAllPreferences(); removeAndCacheAllUnusedPreferences();
}); addAllPreferences();
});
} }
private List<BatteryDiffEntry> getBatteryDiffEntries() { private List<BatteryDiffEntry> getBatteryDiffEntries() {
@@ -348,8 +357,11 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
} }
prefIndex++; prefIndex++;
} }
Log.d(TAG, String.format("addAllPreferences() is finished in %d/ms", Log.d(
(System.currentTimeMillis() - start))); TAG,
String.format(
"addAllPreferences() is finished in %d/ms",
(System.currentTimeMillis() - start)));
} }
@VisibleForTesting @VisibleForTesting
@@ -372,8 +384,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
} }
@VisibleForTesting @VisibleForTesting
void setPreferencePercentage( void setPreferencePercentage(PowerGaugePreference preference, BatteryDiffEntry entry) {
PowerGaugePreference preference, BatteryDiffEntry entry) {
preference.setPercentage( preference.setPercentage(
entry.getPercentage() < BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD entry.getPercentage() < BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD
? mPercentLessThanThresholdText ? mPercentLessThanThresholdText
@@ -383,11 +394,13 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
} }
@VisibleForTesting @VisibleForTesting
void setPreferenceSummary( void setPreferenceSummary(PowerGaugePreference preference, BatteryDiffEntry entry) {
PowerGaugePreference preference, BatteryDiffEntry entry) {
preference.setSummary( preference.setSummary(
BatteryUtils.buildBatteryUsageTimeSummary(mPrefContext, entry.isSystemEntry(), BatteryUtils.buildBatteryUsageTimeSummary(
entry.mForegroundUsageTimeInMs, entry.mBackgroundUsageTimeInMs, mPrefContext,
entry.isSystemEntry(),
entry.mForegroundUsageTimeInMs,
entry.mBackgroundUsageTimeInMs,
entry.mScreenOnTimeInMs)); entry.mScreenOnTimeInMs));
} }
} }

View File

@@ -36,23 +36,23 @@ import java.util.concurrent.Executors;
/** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */ /** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver { public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BatteryUsageBroadcastReceiver"; private static final String TAG = "BatteryUsageBroadcastReceiver";
/** An intent action to request Settings to clear cache data. */ /** An intent action to request Settings to clear cache data. */
public static final String ACTION_CLEAR_BATTERY_CACHE_DATA = public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
"com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"; "com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
/** An intent action for power is plugging. */ /** An intent action for power is plugging. */
public static final String ACTION_BATTERY_PLUGGING = public static final String ACTION_BATTERY_PLUGGING =
"com.android.settings.battery.action.ACTION_BATTERY_PLUGGING"; "com.android.settings.battery.action.ACTION_BATTERY_PLUGGING";
/** An intent action for power is unplugging. */ /** An intent action for power is unplugging. */
public static final String ACTION_BATTERY_UNPLUGGING = public static final String ACTION_BATTERY_UNPLUGGING =
"com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"; "com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING";
@VisibleForTesting @VisibleForTesting static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis();
static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis(); @VisibleForTesting static boolean sIsDebugMode = Build.TYPE.equals("userdebug");
@VisibleForTesting
static boolean sIsDebugMode = Build.TYPE.equals("userdebug");
@VisibleForTesting @VisibleForTesting boolean mFetchBatteryUsageData = false;
boolean mFetchBatteryUsageData = false;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@@ -68,9 +68,10 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
return; return;
} }
DatabaseUtils.recordDateTime(context, action); DatabaseUtils.recordDateTime(context, action);
final String fullChargeIntentAction = FeatureFactory.getFeatureFactory() final String fullChargeIntentAction =
.getPowerUsageFeatureProvider() FeatureFactory.getFeatureFactory()
.getFullChargeIntentAction(); .getPowerUsageFeatureProvider()
.getFullChargeIntentAction();
switch (action) { switch (action) {
case Intent.ACTION_BATTERY_LEVEL_CHANGED: case Intent.ACTION_BATTERY_LEVEL_CHANGED:
// Only when fullChargeIntentAction is ACTION_BATTERY_LEVEL_CHANGED, // Only when fullChargeIntentAction is ACTION_BATTERY_LEVEL_CHANGED,
@@ -117,13 +118,16 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime(); final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime();
// If current boot time is smaller than expected delay, cancel sending the broadcast. // If current boot time is smaller than expected delay, cancel sending the broadcast.
if (delayHourlyJobWhenBooting && broadcastDelay > 0) { if (delayHourlyJobWhenBooting && broadcastDelay > 0) {
Log.d(TAG, "cancel sendBroadcastToFetchUsageData when broadcastDelay is " Log.d(
+ broadcastDelay + "ms."); TAG,
"cancel sendBroadcastToFetchUsageData when broadcastDelay is "
+ broadcastDelay
+ "ms.");
return; return;
} }
mFetchBatteryUsageData = true; mFetchBatteryUsageData = true;
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ true); BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
BootBroadcastReceiver.invokeJobRecheck(context); BootBroadcastReceiver.invokeJobRecheck(context);
} }
@@ -131,7 +135,11 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
final long timestamp = System.currentTimeMillis(); final long timestamp = System.currentTimeMillis();
final Intent intent = BatteryUtils.getBatteryIntent(context); final Intent intent = BatteryUtils.getBatteryIntent(context);
final int batteryLevel = BatteryStatus.getBatteryLevel(intent); final int batteryLevel = BatteryStatus.getBatteryLevel(intent);
mExecutor.execute(() -> DatabaseUtils.sendBatteryEventData(context, mExecutor.execute(
ConvertUtils.convertToBatteryEvent(timestamp, batteryEventType, batteryLevel))); () ->
DatabaseUtils.sendBatteryEventData(
context,
ConvertUtils.convertToBatteryEvent(
timestamp, batteryEventType, batteryLevel)));
} }
} }

View File

@@ -53,6 +53,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
/** Codes */ /** Codes */
private static final int BATTERY_STATE_CODE = 1; private static final int BATTERY_STATE_CODE = 1;
private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2; private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2;
private static final int APP_USAGE_EVENT_CODE = 3; private static final int APP_USAGE_EVENT_CODE = 3;
private static final int BATTERY_EVENT_CODE = 4; private static final int BATTERY_EVENT_CODE = 4;
@@ -67,32 +68,32 @@ public class BatteryUsageContentProvider extends ContentProvider {
static { static {
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_STATE_TABLE, /* path= */ DatabaseUtils.BATTERY_STATE_TABLE,
/*code=*/ BATTERY_STATE_CODE); /* code= */ BATTERY_STATE_CODE);
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.APP_USAGE_LATEST_TIMESTAMP_PATH, /* path= */ DatabaseUtils.APP_USAGE_LATEST_TIMESTAMP_PATH,
/*code=*/ APP_USAGE_LATEST_TIMESTAMP_CODE); /* code= */ APP_USAGE_LATEST_TIMESTAMP_CODE);
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE, /* path= */ DatabaseUtils.APP_USAGE_EVENT_TABLE,
/*code=*/ APP_USAGE_EVENT_CODE); /* code= */ APP_USAGE_EVENT_CODE);
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_EVENT_TABLE, /* path= */ DatabaseUtils.BATTERY_EVENT_TABLE,
/*code=*/ BATTERY_EVENT_CODE); /* code= */ BATTERY_EVENT_CODE);
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.LAST_FULL_CHARGE_TIMESTAMP_PATH, /* path= */ DatabaseUtils.LAST_FULL_CHARGE_TIMESTAMP_PATH,
/*code=*/ LAST_FULL_CHARGE_TIMESTAMP_CODE); /* code= */ LAST_FULL_CHARGE_TIMESTAMP_CODE);
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_STATE_LATEST_TIMESTAMP_PATH, /* path= */ DatabaseUtils.BATTERY_STATE_LATEST_TIMESTAMP_PATH,
/*code=*/ BATTERY_STATE_LATEST_TIMESTAMP_CODE); /* code= */ BATTERY_STATE_LATEST_TIMESTAMP_CODE);
sUriMatcher.addURI( sUriMatcher.addURI(
DatabaseUtils.AUTHORITY, DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_USAGE_SLOT_TABLE, /* path= */ DatabaseUtils.BATTERY_USAGE_SLOT_TABLE,
/*code=*/ BATTERY_USAGE_SLOT_CODE); /* code= */ BATTERY_USAGE_SLOT_CODE);
} }
private Clock mClock; private Clock mClock;
@@ -177,7 +178,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
throw new IllegalArgumentException("unknown URI: " + uri); throw new IllegalArgumentException("unknown URI: " + uri);
} }
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e instanceof IllegalArgumentException) { if (e instanceof IllegalArgumentException) {
throw e; throw e;
} }
Log.e(TAG, "insert() from:" + uri + " error:", e); Log.e(TAG, "insert() from:" + uri + " error:", e);
@@ -208,8 +209,10 @@ public class BatteryUsageContentProvider extends ContentProvider {
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e); Log.e(TAG, "query() from:" + uri + " error:", e);
} }
Log.d(TAG, String.format("getLastFullChargeTimestamp() in %d/ms", Log.d(
mClock.millis() - timestamp)); TAG,
String.format(
"getLastFullChargeTimestamp() in %d/ms", mClock.millis() - timestamp));
return cursor; return cursor;
} }
@@ -222,8 +225,11 @@ public class BatteryUsageContentProvider extends ContentProvider {
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e); Log.e(TAG, "query() from:" + uri + " error:", e);
} }
Log.d(TAG, String.format("getBatteryStateLatestTimestamp() no later than %d in %d/ms", Log.d(
queryTimestamp, mClock.millis() - timestamp)); TAG,
String.format(
"getBatteryStateLatestTimestamp() no later than %d in %d/ms",
queryTimestamp, mClock.millis() - timestamp));
return cursor; return cursor;
} }
@@ -236,8 +242,11 @@ public class BatteryUsageContentProvider extends ContentProvider {
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e); Log.e(TAG, "query() from:" + uri + " error:", e);
} }
Log.d(TAG, String.format("getBatteryStates() after %d in %d/ms", Log.d(
queryTimestamp, mClock.millis() - timestamp)); TAG,
String.format(
"getBatteryStates() after %d in %d/ms",
queryTimestamp, mClock.millis() - timestamp));
return cursor; return cursor;
} }
@@ -270,8 +279,11 @@ public class BatteryUsageContentProvider extends ContentProvider {
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e); Log.e(TAG, "query() from:" + uri + " error:", e);
} }
Log.d(TAG, String.format("getAppUsageLatestTimestamp() for user %d in %d/ms", Log.d(
queryUserId, (mClock.millis() - timestamp))); TAG,
String.format(
"getAppUsageLatestTimestamp() for user %d in %d/ms",
queryUserId, (mClock.millis() - timestamp)));
return cursor; return cursor;
} }

Some files were not shown because too many files have changed in this diff Show More