Make BatterySettings Asynchronous and use enhanced estimate

this CL changes BatteryInfo methods to all use the async style
callback approach it had for one of the methods. Non-async methods
are now annotated to only be used in worker threads. BatteryInfo
can now be obtained via callback by calling one of the async
methods. Alternatively if there is a worker thread available
the synchronous methods similar to the old ones can be used.

The callback methods have all been changed so that they cascade to a
async method that takes all the required info as paremeters. This
will minimize the amount of churn in files that currently use
BatteryInfo.

A new loader was created that can be used to get BatteryInfo in
places that wish to get it. This loader is used in
PowerUsageSummary to get the BatteryInfo.

Test: Robotests
Bug: 38399275
Bug: 38398949
Bug: 38399654
Change-Id: Ic5a82d8ca4c85fad1b883226327ec083badf861d
This commit is contained in:
Salvador Martinez
2017-05-30 11:18:09 -07:00
parent 31d5b3de6f
commit 9cfa7720f4
9 changed files with 237 additions and 191 deletions

View File

@@ -506,8 +506,8 @@ public class BatteryHistoryChart extends View {
mMaxPercentLabelString = Utils.formatPercentage(100);
mMinPercentLabelString = Utils.formatPercentage(0);
mInfo = BatteryInfo.getBatteryInfo(getContext(), mBatteryBroadcast, mStats,
elapsedRealtimeUs);
BatteryInfo.getBatteryInfo(getContext(), info -> {
mInfo = info;
mDrainString = "";
mChargeDurationString = "";
setContentDescription(mInfo.chargeLabelString);
@@ -581,6 +581,7 @@ public class BatteryHistoryChart extends View {
mHavePhoneSignal = true;
}
if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1;
}, mStats, false /* shortString */);
}
@Override

View File

@@ -20,7 +20,6 @@ import android.content.Intent;
import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
@@ -91,12 +90,11 @@ public class BatteryHistoryDetail extends SettingsPreferenceFragment {
}
private void updateEverything() {
BatteryInfo info = BatteryInfo.getBatteryInfo(getContext(), mBatteryBroadcast, mStats,
SystemClock.elapsedRealtime() * 1000);
BatteryInfo.getBatteryInfo(getContext(), info -> {
final View view = getView();
info.bindHistory((UsageView) view.findViewById(R.id.battery_usage), mChargingParser,
mScreenOn, mGpsParser, mFlashlightParser, mCameraParser, mWifiParser, mCpuParser,
mPhoneParser);
mScreenOn, mGpsParser, mFlashlightParser, mCameraParser, mWifiParser,
mCpuParser, mPhoneParser);
((TextView) view.findViewById(R.id.charge)).setText(info.batteryPercentString);
((TextView) view.findViewById(R.id.estimation)).setText(info.remainingLabel);
@@ -108,7 +106,9 @@ public class BatteryHistoryDetail extends SettingsPreferenceFragment {
bindData(mCameraParser, R.string.battery_stats_camera_on_label, R.id.camera_group);
bindData(mWifiParser, R.string.battery_stats_wifi_running_label, R.id.wifi_group);
bindData(mCpuParser, R.string.battery_stats_wake_lock_label, R.id.cpu_group);
bindData(mPhoneParser, R.string.battery_stats_phone_signal_label, R.id.cell_network_group);
bindData(mPhoneParser, R.string.battery_stats_phone_signal_label,
R.id.cell_network_group);
}, mStats, false /* shortString */);
}
private void bindData(BatteryActiveProvider provider, int label, int groupId) {

View File

@@ -17,7 +17,6 @@
package com.android.settings.fuelgauge;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
@@ -43,10 +42,10 @@ public class BatteryHistoryPreference extends Preference {
}
public void setStats(BatteryStatsHelper batteryStats) {
final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
mBatteryInfo = BatteryInfo.getBatteryInfo(getContext(), batteryStats.getBatteryBroadcast(),
batteryStats.getStats(), elapsedRealtimeUs);
BatteryInfo.getBatteryInfo(getContext(), info -> {
mBatteryInfo = info;
notifyChanged();
}, batteryStats.getStats(), false);
}
@Override

View File

@@ -24,9 +24,12 @@ import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.WorkerThread;
import android.text.format.Formatter;
import android.util.SparseIntArray;
import com.android.internal.os.BatteryStatsHelper;
import android.support.annotation.VisibleForTesting;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.R;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.UsageView;
@@ -103,38 +106,57 @@ public class BatteryInfo {
public static void getBatteryInfo(final Context context, final Callback callback,
boolean shortString) {
new AsyncTask<Void, Void, BatteryStats>() {
@Override
protected BatteryStats doInBackground(Void... params) {
BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, true);
statsHelper.create((Bundle) null);
return statsHelper.getStats();
BatteryInfo.getBatteryInfo(context, callback, statsHelper, shortString);
}
public static void getBatteryInfo(final Context context, final Callback callback,
BatteryStatsHelper statsHelper, boolean shortString) {
getBatteryInfo(context, callback, statsHelper.getStats(), shortString);
}
public static void getBatteryInfo(final Context context, final Callback callback,
BatteryStats stats, boolean shortString) {
new AsyncTask<Void, Void, BatteryInfo>() {
@Override
protected BatteryInfo doInBackground(Void... params) {
PowerUsageFeatureProvider provider =
FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
final BatteryUtils batteryUtils = BatteryUtils.getInstance(context);
final long elapsedRealtimeUs =
batteryUtils.convertMsToUs(SystemClock.elapsedRealtime());
Intent batteryBroadcast = context.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
BatteryUtils utils = BatteryUtils.getInstance(context);
if (provider != null && provider.isEnhancedBatteryPredictionEnabled(context)) {
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
elapsedRealtimeUs, shortString,
utils.convertMsToUs(provider.getEnhancedBatteryPrediction(context)),
true);
} else {
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
elapsedRealtimeUs, shortString,
stats.computeBatteryTimeRemaining(elapsedRealtimeUs), false);
}
}
@Override
protected void onPostExecute(BatteryStats batteryStats) {
final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
Intent batteryBroadcast = context.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
BatteryInfo batteryInfo = BatteryInfo.getBatteryInfo(context, batteryBroadcast,
batteryStats, elapsedRealtimeUs, shortString);
protected void onPostExecute(BatteryInfo batteryInfo) {
callback.onBatteryInfoLoaded(batteryInfo);
}
}.execute();
}
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
BatteryStats stats, long elapsedRealtimeUs) {
return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats, elapsedRealtimeUs,
false /* shortString */);
}
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
@WorkerThread
public static BatteryInfo getBatteryInfoOld(Context context, Intent batteryBroadcast,
BatteryStats stats, long elapsedRealtimeUs, boolean shortString) {
return getBatteryInfo(context, batteryBroadcast, stats, elapsedRealtimeUs, shortString,
stats.computeBatteryTimeRemaining(elapsedRealtimeUs), false);
}
@WorkerThread
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
BatteryStats stats, long elapsedRealtimeUs, boolean shortString, long drainTimeUs,
boolean basedOnUsage) {
@@ -144,13 +166,14 @@ public class BatteryInfo {
info.batteryPercentString = Utils.formatPercentage(info.batteryLevel);
info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
final Resources resources = context.getResources();
final BatteryUtils batteryUtils = BatteryUtils.getInstance(context);
info.statusLabel = Utils.getBatteryStatus(resources, batteryBroadcast);
if (!info.mCharging) {
if (drainTimeUs > 0) {
info.remainingTimeUs = drainTimeUs;
String timeString = Formatter.formatShortElapsedTime(context,
drainTimeUs / 1000);
batteryUtils.convertUsToMs(drainTimeUs));
info.remainingLabel = resources.getString(
shortString ?
(basedOnUsage ?
@@ -179,7 +202,7 @@ public class BatteryInfo {
if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
info.remainingTimeUs = chargeTime;
String timeString = Formatter.formatShortElapsedTime(context,
chargeTime / 1000);
batteryUtils.convertUsToMs(chargeTime));
int resId = shortString ? R.string.power_charging_duration_short
: R.string.power_charging_duration;
info.remainingLabel = resources.getString(

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.SystemClock;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AsyncLoader;
/**
* Loader that can be used by classes to load BatteryInfo in a background thread. This loader will
* automatically grab enhanced battery estimates if available or fall back to the system estimate
* when not available.
*/
public class BatteryInfoLoader extends AsyncLoader<BatteryInfo>{
BatteryStatsHelper mStatsHelper;
public BatteryInfoLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
super(context);
mStatsHelper = batteryStatsHelper;
}
@Override
protected void onDiscardResult(BatteryInfo result) {
}
@Override
public BatteryInfo loadInBackground() {
Context context = getContext();
PowerUsageFeatureProvider powerUsageFeatureProvider =
FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
// Stuff we always need to get BatteryInfo
BatteryUtils batteryUtils = BatteryUtils.getInstance(context);
Intent batteryBroadcast = getContext().registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
final long elapsedRealtimeUs = batteryUtils.convertMsToUs(SystemClock.elapsedRealtime());
BatteryInfo batteryInfo;
// Get enhanced prediction if available, otherwise use the old prediction code
Cursor cursor = null;
if (powerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context)) {
final Uri queryUri = powerUsageFeatureProvider.getEnhancedBatteryPredictionUri();
cursor = context.getContentResolver().query(queryUri, null, null, null, null);
}
if (cursor != null && cursor.moveToFirst()) {
long enhancedEstimate = powerUsageFeatureProvider.getTimeRemainingEstimate(cursor);
batteryInfo = BatteryInfo.getBatteryInfo(context, batteryBroadcast,
mStatsHelper.getStats(), elapsedRealtimeUs, false /* shortString */,
batteryUtils.convertMsToUs(enhancedEstimate), true /* basedOnUsage */);
} else {
BatteryStats stats = mStatsHelper.getStats();
batteryInfo = BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
elapsedRealtimeUs, false /* shortString */,
stats.computeBatteryTimeRemaining(elapsedRealtimeUs), false /* basedOnUsage */);
}
return batteryInfo;
}
}

View File

@@ -274,7 +274,7 @@ public class BatteryUtils {
}
private long convertUsToMs(long timeUs) {
public long convertUsToMs(long timeUs) {
return timeUs / 1000;
}

View File

@@ -19,21 +19,15 @@ package com.android.settings.fuelgauge;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Loader;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.SearchIndexableResource;
import android.support.annotation.VisibleForTesting;
@@ -101,7 +95,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
@VisibleForTesting
static final int ANOMALY_LOADER = 1;
@VisibleForTesting
static final int BATTERY_ESTIMATE_LOADER = 2;
static final int BATTERY_INFO_LOADER = 2;
private static final int MENU_STATS_TYPE = Menu.FIRST;
@VisibleForTesting
static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3;
@@ -124,8 +118,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
PowerUsageFeatureProvider mPowerFeatureProvider;
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
long mEnhancedEstimate = -1;
/**
* SparseArray that maps uid to {@link Anomaly}, so we could find {@link Anomaly} by uid
@@ -163,35 +155,21 @@ public class PowerUsageSummary extends PowerUsageBase implements
};
@VisibleForTesting
LoaderManager.LoaderCallbacks<Cursor> mBatteryPredictionLoaderCallbacks =
new LoaderManager.LoaderCallbacks<Cursor>() {
LoaderManager.LoaderCallbacks<BatteryInfo> BatteryInfoLoaderCallbacks =
new LoaderManager.LoaderCallbacks<BatteryInfo>() {
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
final Uri queryUri = mPowerFeatureProvider.getEnhancedBatteryPredictionUri();
return new CursorLoader(getContext(), queryUri, null, null, null, null);
public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) {
return new BatteryInfoLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor == null) {
return;
}
if (cursor.moveToFirst()) {
mEnhancedEstimate =
mPowerFeatureProvider.getTimeRemainingEstimate(cursor);
}
final long elapsedRealtimeUs =
mBatteryUtils.convertMsToUs(SystemClock.elapsedRealtime());
Intent batteryBroadcast = getContext().registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
BatteryInfo batteryInfo = getBatteryInfo(elapsedRealtimeUs, batteryBroadcast);
public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) {
mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
public void onLoaderReset(Loader<BatteryInfo> loader) {
// do nothing
}
};
@@ -213,7 +191,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
mAnomalySparseArray = new SparseArray<>();
initFeatureProvider();
initializeBatteryEstimateLoader();
restartBatteryInfoLoader();
}
@Override
@@ -480,12 +458,8 @@ public class PowerUsageSummary extends PowerUsageBase implements
initAnomalyDetectionIfPossible();
final long elapsedRealtimeUs = mBatteryUtils.convertMsToUs(SystemClock.elapsedRealtime());
Intent batteryBroadcast = context.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
BatteryInfo batteryInfo = getBatteryInfo(elapsedRealtimeUs, batteryBroadcast);
mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo);
// reload BatteryInfo and updateUI
restartBatteryInfoLoader();
final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper,
System.currentTimeMillis());
updateScreenPreference();
@@ -701,28 +675,12 @@ public class PowerUsageSummary extends PowerUsageBase implements
}
}
private BatteryInfo getBatteryInfo(long elapsedRealtimeUs, Intent batteryBroadcast) {
BatteryInfo batteryInfo;
if (mEnhancedEstimate > 0 &&
mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(
getContext())) {
// Drain time is in micro-seconds so we have to multiply by 1000
batteryInfo = BatteryInfo.getBatteryInfo(getContext(), batteryBroadcast,
mStatsHelper.getStats(), elapsedRealtimeUs, false,
mBatteryUtils.convertMsToUs(mEnhancedEstimate), true);
} else {
batteryInfo = BatteryInfo.getBatteryInfo(getContext(), batteryBroadcast,
mStatsHelper.getStats(), elapsedRealtimeUs, false);
}
return batteryInfo;
}
@VisibleForTesting
void initializeBatteryEstimateLoader() {
void restartBatteryInfoLoader() {
if (mPowerFeatureProvider != null
&& mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(getContext())) {
getLoaderManager().initLoader(BATTERY_ESTIMATE_LOADER, Bundle.EMPTY,
mBatteryPredictionLoaderCallbacks);
getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
BatteryInfoLoaderCallbacks);
}
}

View File

@@ -24,6 +24,7 @@ import android.os.BatteryStats;
import android.os.SystemClock;
import com.android.settings.TestConfig;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.R;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +70,7 @@ public class BatteryInfoTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(mContext);
mDisChargingBatteryBroadcast = new Intent();
mDisChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_PLUGGED, 0);
@@ -97,8 +99,9 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_hasStatusLabel() {
doReturn(REMAINING_TIME_NULL).when(mBatteryStats).computeBatteryTimeRemaining(anyLong());
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mDisChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, true /* shortString */);
BatteryInfo info = BatteryInfo.getBatteryInfoOld(mContext,
mDisChargingBatteryBroadcast, mBatteryStats, SystemClock.elapsedRealtime() * 1000,
true /* shortString */);
assertThat(info.statusLabel).isEqualTo(STATUS_FULL);
}
@@ -106,7 +109,7 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_doNotShowChargingMethod_hasRemainingTime() {
doReturn(REMAINING_TIME).when(mBatteryStats).computeChargeTimeRemaining(anyLong());
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
BatteryInfo info = BatteryInfo.getBatteryInfoOld(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */);
assertThat(info.chargeLabelString).isEqualTo(STATUS_CHARGING_TIME);
@@ -115,7 +118,7 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_doNotShowChargingMethod_noRemainingTime() {
doReturn(REMAINING_TIME_NULL).when(mBatteryStats).computeChargeTimeRemaining(anyLong());
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
BatteryInfo info = BatteryInfo.getBatteryInfoOld(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, false /* shortString */);
assertThat(info.chargeLabelString).isEqualTo(STATUS_CHARGING_NO_TIME);
@@ -123,7 +126,7 @@ public class BatteryInfoTest {
@Test
public void testGetBatteryInfo_pluggedIn_dischargingFalse() {
BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
BatteryInfo info = BatteryInfo.getBatteryInfoOld(mContext, mChargingBatteryBroadcast,
mBatteryStats, SystemClock.elapsedRealtime() * 1000, true /* shortString */);
assertThat(info.discharging).isEqualTo(false);

View File

@@ -438,24 +438,6 @@ public class PowerUsageSummaryTest {
assertThat(mFragment.mAnomalySparseArray.get(UID_2)).containsExactly(anomaly3);
}
@Test
public void testBatteryPredictionLoaderCallbacks_DoesNotCrashOnNull() {
// Sanity test to check for crash
mFragment.mBatteryPredictionLoaderCallbacks.onLoadFinished(null, null);
}
@Test
public void testOnCreate_BatteryPredictionSkippedWhenDisabled() {
PowerUsageFeatureProvider provider = mFeatureFactory.getPowerUsageFeatureProvider(mContext);
when(provider.isEnhancedBatteryPredictionEnabled(any())).thenReturn(false);
mFragment.mPowerFeatureProvider = provider;
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
mFragment.initializeBatteryEstimateLoader();
verify(mLoaderManager, never()).initLoader(eq(PowerUsageSummary.BATTERY_ESTIMATE_LOADER),
eq(Bundle.EMPTY), any());
}
@Test
public void testInitAnomalyDetectionIfPossible_detectionEnabled_init() {
when(mFeatureFactory.powerUsageFeatureProvider.isAnomalyDetectionEnabled()).thenReturn(