Battery usage page latency improvement (1-8)

Save battery slot diff data into database in hourly job. Then read the
saved diff data and only calculate the remaining data. This could speed
up the battery usage loading.

Bug: 261163071
Fix: 261163071
Test: manual
Change-Id: Icd4868ca9326b64b17ddbccdb0311e755dc68026
This commit is contained in:
Zaiyue Xue
2023-07-14 20:57:49 +08:00
parent 83c8f47ddd
commit 50da7feeb9
49 changed files with 2713 additions and 1443 deletions

View File

@@ -83,6 +83,7 @@ android_library {
"net-utils-framework-common",
"app-usage-event-protos-lite",
"battery-event-protos-lite",
"battery-usage-slot-protos-lite",
"power-anomaly-event-protos-lite",
"settings-contextual-card-protos-lite",
"settings-log-bridge-protos-lite",

View File

@@ -16,6 +16,8 @@
package com.android.settings.fuelgauge;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isUserConsumer;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.backup.BackupManager;
@@ -41,7 +43,6 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry;
import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
import com.android.settings.fuelgauge.batteryusage.BatteryHistEntry;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.HelpUtils;
@@ -149,14 +150,13 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
Context context, int sourceMetricsCategory,
BatteryDiffEntry diffEntry, String usagePercent, String slotInformation,
boolean showTimeInformation) {
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
// configure the launch argument.
launchArgs.mUsagePercent = usagePercent;
launchArgs.mPackageName = diffEntry.getPackageName();
launchArgs.mAppLabel = diffEntry.getAppLabel();
launchArgs.mSlotInformation = slotInformation;
launchArgs.mUid = (int) histEntry.mUid;
launchArgs.mUid = (int) diffEntry.mUid;
launchArgs.mIconId = diffEntry.getAppIconId();
launchArgs.mConsumedPower = (int) diffEntry.mConsumePower;
if (showTimeInformation) {
@@ -164,7 +164,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
launchArgs.mBackgroundTimeMs = diffEntry.mBackgroundUsageTimeInMs;
launchArgs.mScreenOnTimeMs = diffEntry.mScreenOnTimeInMs;
}
launchArgs.mIsUserEntry = histEntry.isUserEntry();
launchArgs.mIsUserEntry = isUserConsumer(diffEntry.mConsumerType);
startBatteryDetailPage(context, sourceMetricsCategory, launchArgs);
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage;
import android.app.usage.UsageEvents;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/** Load app usage events data in the background. */
public final class AppUsageDataLoader {
private static final String TAG = "AppUsageDataLoader";
// For testing only.
@VisibleForTesting
static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier;
@VisibleForTesting
static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier;
private AppUsageDataLoader() {}
static void enqueueWork(final Context context) {
AsyncTask.execute(() -> {
Log.d(TAG, "loadAppUsageDataSafely() in the AsyncTask");
loadAppUsageDataSafely(context.getApplicationContext());
});
}
@VisibleForTesting
static void loadAppUsageData(final Context context) {
final long start = System.currentTimeMillis();
final Map<Long, UsageEvents> appUsageEvents =
sFakeAppUsageEventsSupplier != null
? sFakeAppUsageEventsSupplier.get()
: DataProcessor.getAppUsageEvents(context);
if (appUsageEvents == null) {
Log.w(TAG, "loadAppUsageData() returns null");
return;
}
final List<AppUsageEvent> appUsageEventList =
sFakeUsageEventsListSupplier != null
? sFakeUsageEventsListSupplier.get()
: DataProcessor.generateAppUsageEventListFromUsageEvents(
context, appUsageEvents);
if (appUsageEventList == null || appUsageEventList.isEmpty()) {
Log.w(TAG, "loadAppUsageData() returns null or empty content");
return;
}
final long elapsedTime = System.currentTimeMillis() - start;
Log.d(TAG, String.format("loadAppUsageData() size=%d in %d/ms", appUsageEventList.size(),
elapsedTime));
// Uploads the AppUsageEvent data into database.
DatabaseUtils.sendAppUsageEventData(context, appUsageEventList);
}
private static void loadAppUsageDataSafely(final Context context) {
try {
loadAppUsageData(context);
} catch (RuntimeException e) {
Log.e(TAG, "loadAppUsageData:" + e);
}
}
}

View File

@@ -125,7 +125,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
private boolean mIs24HourFormat;
private boolean mHourlyChartVisible = true;
private View mBatteryChartViewGroup;
private TextView mChartSummaryTextView;
private BatteryChartViewModel mDailyViewModel;
@@ -227,20 +226,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mOnBatteryTipsUpdatedListener = listener;
}
void setBatteryHistoryMap(
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
: ("size=" + batteryHistoryMap.size())));
// Ensure the battery chart group is visible for users.
animateBatteryChartViewGroup();
final BatteryLevelData batteryLevelData =
DataProcessManager.getBatteryLevelData(mContext, mHandler, batteryHistoryMap,
batteryUsageMap -> {
mBatteryUsageMap = batteryUsageMap;
logScreenUsageTime();
refreshUi();
});
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
void onBatteryLevelDataUpdate(final BatteryLevelData batteryLevelData) {
Log.d(TAG, "onBatteryLevelDataUpdate: " + batteryLevelData);
mMetricsFeatureProvider.action(
mPrefContext,
SettingsEnums.ACTION_BATTERY_HISTORY_LOADED,
@@ -271,6 +258,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
refreshUi();
}
void onBatteryUsageMapUpdate(Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
Log.d(TAG, "onBatteryUsageMapUpdate: " + batteryUsageMap);
mBatteryUsageMap = batteryUsageMap;
logScreenUsageTime();
refreshUi();
}
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) {
final View parentView = (View) dailyChartView.getParent();
@@ -472,10 +466,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
private void animateBatteryHourlyChartView(final boolean visible) {
if (mHourlyChartView == null || mHourlyChartVisible == visible) {
if (mHourlyChartView == null
|| (mHourlyChartView.getVisibility() == View.VISIBLE) == visible) {
return;
}
mHourlyChartVisible = visible;
if (visible) {
mHourlyChartView.setVisibility(View.VISIBLE);
@@ -632,10 +626,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return null;
}
for (BatteryDiffEntry entry : entries) {
final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry;
if (batteryHistEntry != null
&& batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
&& batteryHistEntry.mUserId == userId
if (!entry.isSystemEntry()
&& entry.mUserId == userId
&& packageName.equals(entry.getPackageName())) {
return entry;
}

View File

@@ -17,6 +17,7 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.Utils.formatPercentage;
import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS;
import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKNOWN;
import static java.lang.Math.abs;
import static java.lang.Math.round;
@@ -615,8 +616,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private static boolean isTrapezoidValid(
@NonNull BatteryChartViewModel viewModel, int trapezoidIndex) {
return viewModel.getLevel(trapezoidIndex) != null
&& viewModel.getLevel(trapezoidIndex + 1) != null;
return viewModel.getLevel(trapezoidIndex) != BATTERY_LEVEL_UNKNOWN
&& viewModel.getLevel(trapezoidIndex + 1) != BATTERY_LEVEL_UNKNOWN;
}
private static boolean isTrapezoidIndexValid(

View File

@@ -16,6 +16,8 @@
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
import android.content.Context;
import android.os.BatteryConsumer;
@@ -34,6 +36,10 @@ import java.util.Set;
public class BatteryDiffData {
static final double SMALL_PERCENTAGE_THRESHOLD = 1f;
private final long mStartTimestamp;
private final long mEndTimestamp;
private final int mStartBatteryLevel;
private final int mEndBatteryLevel;
private final long mScreenOnTime;
private final List<BatteryDiffEntry> mAppEntries;
private final List<BatteryDiffEntry> mSystemEntries;
@@ -41,12 +47,20 @@ public class BatteryDiffData {
/** Constructor for the diff entries. */
public BatteryDiffData(
final Context context,
final long startTimestamp,
final long endTimestamp,
final int startBatteryLevel,
final int endBatteryLevel,
final long screenOnTime,
final @NonNull List<BatteryDiffEntry> appDiffEntries,
final @NonNull List<BatteryDiffEntry> systemDiffEntries,
final @NonNull Set<String> systemAppsPackageNames,
final @NonNull Set<Integer> systemAppsUids,
final boolean isAccumulated) {
mStartTimestamp = startTimestamp;
mEndTimestamp = endTimestamp;
mStartBatteryLevel = startBatteryLevel;
mEndBatteryLevel = endBatteryLevel;
mScreenOnTime = screenOnTime;
mAppEntries = appDiffEntries;
mSystemEntries = systemDiffEntries;
@@ -63,18 +77,48 @@ public class BatteryDiffData {
processAndSortEntries(mSystemEntries);
}
public long getScreenOnTime() {
long getStartTimestamp() {
return mStartTimestamp;
}
long getEndTimestamp() {
return mEndTimestamp;
}
int getStartBatteryLevel() {
return mStartBatteryLevel;
}
int getEndBatteryLevel() {
return mEndBatteryLevel;
}
long getScreenOnTime() {
return mScreenOnTime;
}
public List<BatteryDiffEntry> getAppDiffEntryList() {
List<BatteryDiffEntry> getAppDiffEntryList() {
return mAppEntries;
}
public List<BatteryDiffEntry> getSystemDiffEntryList() {
List<BatteryDiffEntry> getSystemDiffEntryList() {
return mSystemEntries;
}
@Override
public String toString() {
return new StringBuilder("BatteryDiffData{")
.append("startTimestamp:" + utcToLocalTimeForLogging(mStartTimestamp))
.append("|endTimestamp:" + utcToLocalTimeForLogging(mEndTimestamp))
.append("|startLevel:" + mStartBatteryLevel)
.append("|endLevel:" + mEndBatteryLevel)
.append("|screenOnTime:" + mScreenOnTime)
.append("|appEntries.size:" + mAppEntries.size())
.append("|systemEntries.size:" + mSystemEntries.size())
.append("}")
.toString();
}
/** Removes fake usage data and hidden packages. */
private void purgeBatteryDiffData(final PowerUsageFeatureProvider featureProvider) {
purgeBatteryDiffData(featureProvider, mAppEntries);
@@ -109,7 +153,7 @@ public class BatteryDiffData {
final long screenOnTimeInMs = entry.mScreenOnTimeInMs;
final double comsumePower = entry.mConsumePower;
final String packageName = entry.getPackageName();
final Integer componentId = entry.mBatteryHistEntry.mDrainType;
final Integer componentId = entry.mComponentId;
if ((screenOnTimeInMs < screenOnTimeThresholdInMs
&& comsumePower < consumePowerThreshold)
|| ConvertUtils.FAKE_PACKAGE_NAME.equals(packageName)
@@ -130,14 +174,16 @@ public class BatteryDiffData {
final @NonNull Set<Integer> systemAppsUids,
final @NonNull List<BatteryDiffEntry> appEntries) {
final List<String> systemAppsAllowlist = featureProvider.getSystemAppsAllowlist();
BatteryDiffEntry.SystemAppsBatteryDiffEntry systemAppsDiffEntry = null;
BatteryDiffEntry systemAppsDiffEntry = null;
final Iterator<BatteryDiffEntry> appListIterator = appEntries.iterator();
while (appListIterator.hasNext()) {
final BatteryDiffEntry batteryDiffEntry = appListIterator.next();
if (needsCombineInSystemApp(batteryDiffEntry, systemAppsAllowlist,
systemAppsPackageNames, systemAppsUids)) {
if (systemAppsDiffEntry == null) {
systemAppsDiffEntry = new BatteryDiffEntry.SystemAppsBatteryDiffEntry(context);
systemAppsDiffEntry = new BatteryDiffEntry(context,
BatteryDiffEntry.SYSTEM_APPS_KEY, BatteryDiffEntry.SYSTEM_APPS_KEY,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
}
systemAppsDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
systemAppsDiffEntry.mForegroundUsageTimeInMs +=
@@ -159,17 +205,18 @@ public class BatteryDiffData {
final Set<Integer> othersSystemComponentSet = featureProvider.getOthersSystemComponentSet();
final Set<String> othersCustomComponentNameSet =
featureProvider.getOthersCustomComponentNameSet();
BatteryDiffEntry.OthersBatteryDiffEntry othersDiffEntry = null;
BatteryDiffEntry othersDiffEntry = null;
final Iterator<BatteryDiffEntry> systemListIterator = systemEntries.iterator();
while (systemListIterator.hasNext()) {
final BatteryDiffEntry batteryDiffEntry = systemListIterator.next();
final int componentId = batteryDiffEntry.mBatteryHistEntry.mDrainType;
final int componentId = batteryDiffEntry.mComponentId;
if (othersSystemComponentSet.contains(componentId) || (
componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
&& othersCustomComponentNameSet.contains(
batteryDiffEntry.getAppLabel()))) {
if (othersDiffEntry == null) {
othersDiffEntry = new BatteryDiffEntry.OthersBatteryDiffEntry(context);
othersDiffEntry = new BatteryDiffEntry(context, BatteryDiffEntry.OTHERS_KEY,
BatteryDiffEntry.OTHERS_KEY, ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
}
othersDiffEntry.mConsumePower += batteryDiffEntry.mConsumePower;
othersDiffEntry.setTotalConsumePower(
@@ -188,7 +235,7 @@ public class BatteryDiffData {
final @NonNull List<String> systemAppsAllowlist,
final @NonNull Set<String> systemAppsPackageNames,
final @NonNull Set<Integer> systemAppsUids) {
if (batteryDiffEntry.mBatteryHistEntry.mIsHidden) {
if (batteryDiffEntry.mIsHidden) {
return true;
}
@@ -201,7 +248,7 @@ public class BatteryDiffData {
return true;
}
int uid = (int) batteryDiffEntry.mBatteryHistEntry.mUid;
int uid = (int) batteryDiffEntry.mUid;
return systemAppsPackageNames.contains(packageName) || systemAppsUids.contains(uid);
}

View File

@@ -15,7 +15,6 @@
*/
package com.android.settings.fuelgauge.batteryusage;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -24,6 +23,7 @@ import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.VisibleForTesting;
@@ -45,12 +45,29 @@ public class BatteryDiffEntry {
static final Map<String, BatteryEntry.NameAndIcon> sResourceCache = new HashMap<>();
// Whether a specific item is valid to launch restriction page?
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public static final Map<String, Boolean> sValidForRestriction = new HashMap<>();
static final Map<String, Boolean> sValidForRestriction = new HashMap<>();
/** A comparator for {@link BatteryDiffEntry} based on the sorting key. */
public static final Comparator<BatteryDiffEntry> COMPARATOR =
static final Comparator<BatteryDiffEntry> COMPARATOR =
(a, b) -> Double.compare(b.getSortingKey(), a.getSortingKey());
static final String SYSTEM_APPS_KEY = "A|SystemApps";
static final String OTHERS_KEY = "S|Others";
// key -> (label_id, icon_id)
private static final Map<String, Pair<Integer, Integer>> SPECIAL_ENTRY_MAP = Map.of(
SYSTEM_APPS_KEY,
Pair.create(R.string.battery_usage_system_apps, R.drawable.ic_power_system),
OTHERS_KEY,
Pair.create(R.string.battery_usage_others,
R.drawable.ic_settings_battery_usage_others));
public long mUid;
public long mUserId;
public String mKey;
public boolean mIsHidden;
public int mComponentId;
public String mLegacyPackageName;
public String mLegacyLabel;
public int mConsumerType;
public long mForegroundUsageTimeInMs;
public long mBackgroundUsageTimeInMs;
public long mScreenOnTimeInMs;
@@ -59,8 +76,6 @@ public class BatteryDiffEntry {
public double mForegroundServiceUsageConsumePower;
public double mBackgroundUsageConsumePower;
public double mCachedUsageConsumePower;
// A BatteryHistEntry corresponding to this diff usage data.
public final BatteryHistEntry mBatteryHistEntry;
protected Context mContext;
@@ -83,6 +98,14 @@ public class BatteryDiffEntry {
public BatteryDiffEntry(
Context context,
long uid,
long userId,
String key,
boolean isHidden,
int componentId,
String legacyPackageName,
String legacyLabel,
int consumerType,
long foregroundUsageTimeInMs,
long backgroundUsageTimeInMs,
long screenOnTimeInMs,
@@ -90,21 +113,36 @@ public class BatteryDiffEntry {
double foregroundUsageConsumePower,
double foregroundServiceUsageConsumePower,
double backgroundUsageConsumePower,
double cachedUsageConsumePower,
BatteryHistEntry batteryHistEntry) {
double cachedUsageConsumePower) {
mContext = context;
mUid = uid;
mUserId = userId;
mKey = key;
mIsHidden = isHidden;
mComponentId = componentId;
mLegacyPackageName = legacyPackageName;
mLegacyLabel = legacyLabel;
mConsumerType = consumerType;
mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
mScreenOnTimeInMs = screenOnTimeInMs;
mConsumePower = consumePower;
mForegroundUsageConsumePower = foregroundUsageConsumePower;
mForegroundServiceUsageConsumePower = foregroundServiceUsageConsumePower;
mBackgroundUsageConsumePower = backgroundUsageConsumePower;
mCachedUsageConsumePower = cachedUsageConsumePower;
mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
mScreenOnTimeInMs = screenOnTimeInMs;
mBatteryHistEntry = batteryHistEntry;
mUserManager = context.getSystemService(UserManager.class);
}
public BatteryDiffEntry(Context context, String key, String legacyLabel, int consumerType) {
this(context, /*uid=*/ 0, /*userId=*/ 0, key, /*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. */
public void setTotalConsumePower(double totalConsumePower) {
mTotalConsumePower = totalConsumePower;
@@ -135,13 +173,22 @@ public class BatteryDiffEntry {
/** Gets the key for sorting */
public double getSortingKey() {
return getPercentage() + getAdjustPercentageOffset();
return getKey() != null && SPECIAL_ENTRY_MAP.containsKey(getKey())
? -1 : getPercentage() + getAdjustPercentageOffset();
}
/** Clones a new instance. */
public BatteryDiffEntry clone() {
return new BatteryDiffEntry(
this.mContext,
this.mUid,
this.mUserId,
this.mKey,
this.mIsHidden,
this.mComponentId,
this.mLegacyPackageName,
this.mLegacyLabel,
this.mConsumerType,
this.mForegroundUsageTimeInMs,
this.mBackgroundUsageTimeInMs,
this.mScreenOnTimeInMs,
@@ -149,17 +196,14 @@ public class BatteryDiffEntry {
this.mForegroundUsageConsumePower,
this.mForegroundServiceUsageConsumePower,
this.mBackgroundUsageConsumePower,
this.mCachedUsageConsumePower,
this.mBatteryHistEntry /*same instance*/);
this.mCachedUsageConsumePower);
}
/** Gets the app label name for this entry. */
public String getAppLabel() {
loadLabelAndIcon();
// Returns default applicationn label if we cannot find it.
return mAppLabel == null || mAppLabel.length() == 0
? mBatteryHistEntry.mAppLabel
: mAppLabel;
// Returns default application label if we cannot find it.
return mAppLabel == null || mAppLabel.length() == 0 ? mLegacyLabel : mAppLabel;
}
/** Gets the app icon {@link Drawable} for this entry. */
@@ -179,7 +223,7 @@ public class BatteryDiffEntry {
/** Gets the searching package name for UID battery type. */
public String getPackageName() {
final String packageName = mDefaultPackageName != null
? mDefaultPackageName : mBatteryHistEntry.mPackageName;
? mDefaultPackageName : mLegacyPackageName;
if (packageName == null) {
return packageName;
}
@@ -198,10 +242,10 @@ public class BatteryDiffEntry {
/** Whether the current BatteryDiffEntry is system component or not. */
public boolean isSystemEntry() {
if (mBatteryHistEntry.mIsHidden) {
if (mIsHidden) {
return false;
}
switch (mBatteryHistEntry.mConsumerType) {
switch (mConsumerType) {
case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
return true;
@@ -236,12 +280,22 @@ public class BatteryDiffEntry {
updateRestrictionFlagState();
sValidForRestriction.put(getKey(), Boolean.valueOf(mValidForRestriction));
if (getKey() != null && SPECIAL_ENTRY_MAP.containsKey(getKey())) {
Pair<Integer, Integer> pair = SPECIAL_ENTRY_MAP.get(getKey());
mAppLabel = mContext.getString(pair.first);
mAppIconId = pair.second;
mAppIcon = mContext.getDrawable(mAppIconId);
sResourceCache.put(
getKey(),
new BatteryEntry.NameAndIcon(mAppLabel, mAppIcon, mAppIconId));
return;
}
// Loads application icon and label based on consumer type.
switch (mBatteryHistEntry.mConsumerType) {
switch (mConsumerType) {
case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
final BatteryEntry.NameAndIcon nameAndIconForUser =
BatteryEntry.getNameAndIconFromUserId(
mContext, (int) mBatteryHistEntry.mUserId);
BatteryEntry.getNameAndIconFromUserId(mContext, (int) mUserId);
if (nameAndIconForUser != null) {
mAppIcon = nameAndIconForUser.mIcon;
mAppLabel = nameAndIconForUser.mName;
@@ -252,8 +306,7 @@ public class BatteryDiffEntry {
break;
case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
final BatteryEntry.NameAndIcon nameAndIconForSystem =
BatteryEntry.getNameAndIconFromPowerComponent(
mContext, mBatteryHistEntry.mDrainType);
BatteryEntry.getNameAndIconFromPowerComponent(mContext, mComponentId);
if (nameAndIconForSystem != null) {
mAppLabel = nameAndIconForSystem.mName;
if (nameAndIconForSystem.mIconId != 0) {
@@ -283,12 +336,12 @@ public class BatteryDiffEntry {
}
String getKey() {
return mBatteryHistEntry.getKey();
return mKey;
}
@VisibleForTesting
void updateRestrictionFlagState() {
if (!mBatteryHistEntry.isAppEntry()) {
if (isSystemEntry()) {
mValidForRestriction = false;
return;
}
@@ -348,7 +401,7 @@ public class BatteryDiffEntry {
return;
}
final int uid = (int) mBatteryHistEntry.mUid;
final int uid = (int) mUid;
final String[] packages = packageManager.getPackagesForUid(uid);
// Loads special defined application label and icon if available.
if (packages == null || packages.length == 0) {
@@ -394,8 +447,7 @@ public class BatteryDiffEntry {
StringUtil.formatElapsedTime(mContext, (double) mScreenOnTimeInMs,
/*withSeconds=*/ true, /*collapseTimeUnit=*/ false)))
.append(String.format("\n\tpackage:%s|%s uid:%d userId:%d",
mBatteryHistEntry.mPackageName, getPackageName(),
mBatteryHistEntry.mUid, mBatteryHistEntry.mUserId));
mLegacyPackageName, getPackageName(), mUid, mUserId));
return builder.toString();
}
@@ -406,130 +458,8 @@ public class BatteryDiffEntry {
}
private Drawable getBadgeIconForUser(Drawable icon) {
final int userId = UserHandle.getUserId((int) mBatteryHistEntry.mUid);
final int userId = UserHandle.getUserId((int) mUid);
return userId == UserHandle.USER_OWNER ? icon :
mUserManager.getBadgedIconForUser(icon, new UserHandle(userId));
}
/** Specific battery diff entry for system apps. */
static class SystemAppsBatteryDiffEntry extends BatteryDiffEntry {
SystemAppsBatteryDiffEntry(Context context) {
super(context,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
/*screenOnTimeInMs=*/ 0,
/*consumePower=*/ 0,
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0,
new BatteryHistEntry(new ContentValues()));
}
@Override
public String getKey() {
return "A|SystemApps";
}
@Override
public String getAppLabel() {
return mContext.getString(R.string.battery_usage_system_apps);
}
@Override
public Drawable getAppIcon() {
return mContext.getDrawable(R.drawable.ic_power_system);
}
@Override
public boolean validForRestriction() {
return false;
}
@Override
public boolean isSystemEntry() {
return false;
}
@Override
public double getSortingKey() {
// Always on the bottom of the app list.
return -1;
}
@Override
public BatteryDiffEntry clone() {
SystemAppsBatteryDiffEntry newEntry = new SystemAppsBatteryDiffEntry(this.mContext);
newEntry.mForegroundUsageTimeInMs = this.mForegroundUsageTimeInMs;
newEntry.mBackgroundUsageTimeInMs = this.mBackgroundUsageTimeInMs;
newEntry.mScreenOnTimeInMs = this.mScreenOnTimeInMs;
newEntry.mConsumePower = this.mConsumePower;
newEntry.mForegroundUsageConsumePower = this.mForegroundUsageConsumePower;
newEntry.mForegroundServiceUsageConsumePower = this.mForegroundServiceUsageConsumePower;
newEntry.mBackgroundUsageConsumePower = this.mBackgroundUsageConsumePower;
newEntry.mCachedUsageConsumePower = this.mCachedUsageConsumePower;
return newEntry;
}
}
/** Specific battery diff entry for others. */
static class OthersBatteryDiffEntry extends BatteryDiffEntry {
OthersBatteryDiffEntry(Context context) {
super(context,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
/*screenOnTimeInMs=*/ 0,
/*consumePower=*/ 0,
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0,
new BatteryHistEntry(new ContentValues()));
}
@Override
public String getKey() {
return "S|Others";
}
@Override
public String getAppLabel() {
return mContext.getString(R.string.battery_usage_others);
}
@Override
public Drawable getAppIcon() {
return mContext.getDrawable(R.drawable.ic_settings_battery_usage_others);
}
@Override
public boolean validForRestriction() {
return false;
}
@Override
public boolean isSystemEntry() {
return true;
}
@Override
public double getSortingKey() {
// Always on the bottom of the system list.
return -1;
}
@Override
public BatteryDiffEntry clone() {
OthersBatteryDiffEntry newEntry = new OthersBatteryDiffEntry(this.mContext);
newEntry.mForegroundUsageTimeInMs = this.mForegroundUsageTimeInMs;
newEntry.mBackgroundUsageTimeInMs = this.mBackgroundUsageTimeInMs;
newEntry.mScreenOnTimeInMs = this.mScreenOnTimeInMs;
newEntry.mConsumePower = this.mConsumePower;
newEntry.mForegroundUsageConsumePower = this.mForegroundUsageConsumePower;
newEntry.mForegroundServiceUsageConsumePower = this.mForegroundServiceUsageConsumePower;
newEntry.mBackgroundUsageConsumePower = this.mBackgroundUsageConsumePower;
newEntry.mCachedUsageConsumePower = this.mCachedUsageConsumePower;
return newEntry;
}
}
}

View File

@@ -169,21 +169,6 @@ public class BatteryHistEntry {
return mIsValidEntry;
}
/** Whether this {@link BatteryHistEntry} is user consumer or not. */
public boolean isUserEntry() {
return mConsumerType == ConvertUtils.CONSUMER_TYPE_USER_BATTERY;
}
/** Whether this {@link BatteryHistEntry} is app consumer or not. */
public boolean isAppEntry() {
return mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
}
/** Whether this {@link BatteryHistEntry} is system consumer or not. */
public boolean isSystemEntry() {
return mConsumerType == ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
}
/** Gets an identifier to represent this {@link BatteryHistEntry}. */
public String getKey() {
if (mKey == null) {

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage;
import android.content.Context;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.Calendar;
import java.util.Map;
/** Loader that can be used to load battery history information. */
public class BatteryHistoryLoader
extends AsyncLoaderCompat<Map<Long, Map<String, BatteryHistEntry>>> {
private static final String TAG = "BatteryHistoryLoader";
private final Context mContext;
public BatteryHistoryLoader(Context context) {
super(context);
mContext = context;
}
@Override
protected void onDiscardResult(Map<Long, Map<String, BatteryHistEntry>> result) {
}
@Override
public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() {
return DatabaseUtils.getHistoryMapSinceLastFullCharge(mContext, Calendar.getInstance());
}
}

View File

@@ -17,17 +17,13 @@
package com.android.settings.fuelgauge.batteryusage;
import android.content.Context;
import android.os.BatteryUsageStats;
import android.util.AttributeSet;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils;
/**
@@ -36,9 +32,6 @@ import com.android.settings.fuelgauge.BatteryUtils;
public class BatteryHistoryPreference extends Preference {
private static final String TAG = "BatteryHistoryPreference";
@VisibleForTesting
BatteryInfo mBatteryInfo;
private BatteryChartView mDailyChartView;
private BatteryChartView mHourlyChartView;
private BatteryChartPreferenceController mChartPreferenceController;
@@ -49,13 +42,6 @@ public class BatteryHistoryPreference extends Preference {
setSelectable(false);
}
void setBatteryUsageStats(@NonNull BatteryUsageStats batteryUsageStats) {
BatteryInfo.getBatteryInfo(getContext(), info -> {
mBatteryInfo = info;
notifyChanged();
}, batteryUsageStats, false);
}
void setChartPreferenceController(BatteryChartPreferenceController controller) {
mChartPreferenceController = controller;
if (mDailyChartView != null && mHourlyChartView != null) {
@@ -67,9 +53,6 @@ public class BatteryHistoryPreference extends Preference {
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
final long startTime = System.currentTimeMillis();
if (mBatteryInfo == null) {
return;
}
final TextView companionTextView = (TextView) view.findViewById(R.id.companion_text);
mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
mDailyChartView.setCompanionTextView(companionTextView);

View File

@@ -16,15 +16,28 @@
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKNOWN;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.util.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
/** Wraps the battery timestamp and level data used for battery usage chart. */
public final class BatteryLevelData {
private static final long MIN_SIZE = 2;
private static final long TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
/** A container for the battery timestamp and level data. */
public static final class PeriodBatteryLevelData {
// The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when
@@ -33,12 +46,14 @@ public final class BatteryLevelData {
private final List<Integer> mLevels;
public PeriodBatteryLevelData(
@NonNull List<Long> timestamps, @NonNull List<Integer> levels) {
Preconditions.checkArgument(timestamps.size() == levels.size(),
/* errorMessage= */ "Timestamp: " + timestamps.size() + ", Level: "
+ levels.size());
@NonNull Map<Long, Integer> batteryLevelMap,
@NonNull List<Long> timestamps) {
mTimestamps = timestamps;
mLevels = levels;
mLevels = new ArrayList<>(timestamps.size());
for (Long timestamp : timestamps) {
mLevels.add(batteryLevelMap.containsKey(timestamp)
? batteryLevelMap.get(timestamp) : BATTERY_LEVEL_UNKNOWN);
}
}
public List<Long> getTimestamps() {
@@ -68,15 +83,21 @@ public final class BatteryLevelData {
// The size of hourly data must be the size of daily data - 1.
private final List<PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
public BatteryLevelData(
@NonNull PeriodBatteryLevelData dailyBatteryLevels,
@NonNull List<PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) {
final long dailySize = dailyBatteryLevels.getTimestamps().size();
final long hourlySize = hourlyBatteryLevelsPerDay.size();
Preconditions.checkArgument(hourlySize == dailySize - 1,
/* errorMessage= */ "DailySize: " + dailySize + ", HourlySize: " + hourlySize);
mDailyBatteryLevels = dailyBatteryLevels;
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
public BatteryLevelData(@NonNull Map<Long, Integer> batteryLevelMap) {
final int mapSize = batteryLevelMap.size();
Preconditions.checkArgument(mapSize >= MIN_SIZE, "batteryLevelMap size:" + mapSize);
final List<Long> timestampList = new ArrayList<>(batteryLevelMap.keySet());
Collections.sort(timestampList);
final List<Long> dailyTimestamps = getDailyTimestamps(timestampList);
final List<List<Long>> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps);
mDailyBatteryLevels = new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps);
mHourlyBatteryLevelsPerDay = new ArrayList<>(hourlyTimestamps.size());
for (List<Long> hourlyTimestampsPerDay : hourlyTimestamps) {
mHourlyBatteryLevelsPerDay.add(
new PeriodBatteryLevelData(batteryLevelMap, hourlyTimestampsPerDay));
}
}
public PeriodBatteryLevelData getDailyBatteryLevels() {
@@ -94,5 +115,69 @@ public final class BatteryLevelData {
Objects.toString(mDailyBatteryLevels),
Objects.toString(mHourlyBatteryLevelsPerDay));
}
@Nullable
static BatteryLevelData combine(@Nullable BatteryLevelData existingBatteryLevelData,
List<BatteryEvent> batteryLevelRecordEvents) {
final Map<Long, Integer> batteryLevelMap = new ArrayMap<>(batteryLevelRecordEvents.size());
for (BatteryEvent event : batteryLevelRecordEvents) {
batteryLevelMap.put(event.getTimestamp(), event.getBatteryLevel());
}
if (existingBatteryLevelData != null) {
List<PeriodBatteryLevelData> multiDaysData =
existingBatteryLevelData.getHourlyBatteryLevelsPerDay();
for (int dayIndex = 0; dayIndex < multiDaysData.size(); dayIndex++) {
PeriodBatteryLevelData oneDayData = multiDaysData.get(dayIndex);
for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size(); hourIndex++) {
batteryLevelMap.put(oneDayData.getTimestamps().get(hourIndex),
oneDayData.getLevels().get(hourIndex));
}
}
}
return batteryLevelMap.size() < MIN_SIZE ? null : new BatteryLevelData(batteryLevelMap);
}
/**
* Computes expected daily timestamp slots.
*
* The valid result should be composed of 3 parts:
* 1) start timestamp
* 2) every 00:00 timestamp (default timezone) between the start and end
* 3) end timestamp
* Otherwise, returns an empty list.
*/
@VisibleForTesting
static List<Long> getDailyTimestamps(final List<Long> timestampList) {
Preconditions.checkArgument(
timestampList.size() >= MIN_SIZE, "timestampList size:" + timestampList.size());
final List<Long> dailyTimestampList = new ArrayList<>();
final long startTimestamp = timestampList.get(0);
final long endTimestamp = timestampList.get(timestampList.size() - 1);
for (long timestamp = startTimestamp; timestamp < endTimestamp;
timestamp = TimestampUtils.getNextDayTimestamp(timestamp)) {
dailyTimestampList.add(timestamp);
}
dailyTimestampList.add(endTimestamp);
return dailyTimestampList;
}
private static List<List<Long>> getHourlyTimestamps(final List<Long> dailyTimestamps) {
final List<List<Long>> hourlyTimestamps = new ArrayList<>();
for (int dailyIndex = 0; dailyIndex < dailyTimestamps.size() - 1; dailyIndex++) {
final List<Long> hourlyTimestampsPerDay = new ArrayList<>();
final long startTime = dailyTimestamps.get(dailyIndex);
final long endTime = dailyTimestamps.get(dailyIndex + 1);
hourlyTimestampsPerDay.add(startTime);
for (long timestamp = TimestampUtils.getNextEvenHourTimestamp(startTime);
timestamp < endTime; timestamp += TIME_SLOT) {
hourlyTimestampsPerDay.add(timestamp);
}
hourlyTimestampsPerDay.add(endTime);
hourlyTimestamps.add(hourlyTimestampsPerDay);
}
return hourlyTimestamps;
}
}

View File

@@ -144,19 +144,17 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
}
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
final String packageName = histEntry.mPackageName;
final boolean isAppEntry = histEntry.isAppEntry();
final String packageName = diffEntry.getPackageName();
mMetricsFeatureProvider.action(
/* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
/* action */ isAppEntry
? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM
: SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
/* action */ diffEntry.isSystemEntry()
? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM
: SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
(int) Math.round(diffEntry.getPercentage()));
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
diffEntry.getAppLabel(), histEntry.getKey(), histEntry.mPackageName));
diffEntry.getAppLabel(), diffEntry.getKey(), packageName));
AdvancedPowerUsageDetail.startBatteryDetailPage(
mActivity, mFragment, diffEntry, powerPref.getPercentage(), mSlotTimestamp);
return true;

View File

@@ -21,7 +21,6 @@ import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
@@ -36,12 +35,14 @@ import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
import java.time.Clock;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/** {@link ContentProvider} class to fetch battery usage data. */
public class BatteryUsageContentProvider extends ContentProvider {
@@ -55,7 +56,12 @@ public class BatteryUsageContentProvider extends ContentProvider {
private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2;
private static final int APP_USAGE_EVENT_CODE = 3;
private static final int BATTERY_EVENT_CODE = 4;
private static final int LAST_FULL_CHARGE_TIMESTAMP_CODE = 5;
private static final int BATTERY_STATE_LATEST_TIMESTAMP_CODE = 6;
private static final int BATTERY_USAGE_SLOT_CODE = 7;
private static final List<Integer> ALL_BATTERY_EVENT_TYPES =
Arrays.stream(BatteryEventType.values()).map(type -> type.getNumber()).toList();
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
@@ -75,12 +81,25 @@ public class BatteryUsageContentProvider extends ContentProvider {
DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_EVENT_TABLE,
/*code=*/ BATTERY_EVENT_CODE);
sUriMatcher.addURI(
DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.LAST_FULL_CHARGE_TIMESTAMP_PATH,
/*code=*/ LAST_FULL_CHARGE_TIMESTAMP_CODE);
sUriMatcher.addURI(
DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_STATE_LATEST_TIMESTAMP_PATH,
/*code=*/ BATTERY_STATE_LATEST_TIMESTAMP_CODE);
sUriMatcher.addURI(
DatabaseUtils.AUTHORITY,
/*path=*/ DatabaseUtils.BATTERY_USAGE_SLOT_TABLE,
/*code=*/ BATTERY_USAGE_SLOT_CODE);
}
private Clock mClock;
private BatteryStateDao mBatteryStateDao;
private AppUsageEventDao mAppUsageEventDao;
private BatteryEventDao mBatteryEventDao;
private BatteryUsageSlotDao mBatteryUsageSlotDao;
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public void setClock(Clock clock) {
@@ -94,9 +113,11 @@ public class BatteryUsageContentProvider extends ContentProvider {
return false;
}
mClock = Clock.systemUTC();
mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao();
mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao();
mBatteryEventDao = BatteryStateDatabase.getInstance(getContext()).batteryEventDao();
final BatteryStateDatabase database = BatteryStateDatabase.getInstance(getContext());
mBatteryStateDao = database.batteryStateDao();
mAppUsageEventDao = database.appUsageEventDao();
mBatteryEventDao = database.batteryEventDao();
mBatteryUsageSlotDao = database.batteryUsageSlotDao();
Log.w(TAG, "create content provider from " + getCallingPackage());
return true;
}
@@ -118,6 +139,12 @@ public class BatteryUsageContentProvider extends ContentProvider {
return getAppUsageLatestTimestamp(uri);
case BATTERY_EVENT_CODE:
return getBatteryEvents(uri);
case LAST_FULL_CHARGE_TIMESTAMP_CODE:
return getLastFullChargeTimestamp(uri);
case BATTERY_STATE_LATEST_TIMESTAMP_CODE:
return getBatteryStateLatestTimestamp(uri);
case BATTERY_USAGE_SLOT_CODE:
return getBatteryUsageSlots(uri);
default:
throw new IllegalArgumentException("unknown URI: " + uri);
}
@@ -132,34 +159,31 @@ public class BatteryUsageContentProvider extends ContentProvider {
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
switch (sUriMatcher.match(uri)) {
case BATTERY_STATE_CODE:
try {
try {
switch (sUriMatcher.match(uri)) {
case BATTERY_STATE_CODE:
mBatteryStateDao.insert(BatteryState.create(contentValues));
return uri;
} catch (RuntimeException e) {
Log.e(TAG, "insert() from:" + uri + " error:" + e);
return null;
}
case APP_USAGE_EVENT_CODE:
try {
break;
case APP_USAGE_EVENT_CODE:
mAppUsageEventDao.insert(AppUsageEventEntity.create(contentValues));
return uri;
} catch (RuntimeException e) {
Log.e(TAG, "insert() from:" + uri + " error:" + e);
return null;
}
case BATTERY_EVENT_CODE:
try {
break;
case BATTERY_EVENT_CODE:
mBatteryEventDao.insert(BatteryEventEntity.create(contentValues));
return uri;
} catch (RuntimeException e) {
Log.e(TAG, "insert() from:" + uri + " error:" + e);
return null;
}
default:
throw new IllegalArgumentException("unknown URI: " + uri);
break;
case BATTERY_USAGE_SLOT_CODE:
mBatteryUsageSlotDao.insert(BatteryUsageSlotEntity.create(contentValues));
break;
default:
throw new IllegalArgumentException("unknown URI: " + uri);
}
} catch (RuntimeException e) {
if (e instanceof IllegalArgumentException) {
throw e;
}
Log.e(TAG, "insert() from:" + uri + " error:", e);
return null;
}
return uri;
}
@Override
@@ -176,21 +200,44 @@ public class BatteryUsageContentProvider extends ContentProvider {
throw new UnsupportedOperationException("unsupported!");
}
private Cursor getBatteryStates(Uri uri) {
final long queryTimestamp = getQueryTimestamp(uri);
return getBatteryStates(uri, queryTimestamp);
}
private Cursor getBatteryStates(Uri uri, long firstTimestamp) {
private Cursor getLastFullChargeTimestamp(Uri uri) {
final long timestamp = mClock.millis();
Cursor cursor = null;
try {
cursor = mBatteryStateDao.getCursorSinceLastFullCharge(firstTimestamp);
cursor = mBatteryEventDao.getLastFullChargeTimestamp();
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:" + e);
Log.e(TAG, "query() from:" + uri + " error:", e);
}
AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext()));
Log.d(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms");
Log.d(TAG, String.format("getLastFullChargeTimestamp() in %d/ms",
mClock.millis() - timestamp));
return cursor;
}
private Cursor getBatteryStateLatestTimestamp(Uri uri) {
final long queryTimestamp = getQueryTimestamp(uri);
final long timestamp = mClock.millis();
Cursor cursor = null;
try {
cursor = mBatteryStateDao.getLatestTimestampBefore(queryTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e);
}
Log.d(TAG, String.format("getBatteryStateLatestTimestamp() no later than %d in %d/ms",
queryTimestamp, mClock.millis() - timestamp));
return cursor;
}
private Cursor getBatteryStates(Uri uri) {
final long queryTimestamp = getQueryTimestamp(uri);
final long timestamp = mClock.millis();
Cursor cursor = null;
try {
cursor = mBatteryStateDao.getBatteryStatesAfter(queryTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e);
}
Log.d(TAG, String.format("getBatteryStates() after %d in %d/ms",
queryTimestamp, mClock.millis() - timestamp));
return cursor;
}
@@ -205,9 +252,9 @@ public class BatteryUsageContentProvider extends ContentProvider {
try {
cursor = mAppUsageEventDao.getAllForUsersAfter(queryUserIds, queryTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:" + e);
Log.e(TAG, "query() from:" + uri + " error:", e);
}
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
Log.w(TAG, "getAppUsageEvents() in " + (mClock.millis() - timestamp) + "/ms");
return cursor;
}
@@ -221,42 +268,78 @@ public class BatteryUsageContentProvider extends ContentProvider {
try {
cursor = mAppUsageEventDao.getLatestTimestampOfUser(queryUserId);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:" + e);
Log.e(TAG, "query() from:" + uri + " error:", e);
}
Log.d(TAG, String.format("query app usage latest timestamp %d for user %d in %d/ms",
timestamp, queryUserId, (mClock.millis() - timestamp)));
Log.d(TAG, String.format("getAppUsageLatestTimestamp() for user %d in %d/ms",
queryUserId, (mClock.millis() - timestamp)));
return cursor;
}
private Cursor getBatteryEvents(Uri uri) {
List<Integer> queryBatteryEventTypes = getQueryBatteryEventTypes(uri);
if (queryBatteryEventTypes == null || queryBatteryEventTypes.isEmpty()) {
queryBatteryEventTypes = ALL_BATTERY_EVENT_TYPES;
}
final long queryTimestamp = getQueryTimestamp(uri);
final long timestamp = mClock.millis();
Cursor cursor = null;
try {
cursor = mBatteryEventDao.getAllAfter(queryTimestamp);
cursor = mBatteryEventDao.getAllAfter(queryTimestamp, queryBatteryEventTypes);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:" + e);
Log.e(TAG, "query() from:" + uri + " error:", e);
}
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
Log.w(TAG, "getBatteryEvents() in " + (mClock.millis() - timestamp) + "/ms");
return cursor;
}
private Cursor getBatteryUsageSlots(Uri uri) {
final long queryTimestamp = getQueryTimestamp(uri);
final long timestamp = mClock.millis();
Cursor cursor = null;
try {
cursor = mBatteryUsageSlotDao.getAllAfter(queryTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:", e);
}
Log.w(TAG, "getBatteryUsageSlots() in " + (mClock.millis() - timestamp) + "/ms");
return cursor;
}
private List<Integer> getQueryBatteryEventTypes(Uri uri) {
Log.d(TAG, "getQueryBatteryEventTypes from uri: " + uri);
final String batteryEventTypesParameter =
uri.getQueryParameter(DatabaseUtils.QUERY_BATTERY_EVENT_TYPE);
if (TextUtils.isEmpty(batteryEventTypesParameter)) {
return null;
}
try {
List<Integer> batteryEventTypes = new ArrayList<>();
for (String typeString : batteryEventTypesParameter.split(",")) {
batteryEventTypes.add(Integer.parseInt(typeString.trim()));
}
return batteryEventTypes;
} catch (NumberFormatException e) {
Log.e(TAG, "invalid query value: " + batteryEventTypesParameter, e);
return null;
}
}
// If URI contains query parameter QUERY_KEY_USERID, use the value directly.
// Otherwise, return null.
private List<Long> getQueryUserIds(Uri uri) {
Log.d(TAG, "getQueryUserIds from uri: " + uri);
final String value = uri.getQueryParameter(DatabaseUtils.QUERY_KEY_USERID);
if (TextUtils.isEmpty(value)) {
Log.w(TAG, "empty query value");
final String userIdsParameter = uri.getQueryParameter(DatabaseUtils.QUERY_KEY_USERID);
if (TextUtils.isEmpty(userIdsParameter)) {
return null;
}
try {
return Arrays.asList(value.split(","))
.stream()
.map(s -> Long.parseLong(s.trim()))
.collect(Collectors.toList());
List<Long> userIds = new ArrayList<>();
for (String idString : userIdsParameter.split(",")) {
userIds.add(Long.parseLong(idString.trim()));
}
return userIds;
} catch (NumberFormatException e) {
Log.e(TAG, "invalid query value: " + value, e);
Log.e(TAG, "invalid query value: " + userIdsParameter, e);
return null;
}
}

View File

@@ -16,9 +16,12 @@
package com.android.settings.fuelgauge.batteryusage;
import android.app.usage.UsageEvents;
import android.content.Context;
import android.os.AsyncTask;
import android.os.BatteryUsageStats;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -27,6 +30,7 @@ import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/** Load battery usage data in the background. */
@@ -36,6 +40,10 @@ public final class BatteryUsageDataLoader {
// For testing only.
@VisibleForTesting
static Supplier<List<BatteryEntry>> sFakeBatteryEntryListSupplier;
@VisibleForTesting
static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier;
@VisibleForTesting
static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier;
private BatteryUsageDataLoader() {
}
@@ -48,9 +56,9 @@ public final class BatteryUsageDataLoader {
}
@VisibleForTesting
static void loadUsageData(final Context context, final boolean isFullChargeStart) {
static void loadBatteryStatsData(final Context context, final boolean isFullChargeStart) {
BatteryUsageLogUtils.writeLog(context, Action.FETCH_USAGE_DATA, "");
final long start = System.currentTimeMillis();
final long currentTime = System.currentTimeMillis();
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
final List<BatteryEntry> batteryEntryList =
sFakeBatteryEntryListSupplier != null ? sFakeBatteryEntryListSupplier.get()
@@ -59,25 +67,81 @@ public final class BatteryUsageDataLoader {
if (batteryEntryList == null || batteryEntryList.isEmpty()) {
Log.w(TAG, "getBatteryEntryList() returns null or empty content");
}
final long elapsedTime = System.currentTimeMillis() - start;
final long elapsedTime = System.currentTimeMillis() - currentTime;
Log.d(TAG, String.format("getBatteryUsageStats() in %d/ms", elapsedTime));
if (isFullChargeStart) {
DatabaseUtils.recordDateTime(
context, DatabaseUtils.KEY_LAST_LOAD_FULL_CHARGE_TIME);
DatabaseUtils.sendBatteryEventData(context, ConvertUtils.convertToBatteryEvent(
currentTime, BatteryEventType.FULL_CHARGED, 100));
}
// Uploads the BatteryEntry data into database.
DatabaseUtils.sendBatteryEntryData(
context, batteryEntryList, batteryUsageStats, isFullChargeStart);
context, currentTime, batteryEntryList, batteryUsageStats, isFullChargeStart);
DataProcessor.closeBatteryUsageStats(batteryUsageStats);
}
@VisibleForTesting
static void loadAppUsageData(final Context context) {
final long start = System.currentTimeMillis();
final Map<Long, UsageEvents> appUsageEvents =
sFakeAppUsageEventsSupplier != null
? sFakeAppUsageEventsSupplier.get()
: DataProcessor.getAppUsageEvents(context);
if (appUsageEvents == null) {
Log.w(TAG, "loadAppUsageData() returns null");
return;
}
final List<AppUsageEvent> appUsageEventList =
sFakeUsageEventsListSupplier != null
? sFakeUsageEventsListSupplier.get()
: DataProcessor.generateAppUsageEventListFromUsageEvents(
context, appUsageEvents);
if (appUsageEventList == null || appUsageEventList.isEmpty()) {
Log.w(TAG, "loadAppUsageData() returns null or empty content");
return;
}
final long elapsedTime = System.currentTimeMillis() - start;
Log.d(TAG, String.format("loadAppUsageData() size=%d in %d/ms", appUsageEventList.size(),
elapsedTime));
// Uploads the AppUsageEvent data into database.
DatabaseUtils.sendAppUsageEventData(context, appUsageEventList);
}
private static void preprocessBatteryUsageSlots(final Context context) {
final long start = System.currentTimeMillis();
final Handler handler = new Handler(Looper.getMainLooper());
final BatteryLevelData batteryLevelData = DataProcessManager.getBatteryLevelData(
context, handler, /*isFromPeriodJob=*/ true,
batteryDiffDataMap -> DatabaseUtils.sendBatteryUsageSlotData(context,
ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap)));
if (batteryLevelData == null) {
Log.d(TAG, "preprocessBatteryUsageSlots() no new battery usage data.");
return;
}
DatabaseUtils.sendBatteryEventData(
context, ConvertUtils.convertToBatteryEventList(batteryLevelData));
Log.d(TAG, String.format(
"preprocessBatteryUsageSlots() batteryLevelData=%s in %d/ms",
batteryLevelData, System.currentTimeMillis() - start));
}
private static void loadUsageDataSafely(
final Context context, final boolean isFullChargeStart) {
try {
loadUsageData(context, isFullChargeStart);
final long start = System.currentTimeMillis();
loadBatteryStatsData(context, isFullChargeStart);
if (!isFullChargeStart) {
// No app usage data or battery diff data at this time.
loadAppUsageData(context);
preprocessBatteryUsageSlots(context);
}
Log.d(TAG, String.format(
"loadUsageDataSafely() in %d/ms", System.currentTimeMillis() - start));
} catch (RuntimeException e) {
Log.e(TAG, "loadUsageData:" + e);
Log.e(TAG, "loadUsageData:", e);
}
}
}

View File

@@ -33,15 +33,21 @@ import android.text.format.DateFormat;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
/** A utility class to convert data into another types. */
@@ -75,7 +81,22 @@ public final class ConvertUtils {
private ConvertUtils() {
}
/** Converts {@link BatteryEntry} to content values */
/** Whether {@code consumerType} is app consumer or not. */
public static boolean isUidConsumer(final int consumerType) {
return consumerType == CONSUMER_TYPE_UID_BATTERY;
}
/** Whether {@code consumerType} is user consumer or not. */
public static boolean isUserConsumer(final int consumerType) {
return consumerType == CONSUMER_TYPE_USER_BATTERY;
}
/** Whether {@code consumerType} is system consumer or not. */
public static boolean isSystemConsumer(final int consumerType) {
return consumerType == CONSUMER_TYPE_SYSTEM_BATTERY;
}
/** Converts {@link BatteryEntry} to {@link ContentValues} */
public static ContentValues convertBatteryEntryToContentValues(
final BatteryEntry entry,
final BatteryUsageStats batteryUsageStats,
@@ -118,7 +139,7 @@ public final class ConvertUtils {
return values;
}
/** Converts {@link AppUsageEvent} to content values */
/** Converts {@link AppUsageEvent} to {@link ContentValues} */
public static ContentValues convertAppUsageEventToContentValues(final AppUsageEvent event) {
final ContentValues values = new ContentValues();
values.put(AppUsageEventEntity.KEY_UID, event.getUid());
@@ -131,7 +152,7 @@ public final class ConvertUtils {
return values;
}
/** Converts {@link BatteryEvent} to content values */
/** Converts {@link BatteryEvent} to {@link ContentValues} */
public static ContentValues convertBatteryEventToContentValues(final BatteryEvent event) {
final ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, event.getTimestamp());
@@ -140,6 +161,16 @@ public final class ConvertUtils {
return values;
}
/** Converts {@link BatteryUsageSlot} to {@link ContentValues} */
public static ContentValues convertBatteryUsageSlotToContentValues(
final BatteryUsageSlot batteryUsageSlot) {
final ContentValues values = new ContentValues(2);
values.put(BatteryUsageSlotEntity.KEY_TIMESTAMP, batteryUsageSlot.getStartTimestamp());
values.put(BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT,
Base64.encodeToString(batteryUsageSlot.toByteArray(), Base64.DEFAULT));
return values;
}
/** Gets the encoded string from {@link BatteryInformation} instance. */
public static String convertBatteryInformationToString(
final BatteryInformation batteryInformation) {
@@ -183,7 +214,7 @@ public final class ConvertUtils {
/*isFullChargeStart=*/ false));
}
/** Converts to {@link AppUsageEvent} from {@link Event} */
/** Converts from {@link Event} to {@link AppUsageEvent} */
@Nullable
public static AppUsageEvent convertToAppUsageEvent(
Context context, IUsageStatsManager usageStatsManager, final Event event,
@@ -234,8 +265,8 @@ public final class ConvertUtils {
return appUsageEventBuilder.build();
}
/** Converts to {@link AppUsageEvent} from {@link Cursor} */
public static AppUsageEvent convertToAppUsageEventFromCursor(final Cursor cursor) {
/** Converts from {@link Cursor} to {@link AppUsageEvent} */
public static AppUsageEvent convertToAppUsageEvent(final Cursor cursor) {
final AppUsageEvent.Builder eventBuilder = AppUsageEvent.newBuilder();
eventBuilder.setTimestamp(getLongFromCursor(cursor, AppUsageEventEntity.KEY_TIMESTAMP));
eventBuilder.setType(
@@ -253,7 +284,7 @@ public final class ConvertUtils {
return eventBuilder.build();
}
/** Converts to {@link BatteryEvent} from {@link BatteryEventType} */
/** Converts from {@link BatteryEventType} to {@link BatteryEvent} */
public static BatteryEvent convertToBatteryEvent(
long timestamp, BatteryEventType type, int batteryLevel) {
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
@@ -263,8 +294,8 @@ public final class ConvertUtils {
return eventBuilder.build();
}
/** Converts to {@link BatteryEvent} from {@link Cursor} */
public static BatteryEvent convertToBatteryEventFromCursor(final Cursor cursor) {
/** Converts from {@link Cursor} to {@link BatteryEvent} */
public static BatteryEvent convertToBatteryEvent(final Cursor cursor) {
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
eventBuilder.setTimestamp(getLongFromCursor(cursor, BatteryEventEntity.KEY_TIMESTAMP));
eventBuilder.setType(
@@ -276,6 +307,42 @@ public final class ConvertUtils {
return eventBuilder.build();
}
/** Converts from {@link BatteryLevelData} to {@link List<BatteryEvent>} */
public static List<BatteryEvent> convertToBatteryEventList(
final BatteryLevelData batteryLevelData) {
final List<BatteryEvent> batteryEventList = new ArrayList<>();
final List<BatteryLevelData.PeriodBatteryLevelData> levelDataList =
batteryLevelData.getHourlyBatteryLevelsPerDay();
for (BatteryLevelData.PeriodBatteryLevelData oneDayData : levelDataList) {
for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size() - 1; hourIndex++) {
batteryEventList.add(convertToBatteryEvent(
oneDayData.getTimestamps().get(hourIndex),
BatteryEventType.EVEN_HOUR,
oneDayData.getLevels().get(hourIndex)));
}
}
return batteryEventList;
}
/** Converts from {@link Cursor} to {@link BatteryUsageSlot} */
public static BatteryUsageSlot convertToBatteryUsageSlot(final Cursor cursor) {
final BatteryUsageSlot defaultInstance = BatteryUsageSlot.getDefaultInstance();
final int columnIndex =
cursor.getColumnIndex(BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT);
return columnIndex < 0 ? defaultInstance : BatteryUtils.parseProtoFromString(
cursor.getString(columnIndex), defaultInstance);
}
/** Converts from {@link Map<Long, BatteryDiffData>} to {@link List<BatteryUsageSlot>} */
public static List<BatteryUsageSlot> convertToBatteryUsageSlotList(
final Map<Long, BatteryDiffData> batteryDiffDataMap) {
List<BatteryUsageSlot> batteryUsageSlotList = new ArrayList<>();
for (BatteryDiffData batteryDiffData : batteryDiffDataMap.values()) {
batteryUsageSlotList.add(convertToBatteryUsageSlot(batteryDiffData));
}
return batteryUsageSlotList;
}
/** Converts UTC timestamp to local time string for logging only, so use the US locale for
* better readability in debugging. */
public static String utcToLocalTimeForLogging(long timestamp) {
@@ -396,6 +463,100 @@ public final class ConvertUtils {
}
}
private static BatteryUsageDiff convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry) {
BatteryUsageDiff.Builder builder = BatteryUsageDiff.newBuilder()
.setUid(batteryDiffEntry.mUid)
.setUserId(batteryDiffEntry.mUserId)
.setIsHidden(batteryDiffEntry.mIsHidden)
.setComponentId(batteryDiffEntry.mComponentId)
.setConsumerType(batteryDiffEntry.mConsumerType)
.setConsumePower(batteryDiffEntry.mConsumePower)
.setForegroundUsageConsumePower(batteryDiffEntry.mForegroundUsageConsumePower)
.setBackgroundUsageConsumePower(batteryDiffEntry.mBackgroundUsageConsumePower)
.setForegroundUsageTime(batteryDiffEntry.mForegroundUsageTimeInMs)
.setBackgroundUsageTime(batteryDiffEntry.mBackgroundUsageTimeInMs)
.setScreenOnTime(batteryDiffEntry.mScreenOnTimeInMs);
if (batteryDiffEntry.mKey != null) {
builder.setKey(batteryDiffEntry.mKey);
}
if (batteryDiffEntry.mLegacyPackageName != null) {
builder.setPackageName(batteryDiffEntry.mLegacyPackageName);
}
if (batteryDiffEntry.mLegacyLabel != null) {
builder.setLabel(batteryDiffEntry.mLegacyLabel);
}
return builder.build();
}
private static BatteryUsageSlot convertToBatteryUsageSlot(
final BatteryDiffData batteryDiffData) {
if (batteryDiffData == null) {
return BatteryUsageSlot.getDefaultInstance();
}
final BatteryUsageSlot.Builder builder = BatteryUsageSlot.newBuilder()
.setStartTimestamp(batteryDiffData.getStartTimestamp())
.setEndTimestamp(batteryDiffData.getEndTimestamp())
.setStartBatteryLevel(batteryDiffData.getStartBatteryLevel())
.setEndBatteryLevel(batteryDiffData.getEndBatteryLevel())
.setScreenOnTime(batteryDiffData.getScreenOnTime());
for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getAppDiffEntryList()) {
builder.addAppUsage(convertToBatteryUsageDiff(batteryDiffEntry));
}
for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getSystemDiffEntryList()) {
builder.addSystemUsage(convertToBatteryUsageDiff(batteryDiffEntry));
}
return builder.build();
}
private static BatteryDiffEntry convertToBatteryDiffEntry(
Context context, final BatteryUsageDiff batteryUsageDiff) {
return new BatteryDiffEntry(
context,
batteryUsageDiff.getUid(),
batteryUsageDiff.getUserId(),
batteryUsageDiff.getKey(),
batteryUsageDiff.getIsHidden(),
batteryUsageDiff.getComponentId(),
batteryUsageDiff.getPackageName(),
batteryUsageDiff.getLabel(),
batteryUsageDiff.getConsumerType(),
batteryUsageDiff.getForegroundUsageTime(),
batteryUsageDiff.getBackgroundUsageTime(),
batteryUsageDiff.getScreenOnTime(),
batteryUsageDiff.getConsumePower(),
batteryUsageDiff.getForegroundUsageConsumePower(),
/*foregroundServiceUsageConsumePower=*/ 0,
batteryUsageDiff.getBackgroundUsageConsumePower(),
/*cachedUsageConsumePower=*/ 0);
}
static BatteryDiffData convertToBatteryDiffData(
Context context,
final BatteryUsageSlot batteryUsageSlot,
@NonNull final Set<String> systemAppsPackageNames,
@NonNull final Set<Integer> systemAppsUids) {
final List<BatteryDiffEntry> appDiffEntries = new ArrayList<>();
final List<BatteryDiffEntry> systemDiffEntries = new ArrayList<>();
for (BatteryUsageDiff batteryUsageDiff : batteryUsageSlot.getAppUsageList()) {
appDiffEntries.add(convertToBatteryDiffEntry(context, batteryUsageDiff));
}
for (BatteryUsageDiff batteryUsageDiff : batteryUsageSlot.getSystemUsageList()) {
systemDiffEntries.add(convertToBatteryDiffEntry(context, batteryUsageDiff));
}
return new BatteryDiffData(
context,
batteryUsageSlot.getStartTimestamp(),
batteryUsageSlot.getEndTimestamp(),
batteryUsageSlot.getStartBatteryLevel(),
batteryUsageSlot.getEndBatteryLevel(),
batteryUsageSlot.getScreenOnTime(),
appDiffEntries,
systemDiffEntries,
systemAppsPackageNames,
systemAppsUids,
/*isAccumulated=*/ false);
}
private static BatteryInformation constructBatteryInformation(
final BatteryEntry entry,
final BatteryUsageStats batteryUsageStats,

View File

@@ -23,6 +23,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -33,10 +34,10 @@ import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Manages the async tasks to process battery and app usage data.
@@ -69,28 +70,37 @@ import java.util.Map;
*/
public class DataProcessManager {
private static final String TAG = "DataProcessManager";
private static final List<BatteryEventType> POWER_CONNECTION_EVENTS =
List.of(BatteryEventType.POWER_CONNECTED, BatteryEventType.POWER_DISCONNECTED);
private static final List<BatteryEventType> BATTERY_LEVEL_RECORD_EVENTS =
List.of(BatteryEventType.FULL_CHARGED, BatteryEventType.EVEN_HOUR);
private final Handler mHandler;
private final DataProcessor.UsageMapAsyncResponse mCallbackFunction;
private final List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
private final List<BatteryEvent> mBatteryEventList = new ArrayList<>();
private Context mContext;
private UserManager mUserManager;
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
// For testing only.
@VisibleForTesting
static Map<Long, Map<String, BatteryHistEntry>> sFakeBatteryHistoryMap;
// Raw start timestamp with round to the nearest hour.
private long mRawStartTimestamp;
private final long mRawStartTimestamp;
private final long mLastFullChargeTimestamp;
private final Context mContext;
private final Handler mHandler;
private final UserManager mUserManager;
private final OnBatteryDiffDataMapLoadedListener mCallbackFunction;
private final List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
private final List<BatteryEvent> mBatteryEventList = new ArrayList<>();
private final List<BatteryUsageSlot> mBatteryUsageSlotList = new ArrayList<>();
private final List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private final Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
private boolean mIsCurrentBatteryHistoryLoaded = false;
private boolean mIsCurrentAppUsageLoaded = false;
private boolean mIsDatabaseAppUsageLoaded = false;
private boolean mIsBatteryEventLoaded = false;
private boolean mIsBatteryUsageSlotLoaded = false;
// Used to identify whether screen-on time data should be shown in the UI.
private boolean mShowScreenOnTime = true;
// Used to identify whether battery level data should be shown in the UI.
private boolean mShowBatteryLevel = true;
private Set<String> mSystemAppsPackageNames = null;
private Set<Integer> mSystemAppsUids = null;
/**
* The indexed {@link AppUsagePeriod} list data for each corresponding time slot.
@@ -100,6 +110,15 @@ public class DataProcessManager {
private Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
mAppUsagePeriodMap;
/**
* A callback listener when all the data is processed.
* This happens when all the async tasks complete and generate the final callback.
*/
public interface OnBatteryDiffDataMapLoadedListener {
/** The callback function when all the data is processed. */
void onBatteryDiffDataMapLoaded(Map<Long, BatteryDiffData> batteryDiffDataMap);
}
/**
* Constructor when there exists battery level data.
*/
@@ -107,16 +126,18 @@ public class DataProcessManager {
Context context,
Handler handler,
final long rawStartTimestamp,
@NonNull final DataProcessor.UsageMapAsyncResponse callbackFunction,
final long lastFullChargeTimestamp,
@NonNull final OnBatteryDiffDataMapLoadedListener callbackFunction,
@NonNull final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
@NonNull final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mContext = context.getApplicationContext();
mHandler = handler;
mUserManager = mContext.getSystemService(UserManager.class);
mRawStartTimestamp = rawStartTimestamp;
mLastFullChargeTimestamp = lastFullChargeTimestamp;
mCallbackFunction = callbackFunction;
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
mBatteryHistoryMap = batteryHistoryMap;
mRawStartTimestamp = rawStartTimestamp;
}
/**
@@ -125,31 +146,49 @@ public class DataProcessManager {
DataProcessManager(
Context context,
Handler handler,
@NonNull final DataProcessor.UsageMapAsyncResponse callbackFunction) {
@NonNull final OnBatteryDiffDataMapLoadedListener callbackFunction) {
mContext = context.getApplicationContext();
mHandler = handler;
mUserManager = mContext.getSystemService(UserManager.class);
mCallbackFunction = callbackFunction;
mRawStartTimestamp = 0L;
mLastFullChargeTimestamp = 0L;
mHourlyBatteryLevelsPerDay = null;
mBatteryHistoryMap = null;
// When there is no battery level data, don't show screen-on time and battery level chart on
// the UI.
mShowScreenOnTime = false;
mShowBatteryLevel = false;
}
/**
* Starts the async tasks to load battery history data and app usage data.
*/
public void start() {
start(/*isFromPeriodJob=*/ false);
}
/**
* Starts the async tasks to load battery history data and app usage data.
*/
public void start(boolean isFromPeriodJob) {
// If we have battery level data, load the battery history map and app usage simultaneously.
if (mShowBatteryLevel) {
// Loads the latest battery history data from the service.
loadCurrentBatteryHistoryMap();
if (mHourlyBatteryLevelsPerDay != null) {
if (isFromPeriodJob) {
mIsCurrentBatteryHistoryLoaded = true;
mIsCurrentAppUsageLoaded = true;
mIsBatteryUsageSlotLoaded = true;
} else {
// Loads the latest battery history data from the service.
loadCurrentBatteryHistoryMap();
// Loads the latest app usage list from the service.
loadCurrentAppUsageList();
// Loads existing battery usage slots from database.
loadBatteryUsageSlotList();
}
// Loads app usage list from database.
loadDatabaseAppUsageList();
// Loads the latest app usage list from the service.
loadCurrentAppUsageList();
// Loads the battery event list from database.
loadBatteryEventList();
loadPowerConnectionBatteryEventList();
} else {
// If there is no battery level data, only load the battery history data from service
// and show it as the app list directly.
@@ -193,11 +232,6 @@ public class DataProcessManager {
return mShowScreenOnTime;
}
@VisibleForTesting
boolean getShowBatteryLevel() {
return mShowBatteryLevel;
}
private void loadCurrentBatteryHistoryMap() {
new AsyncTask<Void, Void, Map<String, BatteryHistEntry>>() {
@Override
@@ -323,7 +357,7 @@ public class DataProcessManager {
}.execute();
}
private void loadBatteryEventList() {
private void loadPowerConnectionBatteryEventList() {
new AsyncTask<Void, Void, List<BatteryEvent>>() {
@Override
protected List<BatteryEvent> doInBackground(Void... voids) {
@@ -331,8 +365,10 @@ public class DataProcessManager {
// Loads the battery event data from the database.
final List<BatteryEvent> batteryEventList =
DatabaseUtils.getBatteryEvents(
mContext, Calendar.getInstance(), mRawStartTimestamp);
Log.d(TAG, String.format("execute loadBatteryEventList size=%d in %d/ms",
mContext, Calendar.getInstance(), mRawStartTimestamp,
POWER_CONNECTION_EVENTS);
Log.d(TAG, String.format(
"execute loadPowerConnectionBatteryEventList size=%d in %d/ms",
batteryEventList.size(), (System.currentTimeMillis() - startTime)));
return batteryEventList;
}
@@ -352,29 +388,55 @@ public class DataProcessManager {
}.execute();
}
private void loadAndApplyBatteryMapFromServiceOnly() {
new AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>>() {
private void loadBatteryUsageSlotList() {
new AsyncTask<Void, Void, List<BatteryUsageSlot>>() {
@Override
protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
protected List<BatteryUsageSlot> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
DataProcessor.getBatteryUsageMapFromStatsService(mContext);
DataProcessor.loadLabelAndIcon(batteryUsageMap);
Log.d(TAG, String.format(
"execute loadAndApplyBatteryMapFromServiceOnly size=%d in %d/ms",
batteryUsageMap.size(), (System.currentTimeMillis() - startTime)));
return batteryUsageMap;
// Loads the battery usage slot data from the database.
final List<BatteryUsageSlot> batteryUsageSlotList =
DatabaseUtils.getBatteryUsageSlots(
mContext, Calendar.getInstance(), mLastFullChargeTimestamp);
Log.d(TAG, String.format("execute loadBatteryUsageSlotList size=%d in %d/ms",
batteryUsageSlotList.size(), (System.currentTimeMillis() - startTime)));
return batteryUsageSlotList;
}
@Override
protected void onPostExecute(
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
// Set the unused variables to null.
mContext = null;
protected void onPostExecute(final List<BatteryUsageSlot> batteryUsageSlotList) {
if (batteryUsageSlotList == null || batteryUsageSlotList.isEmpty()) {
Log.d(TAG, "batteryUsageSlotList is null or empty");
} else {
mBatteryUsageSlotList.clear();
mBatteryUsageSlotList.addAll(batteryUsageSlotList);
}
mIsBatteryUsageSlotLoaded = true;
tryToGenerateFinalDataAndApplyCallback();
}
}.execute();
}
private void loadAndApplyBatteryMapFromServiceOnly() {
new AsyncTask<Void, Void, Map<Long, BatteryDiffData>>() {
@Override
protected Map<Long, BatteryDiffData> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
final Map<Long, BatteryDiffData> batteryDiffDataMap =
DataProcessor.getBatteryDiffDataMapFromStatsService(
mContext, mRawStartTimestamp, getSystemAppsPackageNames(),
getSystemAppsUids());
Log.d(TAG, String.format(
"execute loadAndApplyBatteryMapFromServiceOnly size=%d in %d/ms",
batteryDiffDataMap.size(), (System.currentTimeMillis() - startTime)));
return batteryDiffDataMap;
}
@Override
protected void onPostExecute(final Map<Long, BatteryDiffData> batteryDiffDataMap) {
// Post results back to main thread to refresh UI.
if (mHandler != null && mCallbackFunction != null) {
mHandler.post(() -> {
mCallbackFunction.onBatteryCallbackDataLoaded(batteryUsageMap);
mCallbackFunction.onBatteryDiffDataMapLoaded(batteryDiffDataMap);
});
}
}
@@ -406,38 +468,41 @@ public class DataProcessManager {
if (!mIsCurrentBatteryHistoryLoaded
|| !mIsCurrentAppUsageLoaded
|| !mIsDatabaseAppUsageLoaded
|| !mIsBatteryEventLoaded) {
|| !mIsBatteryEventLoaded
|| !mIsBatteryUsageSlotLoaded) {
return;
}
generateFinalDataAndApplyCallback();
}
private void generateFinalDataAndApplyCallback() {
new AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>>() {
private synchronized void generateFinalDataAndApplyCallback() {
new AsyncTask<Void, Void, Map<Long, BatteryDiffData>>() {
@Override
protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
protected Map<Long, BatteryDiffData> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
DataProcessor.getBatteryUsageMap(
mContext, mHourlyBatteryLevelsPerDay, mBatteryHistoryMap,
mAppUsagePeriodMap);
DataProcessor.loadLabelAndIcon(batteryUsageMap);
Log.d(TAG, String.format("execute generateFinalDataAndApplyCallback in %d/ms",
(System.currentTimeMillis() - startTime)));
return batteryUsageMap;
final Map<Long, BatteryDiffData> batteryDiffDataMap = new ArrayMap<>();
for (BatteryUsageSlot batteryUsageSlot : mBatteryUsageSlotList) {
batteryDiffDataMap.put(batteryUsageSlot.getStartTimestamp(),
ConvertUtils.convertToBatteryDiffData(
mContext, batteryUsageSlot, getSystemAppsPackageNames(),
getSystemAppsUids()));
}
batteryDiffDataMap.putAll(DataProcessor.getBatteryDiffDataMap(mContext,
mHourlyBatteryLevelsPerDay, mBatteryHistoryMap, mAppUsagePeriodMap,
getSystemAppsPackageNames(), getSystemAppsUids()));
Log.d(TAG, String.format(
"execute generateFinalDataAndApplyCallback size=%d in %d/ms",
batteryDiffDataMap.size(), System.currentTimeMillis() - startTime));
return batteryDiffDataMap;
}
@Override
protected void onPostExecute(
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
// Set the unused variables to null.
mContext = null;
mHourlyBatteryLevelsPerDay = null;
mBatteryHistoryMap = null;
protected void onPostExecute(final Map<Long, BatteryDiffData> batteryDiffDataMap) {
// Post results back to main thread to refresh UI.
if (mHandler != null && mCallbackFunction != null) {
mHandler.post(() -> {
mCallbackFunction.onBatteryCallbackDataLoaded(batteryUsageMap);
mCallbackFunction.onBatteryDiffDataMapLoaded(batteryDiffDataMap);
});
}
}
@@ -445,7 +510,7 @@ public class DataProcessManager {
}
// Whether we should load app usage data from service or database.
private boolean shouldLoadAppUsageData() {
private synchronized boolean shouldLoadAppUsageData() {
if (!mShowScreenOnTime) {
return false;
}
@@ -480,6 +545,20 @@ public class DataProcessManager {
return userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
}
private synchronized Set<String> getSystemAppsPackageNames() {
if (mSystemAppsPackageNames == null) {
mSystemAppsPackageNames = DataProcessor.getSystemAppsPackageNames(mContext);
}
return mSystemAppsPackageNames;
}
private synchronized Set<Integer> getSystemAppsUids() {
if (mSystemAppsUids == null) {
mSystemAppsUids = DataProcessor.getSystemAppsUids(mContext);
}
return mSystemAppsUids;
}
/**
* @return Returns battery level data and start async task to compute battery diff usage data
* and load app labels + icons.
@@ -489,14 +568,55 @@ public class DataProcessManager {
public static BatteryLevelData getBatteryLevelData(
Context context,
@Nullable Handler handler,
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final DataProcessor.UsageMapAsyncResponse asyncResponseDelegate) {
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
Log.d(TAG, "batteryHistoryMap is null in getBatteryLevelData()");
new DataProcessManager(context, handler, asyncResponseDelegate).start();
final boolean isFromPeriodJob,
final OnBatteryDiffDataMapLoadedListener onBatteryUsageMapLoadedListener) {
final long start = System.currentTimeMillis();
final long lastFullChargeTime = DatabaseUtils.getLastFullChargeTime(context);
final List<BatteryEvent> batteryLevelRecordEvents =
DatabaseUtils.getBatteryEvents(
context, Calendar.getInstance(), lastFullChargeTime,
BATTERY_LEVEL_RECORD_EVENTS);
final long startTimestamp = batteryLevelRecordEvents.isEmpty()
? lastFullChargeTime : batteryLevelRecordEvents.get(0).getTimestamp();
final BatteryLevelData batteryLevelData = getPeriodBatteryLevelData(context, handler,
startTimestamp, lastFullChargeTime, isFromPeriodJob,
onBatteryUsageMapLoadedListener);
Log.d(TAG, String.format("execute getBatteryLevelData in %d/ms,"
+ " batteryLevelRecordEvents.size=%d",
(System.currentTimeMillis() - start), batteryLevelRecordEvents.size()));
return isFromPeriodJob
? batteryLevelData
: BatteryLevelData.combine(batteryLevelData, batteryLevelRecordEvents);
}
private static BatteryLevelData getPeriodBatteryLevelData(
Context context,
@Nullable Handler handler,
final long startTimestamp,
final long lastFullChargeTime,
final boolean isFromPeriodJob,
final OnBatteryDiffDataMapLoadedListener onBatteryDiffDataMapLoadedListener) {
final long currentTime = System.currentTimeMillis();
Log.d(TAG, String.format("getPeriodBatteryLevelData() startTimestamp=%s",
ConvertUtils.utcToLocalTimeForLogging(startTimestamp)));
if (isFromPeriodJob
&& startTimestamp >= TimestampUtils.getLastEvenHourTimestamp(currentTime)) {
// Nothing needs to be loaded for period job.
return null;
}
handler = handler != null ? handler : new Handler(Looper.getMainLooper());
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
sFakeBatteryHistoryMap != null ? sFakeBatteryHistoryMap
: DatabaseUtils.getHistoryMapSinceLatestRecordBeforeQueryTimestamp(context,
Calendar.getInstance(), startTimestamp, lastFullChargeTime);
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
Log.d(TAG, "batteryHistoryMap is null in getPeriodBatteryLevelData()");
new DataProcessManager(context, handler, onBatteryDiffDataMapLoadedListener).start();
return null;
}
// Process raw history map data into hourly timestamps.
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
DataProcessor.getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
@@ -505,20 +625,20 @@ public class DataProcessManager {
DataProcessor.getLevelDataThroughProcessedHistoryMap(
context, processedBatteryHistoryMap);
if (batteryLevelData == null) {
new DataProcessManager(context, handler, asyncResponseDelegate).start();
new DataProcessManager(context, handler, onBatteryDiffDataMapLoadedListener).start();
Log.d(TAG, "getBatteryLevelData() returns null");
return null;
}
final long rawStartTimestamp = Collections.min(batteryHistoryMap.keySet());
// Start the async task to compute diff usage data and load labels and icons.
new DataProcessManager(
context,
handler,
rawStartTimestamp,
asyncResponseDelegate,
startTimestamp,
lastFullChargeTime,
onBatteryDiffDataMapLoadedListener,
batteryLevelData.getHourlyBatteryLevelsPerDay(),
processedBatteryHistoryMap).start();
processedBatteryHistoryMap).start(isFromPeriodJob);
return batteryLevelData;
}

View File

@@ -17,6 +17,9 @@
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.getEffectivePackageName;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isSystemConsumer;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isUidConsumer;
import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKNOWN;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
@@ -44,6 +47,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,6 +58,8 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.spaprivileged.model.app.AppListRepositoryUtil;
import com.google.common.base.Preconditions;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Calendar;
@@ -76,9 +82,7 @@ public final class DataProcessor {
private static final int POWER_COMPONENT_WAKELOCK = 12;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final int MIN_DAILY_DATA_SIZE = 2;
private static final int MIN_TIMESTAMP_DATA_SIZE = 2;
private static final int MAX_DIFF_SECONDS_OF_UPPER_TIMESTAMP = 5;
private static final long MIN_TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
private static final String MEDIASERVER_PACKAGE_NAME = "mediaserver";
private static final String ANDROID_CORE_APPS_SHARED_USER_ID = "android.uid.shared";
private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new ArrayMap<>();
@@ -157,11 +161,14 @@ public final class DataProcessor {
}
return batteryLevelData == null
? null
: getBatteryUsageMap(
context,
batteryLevelData.getHourlyBatteryLevelsPerDay(),
processedBatteryHistoryMap,
/*appUsagePeriodMap=*/ null);
: generateBatteryUsageMap(context,
getBatteryDiffDataMap(context,
batteryLevelData.getHourlyBatteryLevelsPerDay(),
processedBatteryHistoryMap,
/*appUsagePeriodMap=*/ null,
getSystemAppsPackageNames(context),
getSystemAppsUids(context)),
batteryLevelData);
}
/**
@@ -261,7 +268,7 @@ public final class DataProcessor {
* </ul>
*
* <p>The structure is consistent with the battery usage map returned by
* {@code getBatteryUsageMap}.</p>
* {@code generateBatteryUsageMap}.</p>
*
* <p>{@code Long} stands for the userId.</p>
* <p>{@code String} stands for the packageName.</p>
@@ -403,8 +410,8 @@ public final class DataProcessor {
/**
* @return Returns the processed history map which has interpolated to every hour data.
* The start and end timestamp must be the even hours.
* The keys of processed history map should contain every hour between the start and end
* The start timestamp is the first timestamp in batteryHistoryMap. The end timestamp is current
* time. The keys of processed history map should contain every hour between the start and end
* timestamp. If there's no data in some key, the value will be the empty map.
*/
static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapWithExpectedTimestamps(
@@ -431,28 +438,23 @@ public final class DataProcessor {
static BatteryLevelData getLevelDataThroughProcessedHistoryMap(
Context context,
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap) {
final List<Long> timestampList = new ArrayList<>(processedBatteryHistoryMap.keySet());
Collections.sort(timestampList);
final List<Long> dailyTimestamps = getDailyTimestamps(timestampList);
// There should be at least the start and end timestamps. Otherwise, return null to not show
// data in usage chart.
if (dailyTimestamps.size() < MIN_DAILY_DATA_SIZE) {
if (processedBatteryHistoryMap.size() < MIN_DAILY_DATA_SIZE) {
return null;
}
final List<List<Long>> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps);
final BatteryLevelData.PeriodBatteryLevelData dailyLevelData =
getPeriodBatteryLevelData(context, processedBatteryHistoryMap, dailyTimestamps);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyLevelData =
getHourlyPeriodBatteryLevelData(
context, processedBatteryHistoryMap, hourlyTimestamps);
return new BatteryLevelData(dailyLevelData, hourlyLevelData);
Map<Long, Integer> batteryLevelMap = new ArrayMap<>();
for (Long timestamp : processedBatteryHistoryMap.keySet()) {
batteryLevelMap.put(
timestamp, getLevel(context, processedBatteryHistoryMap, timestamp));
}
return new BatteryLevelData(batteryLevelMap);
}
/**
* Computes expected timestamp slots. The start timestamp is the last full charge time.
* The end timestamp is current time. The middle timestamps are the sharp hour timestamps
* between the start and end timestamps.
* Computes expected timestamp slots. The start timestamp is the first timestamp in
* rawTimestampList. The end timestamp is current time. The middle timestamps are the sharp hour
* timestamps between the start and end timestamps.
*/
@VisibleForTesting
static List<Long> getTimestampSlots(final List<Long> rawTimestampList, final long currentTime) {
@@ -475,56 +477,6 @@ public final class DataProcessor {
return timestampSlots;
}
/**
* Computes expected daily timestamp slots.
*
* The valid result should be composed of 3 parts:
* 1) start timestamp
* 2) every 00:00 timestamp (default timezone) between the start and end
* 3) end timestamp
* Otherwise, returns an empty list.
*/
@VisibleForTesting
static List<Long> getDailyTimestamps(final List<Long> timestampList) {
final List<Long> dailyTimestampList = new ArrayList<>();
// If timestamp number is smaller than 2, the following computation is not necessary.
if (timestampList.size() < MIN_TIMESTAMP_DATA_SIZE) {
return dailyTimestampList;
}
final long startTime = timestampList.get(0);
final long endTime = timestampList.get(timestampList.size() - 1);
for (long timestamp = startTime; timestamp < endTime;
timestamp = TimestampUtils.getNextDayTimestamp(timestamp)) {
dailyTimestampList.add(timestamp);
}
dailyTimestampList.add(endTime);
return dailyTimestampList;
}
@VisibleForTesting
static List<List<Long>> getHourlyTimestamps(final List<Long> dailyTimestamps) {
final List<List<Long>> hourlyTimestamps = new ArrayList<>();
if (dailyTimestamps.size() < MIN_DAILY_DATA_SIZE) {
return hourlyTimestamps;
}
for (int dailyIndex = 0; dailyIndex < dailyTimestamps.size() - 1; dailyIndex++) {
final List<Long> hourlyTimestampsPerDay = new ArrayList<>();
final long startTime = dailyTimestamps.get(dailyIndex);
final long endTime = dailyTimestamps.get(dailyIndex + 1);
hourlyTimestampsPerDay.add(startTime);
for (long timestamp = TimestampUtils.getNextEvenHourTimestamp(startTime);
timestamp < endTime; timestamp += MIN_TIME_SLOT) {
hourlyTimestampsPerDay.add(timestamp);
}
hourlyTimestampsPerDay.add(endTime);
hourlyTimestamps.add(hourlyTimestampsPerDay);
}
return hourlyTimestamps;
}
@VisibleForTesting
static boolean isFromFullCharge(@Nullable final Map<String, BatteryHistEntry> entryList) {
if (entryList == null) {
@@ -560,34 +512,102 @@ public final class DataProcessor {
return results;
}
static Map<Long, BatteryDiffData> getBatteryDiffDataMap(
Context context,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
appUsagePeriodMap,
final @NonNull Set<String> systemAppsPackageNames,
final @NonNull Set<Integer> systemAppsUids) {
final Map<Long, BatteryDiffData> batteryDiffDataMap = new ArrayMap<>();
final int currentUserId = context.getUserId();
final UserHandle userHandle =
Utils.getManagedProfile(context.getSystemService(UserManager.class));
final int workProfileUserId =
userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
// Each time slot usage diff data =
// sum(Math.abs(timestamp[i+1] data - timestamp[i] data));
// since we want to aggregate every hour usage diff data into a single time slot.
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
continue;
}
final List<Long> hourlyTimestamps =
hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
for (int hourlyIndex = 0; hourlyIndex < hourlyTimestamps.size() - 1; hourlyIndex++) {
final Long startTimestamp = hourlyTimestamps.get(hourlyIndex);
final Long endTimestamp = hourlyTimestamps.get(hourlyIndex + 1);
final int startBatteryLevel =
hourlyBatteryLevelsPerDay.get(dailyIndex).getLevels().get(hourlyIndex);
final int endBatteryLevel =
hourlyBatteryLevelsPerDay.get(dailyIndex).getLevels().get(hourlyIndex + 1);
final long slotDuration = endTimestamp - startTimestamp;
List<Map<String, BatteryHistEntry>> slotBatteryHistoryList = new ArrayList<>();
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(startTimestamp, EMPTY_BATTERY_MAP));
for (Long timestamp = TimestampUtils.getNextHourTimestamp(startTimestamp);
timestamp < endTimestamp; timestamp += DateUtils.HOUR_IN_MILLIS) {
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(timestamp, EMPTY_BATTERY_MAP));
}
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(endTimestamp, EMPTY_BATTERY_MAP));
final BatteryDiffData hourlyBatteryDiffData =
insertHourlyUsageDiffDataPerSlot(
context,
startTimestamp,
endTimestamp,
startBatteryLevel,
endBatteryLevel,
currentUserId,
workProfileUserId,
slotDuration,
systemAppsPackageNames,
systemAppsUids,
appUsagePeriodMap == null
|| appUsagePeriodMap.get(dailyIndex) == null
? null
: appUsagePeriodMap.get(dailyIndex).get(hourlyIndex),
slotBatteryHistoryList);
batteryDiffDataMap.put(startTimestamp, hourlyBatteryDiffData);
}
}
return batteryDiffDataMap;
}
/**
* @return Returns the indexed battery usage data for each corresponding time slot.
*
* <p>There could be 2 cases of the returned value:</p>
* <ul>
* <li>null: empty or invalid data.</li>
* <li>non-null: must be a 2d map and composed by 3 parts:</li>
* <li> null: empty or invalid data.</li>
* <li> 1 part: if batteryLevelData is null.</li>
* <p> [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]</p>
* <li> 3 parts: if batteryLevelData is not null.</li>
* <p> 1 - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]</p>
* <p> 2 - [0][SELECTED_INDEX_ALL] ~ [maxDailyIndex][SELECTED_INDEX_ALL]</p>
* <p> 3 - [0][0] ~ [maxDailyIndex][maxHourlyIndex]</p>
* </ul>
*/
@Nullable
static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMap(
static Map<Integer, Map<Integer, BatteryDiffData>> generateBatteryUsageMap(
final Context context,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
appUsagePeriodMap) {
if (batteryHistoryMap.isEmpty()) {
return null;
}
final Map<Long, BatteryDiffData> batteryDiffDataMap,
final @Nullable BatteryLevelData batteryLevelData) {
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new ArrayMap<>();
final Set<String> systemAppsPackageNames = getSystemAppsPackageNames(context);
final Set<Integer> systemAppsUids = getSystemAppsUids(context);
if (batteryLevelData == null) {
Preconditions.checkArgument(batteryDiffDataMap.size() == 1);
BatteryDiffData batteryDiffData = batteryDiffDataMap.values().stream().toList().get(0);
final Map<Integer, BatteryDiffData> allUsageMap = new ArrayMap<>();
allUsageMap.put(SELECTED_INDEX_ALL, batteryDiffData);
resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
return resultMap;
}
List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
batteryLevelData.getHourlyBatteryLevelsPerDay();
// Insert diff data from [0][0] to [maxDailyIndex][maxHourlyIndex].
insertHourlyUsageDiffData(context, systemAppsPackageNames, systemAppsUids,
hourlyBatteryLevelsPerDay, batteryHistoryMap, appUsagePeriodMap, resultMap);
insertHourlyUsageDiffData(hourlyBatteryLevelsPerDay, batteryDiffDataMap, resultMap);
// Insert diff data from [0][SELECTED_INDEX_ALL] to [maxDailyIndex][SELECTED_INDEX_ALL].
insertDailyUsageDiffData(context, hourlyBatteryLevelsPerDay, resultMap);
// Insert diff data [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL].
@@ -602,7 +622,10 @@ public final class DataProcessor {
@Nullable
static BatteryDiffData generateBatteryDiffData(
final Context context,
final List<BatteryHistEntry> batteryHistEntryList) {
final long startTimestamp,
final List<BatteryHistEntry> batteryHistEntryList,
final @NonNull Set<String> systemAppsPackageNames,
final @NonNull Set<Integer> systemAppsUids) {
if (batteryHistEntryList == null || batteryHistEntryList.isEmpty()) {
Log.w(TAG, "batteryHistEntryList is null or empty in generateBatteryDiffData()");
return null;
@@ -624,6 +647,14 @@ public final class DataProcessor {
} else {
final BatteryDiffEntry currentBatteryDiffEntry = new BatteryDiffEntry(
context,
entry.mUid,
entry.mUserId,
entry.getKey(),
entry.mIsHidden,
entry.mDrainType,
entry.mPackageName,
entry.mAppLabel,
entry.mConsumerType,
entry.mForegroundUsageTimeInMs,
entry.mBackgroundUsageTimeInMs,
/*screenOnTimeInMs=*/ 0,
@@ -631,8 +662,7 @@ public final class DataProcessor {
entry.mForegroundUsageConsumePower,
entry.mForegroundServiceUsageConsumePower,
entry.mBackgroundUsageConsumePower,
entry.mCachedUsageConsumePower,
entry);
entry.mCachedUsageConsumePower);
if (currentBatteryDiffEntry.isSystemEntry()) {
systemEntries.add(currentBatteryDiffEntry);
} else {
@@ -645,11 +675,10 @@ public final class DataProcessor {
if (appEntries.isEmpty() && systemEntries.isEmpty()) {
return null;
}
final Set<String> systemAppsPackageNames = getSystemAppsPackageNames(context);
final Set<Integer> systemAppsUids = getSystemAppsUids(context);
return new BatteryDiffData(context, /* screenOnTime= */ 0L, appEntries, systemEntries,
systemAppsPackageNames, systemAppsUids, /* isAccumulated= */ false);
return new BatteryDiffData(context, startTimestamp, getCurrentTimeMillis(),
/* startBatteryLevel =*/ 100, getCurrentLevel(context), /* screenOnTime= */ 0L,
appEntries, systemEntries, systemAppsPackageNames, systemAppsUids,
/* isAccumulated= */ false);
}
/**
@@ -845,21 +874,15 @@ public final class DataProcessor {
return getScreenOnTime(appUsageMap.get(userId).get(packageName));
}
/**
* @return Returns the overall battery usage data from battery stats service directly.
*
* The returned value should be always a 2d map and composed by only 1 part:
* - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
*/
static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMapFromStatsService(
final Context context) {
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new ArrayMap<>();
final Map<Integer, BatteryDiffData> allUsageMap = new ArrayMap<>();
// Always construct the map whether the value is null or not.
allUsageMap.put(SELECTED_INDEX_ALL,
generateBatteryDiffData(context, getBatteryHistListFromFromStatsService(context)));
resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
return resultMap;
static Map<Long, BatteryDiffData> getBatteryDiffDataMapFromStatsService(
final Context context, final long startTimestamp,
@NonNull final Set<String> systemAppsPackageNames,
@NonNull final Set<Integer> systemAppsUids) {
Map<Long, BatteryDiffData> batteryDiffDataMap = new ArrayMap<>(1);
batteryDiffDataMap.put(startTimestamp, generateBatteryDiffData(
context, startTimestamp, getBatteryHistListFromFromStatsService(context),
systemAppsPackageNames, systemAppsUids));
return batteryDiffDataMap;
}
static void loadLabelAndIcon(
@@ -878,6 +901,22 @@ public final class DataProcessor {
}
}
static Set<String> getSystemAppsPackageNames(Context context) {
return sTestSystemAppsPackageNames != null ? sTestSystemAppsPackageNames
: AppListRepositoryUtil.getSystemPackageNames(context, context.getUserId());
}
static Set<Integer> getSystemAppsUids(Context context) {
Set<Integer> result = new ArraySet<>(1);
try {
result.add(context.getPackageManager().getUidForSharedUser(
ANDROID_CORE_APPS_SHARED_USER_ID));
} catch (PackageManager.NameNotFoundException e) {
// No Android Core Apps
}
return result;
}
/**
* Generates the list of {@link AppUsageEvent} within the specific time range.
* The buffer is added to make sure the app usage calculation near the boundaries is correct.
@@ -1158,28 +1197,6 @@ public final class DataProcessor {
resultMap.put(currentSlot, newHistEntryMap);
}
private static List<BatteryLevelData.PeriodBatteryLevelData> getHourlyPeriodBatteryLevelData(
Context context,
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap,
final List<List<Long>> timestamps) {
final List<BatteryLevelData.PeriodBatteryLevelData> levelData = new ArrayList<>();
timestamps.forEach(
timestampList -> levelData.add(
getPeriodBatteryLevelData(
context, processedBatteryHistoryMap, timestampList)));
return levelData;
}
private static BatteryLevelData.PeriodBatteryLevelData getPeriodBatteryLevelData(
Context context,
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap,
final List<Long> timestamps) {
final List<Integer> levels = new ArrayList<>();
timestamps.forEach(
timestamp -> levels.add(getLevel(context, processedBatteryHistoryMap, timestamp)));
return new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels);
}
private static Integer getLevel(
Context context,
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap,
@@ -1188,13 +1205,12 @@ public final class DataProcessor {
if (entryMap == null || entryMap.isEmpty()) {
Log.e(TAG, "abnormal entry list in the timestamp:"
+ ConvertUtils.utcToLocalTimeForLogging(timestamp));
return null;
return BATTERY_LEVEL_UNKNOWN;
}
// The current time battery history hasn't been loaded yet, returns the current battery
// level.
if (entryMap.containsKey(CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)) {
final Intent intent = BatteryUtils.getBatteryIntent(context);
return BatteryStatus.getBatteryLevel(intent);
return getCurrentLevel(context);
}
// Averages the battery level in each time slot to avoid corner conditions.
float batteryLevelCounter = 0;
@@ -1204,20 +1220,15 @@ public final class DataProcessor {
return Math.round(batteryLevelCounter / entryMap.size());
}
private static int getCurrentLevel(Context context) {
final Intent intent = BatteryUtils.getBatteryIntent(context);
return BatteryStatus.getBatteryLevel(intent);
}
private static void insertHourlyUsageDiffData(
Context context,
final Set<String> systemAppsPackageNames,
final Set<Integer> systemAppsUids,
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
appUsagePeriodMap,
final Map<Long, BatteryDiffData> batteryDiffDataMap,
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
final int currentUserId = context.getUserId();
final UserHandle userHandle =
Utils.getManagedProfile(context.getSystemService(UserManager.class));
final int workProfileUserId =
userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
// Each time slot usage diff data =
// sum(Math.abs(timestamp[i+1] data - timestamp[i] data));
// since we want to aggregate every hour usage diff data into a single time slot.
@@ -1231,33 +1242,7 @@ public final class DataProcessor {
hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
for (int hourlyIndex = 0; hourlyIndex < hourlyTimestamps.size() - 1; hourlyIndex++) {
final Long startTimestamp = hourlyTimestamps.get(hourlyIndex);
final Long endTimestamp = hourlyTimestamps.get(hourlyIndex + 1);
final long slotDuration = endTimestamp - startTimestamp;
List<Map<String, BatteryHistEntry>> slotBatteryHistoryList = new ArrayList<>();
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(startTimestamp, EMPTY_BATTERY_MAP));
for (Long timestamp = TimestampUtils.getNextHourTimestamp(startTimestamp);
timestamp < endTimestamp; timestamp += DateUtils.HOUR_IN_MILLIS) {
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(timestamp, EMPTY_BATTERY_MAP));
}
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(endTimestamp, EMPTY_BATTERY_MAP));
final BatteryDiffData hourlyBatteryDiffData =
insertHourlyUsageDiffDataPerSlot(
context,
currentUserId,
workProfileUserId,
slotDuration,
systemAppsPackageNames,
systemAppsUids,
appUsagePeriodMap == null
|| appUsagePeriodMap.get(dailyIndex) == null
? null
: appUsagePeriodMap.get(dailyIndex).get(hourlyIndex),
slotBatteryHistoryList);
dailyDiffMap.put(hourlyIndex, hourlyBatteryDiffData);
dailyDiffMap.put(hourlyIndex, batteryDiffDataMap.get(startTimestamp));
}
}
}
@@ -1292,6 +1277,10 @@ public final class DataProcessor {
@Nullable
private static BatteryDiffData insertHourlyUsageDiffDataPerSlot(
final Context context,
final long startTimestamp,
final long endTimestamp,
final int startBatteryLevel,
final int endBatteryLevel,
final int currentUserId,
final int workProfileUserId,
final long slotDuration,
@@ -1401,7 +1390,7 @@ public final class DataProcessor {
currentEntry.mCachedUsageConsumePower,
nextEntry.mCachedUsageConsumePower);
}
if (selectedBatteryEntry.isSystemEntry()
if (isSystemConsumer(selectedBatteryEntry.mConsumerType)
&& selectedBatteryEntry.mDrainType == BatteryConsumer.POWER_COMPONENT_SCREEN) {
// Replace Screen system component time with screen on time.
foregroundUsageTimeInMs = slotScreenOnTime;
@@ -1447,6 +1436,14 @@ public final class DataProcessor {
backgroundUsageTimeInMs, (long) slotDuration - screenOnTime);
final BatteryDiffEntry currentBatteryDiffEntry = new BatteryDiffEntry(
context,
selectedBatteryEntry.mUid,
selectedBatteryEntry.mUserId,
selectedBatteryEntry.getKey(),
selectedBatteryEntry.mIsHidden,
selectedBatteryEntry.mDrainType,
selectedBatteryEntry.mPackageName,
selectedBatteryEntry.mAppLabel,
selectedBatteryEntry.mConsumerType,
foregroundUsageTimeInMs,
backgroundUsageTimeInMs,
screenOnTime,
@@ -1454,8 +1451,7 @@ public final class DataProcessor {
foregroundUsageConsumePower,
foregroundServiceUsageConsumePower,
backgroundUsageConsumePower,
cachedUsageConsumePower,
selectedBatteryEntry);
cachedUsageConsumePower);
if (currentBatteryDiffEntry.isSystemEntry()) {
systemEntries.add(currentBatteryDiffEntry);
} else {
@@ -1468,7 +1464,8 @@ public final class DataProcessor {
return null;
}
return new BatteryDiffData(context, slotScreenOnTime, appEntries, systemEntries,
return new BatteryDiffData(context, startTimestamp, endTimestamp, startBatteryLevel,
endBatteryLevel, slotScreenOnTime, appEntries, systemEntries,
systemAppsPackageNames, systemAppsUids, /* isAccumulated= */ false);
}
@@ -1519,7 +1516,7 @@ public final class DataProcessor {
final int currentUserId,
final int workProfileUserId,
final BatteryHistEntry batteryHistEntry) {
return batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
return isUidConsumer(batteryHistEntry.mConsumerType)
&& batteryHistEntry.mUserId != currentUserId
&& batteryHistEntry.mUserId != workProfileUserId;
}
@@ -1531,11 +1528,23 @@ public final class DataProcessor {
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
long startTimestamp = Long.MAX_VALUE;
long endTimestamp = 0;
int startBatteryLevel = BATTERY_LEVEL_UNKNOWN;
int endBatteryLevel = BATTERY_LEVEL_UNKNOWN;
long totalScreenOnTime = 0;
for (BatteryDiffData batteryDiffData : batteryDiffDataList) {
if (batteryDiffData == null) {
continue;
}
if (startTimestamp > batteryDiffData.getStartTimestamp()) {
startTimestamp = batteryDiffData.getStartTimestamp();
startBatteryLevel = batteryDiffData.getStartBatteryLevel();
}
if (endTimestamp > batteryDiffData.getEndTimestamp()) {
endTimestamp = batteryDiffData.getEndTimestamp();
endBatteryLevel = batteryDiffData.getEndBatteryLevel();
}
totalScreenOnTime += batteryDiffData.getScreenOnTime();
for (BatteryDiffEntry entry : batteryDiffData.getAppDiffEntryList()) {
computeUsageDiffDataPerEntry(entry, diffEntryMap);
@@ -1554,8 +1563,9 @@ public final class DataProcessor {
}
}
return diffEntryList.isEmpty() ? null : new BatteryDiffData(context, totalScreenOnTime,
appEntries, systemEntries, /* systemAppsPackageNames= */ new ArraySet<>(),
return diffEntryList.isEmpty() ? null : new BatteryDiffData(context, startTimestamp,
endTimestamp, startBatteryLevel, endBatteryLevel, totalScreenOnTime, appEntries,
systemEntries, /* systemAppsPackageNames= */ new ArraySet<>(),
/* systemAppsUids= */ new ArraySet<>(), /* isAccumulated= */ true);
}
@@ -1751,22 +1761,6 @@ public final class DataProcessor {
return v2 > v1 ? v2 - v1 : 0;
}
private static Set<String> getSystemAppsPackageNames(Context context) {
return sTestSystemAppsPackageNames != null ? sTestSystemAppsPackageNames
: AppListRepositoryUtil.getSystemPackageNames(context, context.getUserId());
}
private static Set<Integer> getSystemAppsUids(Context context) {
Set<Integer> result = new ArraySet<>();
try {
result.add(context.getPackageManager().getUidForSharedUser(
ANDROID_CORE_APPS_SHARED_USER_ID));
} catch (PackageManager.NameNotFoundException e) {
// No Android Core Apps
}
return result;
}
private static long getCurrentTimeMillis() {
return sTestCurrentTimeMillis > 0 ? sTestCurrentTimeMillis : System.currentTimeMillis();
}

View File

@@ -15,6 +15,8 @@
*/
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
import android.content.ContentResolver;
@@ -76,12 +78,20 @@ public final class DatabaseUtils {
public static final String BATTERY_EVENT_TABLE = "BatteryEvent";
/** A table name for battery usage history. */
public static final String BATTERY_STATE_TABLE = "BatteryState";
/** A table name for battery usage slot. */
public static final String BATTERY_USAGE_SLOT_TABLE = "BatteryUsageSlot";
/** A path name for last full charge time query. */
public static final String LAST_FULL_CHARGE_TIMESTAMP_PATH = "lastFullChargeTimestamp";
/** A path name for querying the latest record timestamp in battery state table. */
public static final String BATTERY_STATE_LATEST_TIMESTAMP_PATH = "batteryStateLatestTimestamp";
/** A path name for app usage latest timestamp query. */
public static final String APP_USAGE_LATEST_TIMESTAMP_PATH = "appUsageLatestTimestamp";
/** Key for query parameter timestamp used in BATTERY_CONTENT_URI **/
public static final String QUERY_KEY_TIMESTAMP = "timestamp";
/** Key for query parameter userid used in APP_USAGE_EVENT_URI **/
public static final String QUERY_KEY_USERID = "userid";
/** Key for query parameter battery event type used in BATTERY_EVENT_URI **/
public static final String QUERY_BATTERY_EVENT_TYPE = "batteryEventType";
public static final long INVALID_USER_ID = Integer.MIN_VALUE;
/**
@@ -111,6 +121,13 @@ public final class DatabaseUtils {
.authority(AUTHORITY)
.appendPath(BATTERY_STATE_TABLE)
.build();
/** A content URI to access battery usage slots data. */
public static final Uri BATTERY_USAGE_SLOT_URI =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_USAGE_SLOT_TABLE)
.build();
// For testing only.
@VisibleForTesting
@@ -140,7 +157,7 @@ public final class DatabaseUtils {
.build();
final long latestTimestamp =
loadAppUsageLatestTimestampFromContentProvider(context, appUsageLatestTimestampUri);
final String latestTimestampString = ConvertUtils.utcToLocalTimeForLogging(latestTimestamp);
final String latestTimestampString = utcToLocalTimeForLogging(latestTimestamp);
Log.d(TAG, String.format(
"getAppUsageStartTimestampOfUser() userId=%d latestTimestamp=%s in %d/ms",
userId, latestTimestampString, (System.currentTimeMillis() - startTime)));
@@ -161,8 +178,7 @@ public final class DatabaseUtils {
// sure the app usage calculation near the boundaries is correct.
final long queryTimestamp =
Math.max(rawStartTimestamp, sixDaysAgoTimestamp) - USAGE_QUERY_BUFFER_HOURS;
Log.d(TAG, "sixDayAgoTimestamp: " + ConvertUtils.utcToLocalTimeForLogging(
sixDaysAgoTimestamp));
Log.d(TAG, "sixDaysAgoTimestamp: " + utcToLocalTimeForLogging(sixDaysAgoTimestamp));
final String queryUserIdString = userIds.stream()
.map(userId -> String.valueOf(userId))
.collect(Collectors.joining(","));
@@ -189,11 +205,15 @@ public final class DatabaseUtils {
public static List<BatteryEvent> getBatteryEvents(
Context context,
final Calendar calendar,
final long rawStartTimestamp) {
final long rawStartTimestamp,
final List<BatteryEventType> queryBatteryEventTypes) {
final long startTime = System.currentTimeMillis();
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
final long queryTimestamp = Math.max(rawStartTimestamp, sixDaysAgoTimestamp);
Log.d(TAG, "getBatteryEvents for timestamp: " + queryTimestamp);
final String queryBatteryEventTypesString = queryBatteryEventTypes.stream()
.map(type -> String.valueOf(type.getNumber()))
.collect(Collectors.joining(","));
// Builds the content uri everytime to avoid cache.
final Uri batteryEventUri =
new Uri.Builder()
@@ -202,6 +222,8 @@ public final class DatabaseUtils {
.appendPath(BATTERY_EVENT_TABLE)
.appendQueryParameter(
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.appendQueryParameter(
QUERY_BATTERY_EVENT_TYPE, queryBatteryEventTypesString)
.build();
final List<BatteryEvent> batteryEventList =
@@ -211,13 +233,82 @@ public final class DatabaseUtils {
return batteryEventList;
}
/** Long: for timestamp and String: for BatteryHistEntry.getKey() */
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
Context context, Calendar calendar) {
/**
* Returns the battery usage slot data after {@code rawStartTimestamp} in battery event table.
*/
public static List<BatteryUsageSlot> getBatteryUsageSlots(
Context context,
final Calendar calendar,
final long rawStartTimestamp) {
final long startTime = System.currentTimeMillis();
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
Log.d(TAG, "sixDayAgoTimestamp: " + ConvertUtils.utcToLocalTimeForLogging(
sixDaysAgoTimestamp));
final long queryTimestamp = Math.max(rawStartTimestamp, sixDaysAgoTimestamp);
Log.d(TAG, "getBatteryUsageSlots for timestamp: " + queryTimestamp);
// Builds the content uri everytime to avoid cache.
final Uri batteryUsageSlotUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_USAGE_SLOT_TABLE)
.appendQueryParameter(
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.build();
final List<BatteryUsageSlot> batteryUsageSlotList =
loadBatteryUsageSlotsFromContentProvider(context, batteryUsageSlotUri);
Log.d(TAG, String.format("getBatteryUsageSlots size=%d in %d/ms",
batteryUsageSlotList.size(), (System.currentTimeMillis() - startTime)));
return batteryUsageSlotList;
}
/** Returns the last full charge time. */
public static long getLastFullChargeTime(Context context) {
final long startTime = System.currentTimeMillis();
// Builds the content uri everytime to avoid cache.
final Uri lastFullChargeTimeUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(LAST_FULL_CHARGE_TIMESTAMP_PATH)
.build();
final long lastFullChargeTime = loadLastFullChargeTimeFromContentProvider(
context, lastFullChargeTimeUri);
final String lastFullChargeTimeString = utcToLocalTimeForLogging(lastFullChargeTime);
Log.d(TAG, String.format(
"getLastFullChargeTime() lastFullChargeTime=%s in %d/ms",
lastFullChargeTimeString, (System.currentTimeMillis() - startTime)));
return lastFullChargeTime;
}
/** Returns the first battery state timestamp no later than the {@code queryTimestamp}. */
@VisibleForTesting
static long getBatteryStateLatestTimestampBeforeQueryTimestamp(
Context context, final long queryTimestamp) {
final long startTime = System.currentTimeMillis();
// Builds the content uri everytime to avoid cache.
final Uri batteryStateLatestTimestampUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY)
.appendPath(BATTERY_STATE_LATEST_TIMESTAMP_PATH)
.appendQueryParameter(
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.build();
final long batteryStateLatestTimestamp = loadBatteryStateLatestTimestampFromContentProvider(
context, batteryStateLatestTimestampUri);
final String batteryStateLatestTimestampString =
utcToLocalTimeForLogging(batteryStateLatestTimestamp);
Log.d(TAG, String.format(
"getBatteryStateLatestTimestamp() batteryStateLatestTimestamp=%s in %d/ms",
batteryStateLatestTimestampString, (System.currentTimeMillis() - startTime)));
return batteryStateLatestTimestamp;
}
/** Returns the battery history map after the given timestamp. */
@VisibleForTesting
static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceQueryTimestamp(
Context context, final long queryTimestamp) {
final long startTime = System.currentTimeMillis();
// Builds the content uri everytime to avoid cache.
final Uri batteryStateUri =
new Uri.Builder()
@@ -225,20 +316,46 @@ public final class DatabaseUtils {
.authority(AUTHORITY)
.appendPath(BATTERY_STATE_TABLE)
.appendQueryParameter(
QUERY_KEY_TIMESTAMP, Long.toString(sixDaysAgoTimestamp))
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.build();
final Map<Long, Map<String, BatteryHistEntry>> resultMap =
loadHistoryMapFromContentProvider(context, batteryStateUri);
if (resultMap == null || resultMap.isEmpty()) {
Log.d(TAG, "getHistoryMapSinceLastFullCharge() returns empty or null");
Log.d(TAG, "getBatteryHistoryMap() returns empty or null");
} else {
Log.d(TAG, String.format("getHistoryMapSinceLastFullCharge() size=%d in %d/ms",
Log.d(TAG, String.format("getBatteryHistoryMap() size=%d in %d/ms",
resultMap.size(), (System.currentTimeMillis() - startTime)));
}
return resultMap;
}
/**
* Returns the battery history map since the latest record no later than the given timestamp.
* If there is no record before the given timestamp or the given timestamp is before last full
* charge time, returns the history map since last full charge time.
*/
public static Map<Long, Map<String, BatteryHistEntry>>
getHistoryMapSinceLatestRecordBeforeQueryTimestamp(Context context, Calendar calendar,
final long queryTimestamp, final long lastFullChargeTime) {
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
Log.d(TAG, "sixDaysAgoTimestamp: " + utcToLocalTimeForLogging(sixDaysAgoTimestamp));
final long batteryStateLatestTimestamp =
queryTimestamp == 0L ? 0L : getBatteryStateLatestTimestampBeforeQueryTimestamp(
context, queryTimestamp);
final long maxTimestamp = Math.max(Math.max(
sixDaysAgoTimestamp, lastFullChargeTime), batteryStateLatestTimestamp);
return getHistoryMapSinceQueryTimestamp(context, maxTimestamp);
}
/** Returns the history map since last full charge time. */
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
Context context, Calendar calendar) {
final long lastFullChargeTime = getLastFullChargeTime(context);
return getHistoryMapSinceLatestRecordBeforeQueryTimestamp(
context, calendar, 0, lastFullChargeTime);
}
/** Clears all data in the battery usage database. */
public static void clearAll(Context context) {
AsyncTask.execute(() -> {
@@ -248,6 +365,7 @@ public final class DatabaseUtils {
database.appUsageEventDao().clearAll();
database.batteryEventDao().clearAll();
database.batteryStateDao().clearAll();
database.batteryUsageSlotDao().clearAll();
} catch (RuntimeException e) {
Log.e(TAG, "clearAll() failed", e);
}
@@ -265,6 +383,7 @@ public final class DatabaseUtils {
database.appUsageEventDao().clearAllBefore(earliestTimestamp);
database.batteryEventDao().clearAllBefore(earliestTimestamp);
database.batteryStateDao().clearAllBefore(earliestTimestamp);
database.batteryUsageSlotDao().clearAllBefore(earliestTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "clearAllBefore() failed", e);
}
@@ -293,7 +412,7 @@ public final class DatabaseUtils {
/*user=*/ context.getSystemService(UserManager.class)
.getProfileParent(context.getUser()));
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "context.createPackageContextAsUser() fail:" + e);
Log.e(TAG, "context.createPackageContextAsUser() fail:", e);
return null;
}
}
@@ -320,7 +439,7 @@ public final class DatabaseUtils {
resolver.notifyChange(APP_USAGE_EVENT_URI, /*observer=*/ null);
Log.d(TAG, "insert() app usage events data into database");
} catch (Exception e) {
Log.e(TAG, "bulkInsert() app usage data into database error:\n" + e);
Log.e(TAG, "bulkInsert() app usage data into database error:", e);
}
}
Log.d(TAG, String.format("sendAppUsageEventData() size=%d in %d/ms",
@@ -346,8 +465,65 @@ public final class DatabaseUtils {
return contentValues;
}
static List<ContentValues> sendBatteryEventData(
final Context context, final List<BatteryEvent> batteryEventList) {
final long startTime = System.currentTimeMillis();
// Creates the ContentValues list to insert them into provider.
final List<ContentValues> valuesList = new ArrayList<>();
batteryEventList.stream()
.forEach(batteryEvent -> valuesList.add(
ConvertUtils.convertBatteryEventToContentValues(batteryEvent)));
int size = 0;
final ContentResolver resolver = context.getContentResolver();
// Inserts all ContentValues into battery provider.
if (!valuesList.isEmpty()) {
final ContentValues[] valuesArray = new ContentValues[valuesList.size()];
valuesList.toArray(valuesArray);
try {
size = resolver.bulkInsert(BATTERY_EVENT_URI, valuesArray);
resolver.notifyChange(BATTERY_EVENT_URI, /*observer=*/ null);
Log.d(TAG, "insert() battery event data into database");
} catch (Exception e) {
Log.e(TAG, "bulkInsert() battery event data into database error:", e);
}
}
Log.d(TAG, String.format("sendBatteryEventData() size=%d in %d/ms",
size, (System.currentTimeMillis() - startTime)));
clearMemory();
return valuesList;
}
static List<ContentValues> sendBatteryUsageSlotData(
final Context context, final List<BatteryUsageSlot> batteryUsageSlotList) {
final long startTime = System.currentTimeMillis();
// Creates the ContentValues list to insert them into provider.
final List<ContentValues> valuesList = new ArrayList<>();
batteryUsageSlotList.stream()
.forEach(batteryUsageSlot -> valuesList.add(
ConvertUtils.convertBatteryUsageSlotToContentValues(batteryUsageSlot)));
int size = 0;
final ContentResolver resolver = context.getContentResolver();
// Inserts all ContentValues into battery provider.
if (!valuesList.isEmpty()) {
final ContentValues[] valuesArray = new ContentValues[valuesList.size()];
valuesList.toArray(valuesArray);
try {
size = resolver.bulkInsert(BATTERY_USAGE_SLOT_URI, valuesArray);
resolver.notifyChange(BATTERY_USAGE_SLOT_URI, /*observer=*/ null);
Log.d(TAG, "insert() battery usage slots data into database");
} catch (Exception e) {
Log.e(TAG, "bulkInsert() battery usage slots data into database error:", e);
}
}
Log.d(TAG, String.format("sendBatteryUsageSlotData() size=%d in %d/ms",
size, (System.currentTimeMillis() - startTime)));
clearMemory();
return valuesList;
}
static List<ContentValues> sendBatteryEntryData(
final Context context,
final long snapshotTimestamp,
final List<BatteryEntry> batteryEntryList,
final BatteryUsageStats batteryUsageStats,
final boolean isFullChargeStart) {
@@ -364,7 +540,6 @@ public final class DatabaseUtils {
final int batteryHealth = intent.getIntExtra(
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
// We should use the same timestamp for each data snapshot.
final long snapshotTimestamp = Clock.systemUTC().millis();
final long snapshotBootTimestamp = SystemClock.elapsedRealtime();
// Creates the ContentValues list to insert them into provider.
@@ -409,8 +584,7 @@ public final class DatabaseUtils {
Log.d(TAG, "insert() battery states data into database with isFullChargeStart:"
+ isFullChargeStart);
} catch (Exception e) {
errorMessage = e.toString();
Log.e(TAG, "bulkInsert() data into database error:\n" + errorMessage);
Log.e(TAG, "bulkInsert() data into database error:", e);
}
} else {
// Inserts one fake data into battery provider.
@@ -430,8 +604,7 @@ public final class DatabaseUtils {
+ isFullChargeStart);
} catch (Exception e) {
errorMessage = e.toString();
Log.e(TAG, "insert() data into database error:\n" + errorMessage);
Log.e(TAG, "insert() data into database error:", e);
}
valuesList.add(contentValues);
}
@@ -504,8 +677,7 @@ public final class DatabaseUtils {
static void recordDateTime(Context context, String preferenceKey) {
final SharedPreferences sharedPreferences = getSharedPreferences(context);
if (sharedPreferences != null) {
final String currentTime = ConvertUtils.utcToLocalTimeForLogging(
System.currentTimeMillis());
final String currentTime = utcToLocalTimeForLogging(System.currentTimeMillis());
sharedPreferences.edit().putString(preferenceKey, currentTime).apply();
}
}
@@ -533,11 +705,6 @@ public final class DatabaseUtils {
cursor.moveToFirst();
// There is only one column returned so use the index 0 directly.
final long latestTimestamp = cursor.getLong(/*columnIndex=*/ 0);
try {
cursor.close();
} catch (Exception e) {
Log.e(TAG, "cursor.close() failed", e);
}
// If there is no data for this user, 0 will be returned from the database.
return latestTimestamp == 0 ? INVALID_USER_ID : latestTimestamp;
}
@@ -556,14 +723,9 @@ public final class DatabaseUtils {
if (cursor == null || cursor.getCount() == 0) {
return appUsageEventList;
}
// Loads and recovers all AppUsageEvent data from cursor.
// Loads and converts all AppUsageEvent data from cursor.
while (cursor.moveToNext()) {
appUsageEventList.add(ConvertUtils.convertToAppUsageEventFromCursor(cursor));
}
try {
cursor.close();
} catch (Exception e) {
Log.e(TAG, "cursor.close() failed", e);
appUsageEventList.add(ConvertUtils.convertToAppUsageEvent(cursor));
}
}
return appUsageEventList;
@@ -582,19 +744,71 @@ public final class DatabaseUtils {
if (cursor == null || cursor.getCount() == 0) {
return batteryEventList;
}
// Loads and recovers all AppUsageEvent data from cursor.
// Loads and converts all AppUsageEvent data from cursor.
while (cursor.moveToNext()) {
batteryEventList.add(ConvertUtils.convertToBatteryEventFromCursor(cursor));
}
try {
cursor.close();
} catch (Exception e) {
Log.e(TAG, "cursor.close() failed", e);
batteryEventList.add(ConvertUtils.convertToBatteryEvent(cursor));
}
}
return batteryEventList;
}
private static List<BatteryUsageSlot> loadBatteryUsageSlotsFromContentProvider(
Context context, Uri batteryUsageSlotUri) {
final List<BatteryUsageSlot> batteryUsageSlotList = new ArrayList<>();
context = getParentContext(context);
if (context == null) {
return batteryUsageSlotList;
}
try (Cursor cursor = sFakeSupplier != null
? sFakeSupplier.get()
: context.getContentResolver().query(batteryUsageSlotUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return batteryUsageSlotList;
}
// Loads and converts all AppUsageEvent data from cursor.
while (cursor.moveToNext()) {
batteryUsageSlotList.add(ConvertUtils.convertToBatteryUsageSlot(cursor));
}
}
return batteryUsageSlotList;
}
private static long loadLastFullChargeTimeFromContentProvider(
Context context, final Uri lastFullChargeTimeUri) {
// We have already make sure the context here is with profile parent's user identity. Don't
// need to check whether current user is work profile.
try (Cursor cursor = sFakeSupplier != null
? sFakeSupplier.get()
: context.getContentResolver().query(
lastFullChargeTimeUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return 0L;
}
cursor.moveToFirst();
// There is only one column returned so use the index 0 directly.
final long lastFullChargeTime = cursor.getLong(/*columnIndex=*/ 0);
return lastFullChargeTime;
}
}
private static long loadBatteryStateLatestTimestampFromContentProvider(
Context context, final Uri batteryStateLatestTimestampUri) {
// We have already make sure the context here is with profile parent's user identity. Don't
// need to check whether current user is work profile.
try (Cursor cursor = sFakeSupplier != null
? sFakeSupplier.get()
: context.getContentResolver().query(
batteryStateLatestTimestampUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return 0L;
}
cursor.moveToFirst();
// There is only one column returned so use the index 0 directly.
final long batteryStateLatestTimestamp = cursor.getLong(/*columnIndex=*/ 0);
return batteryStateLatestTimestamp;
}
}
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
Context context, Uri batteryStateUri) {
context = getParentContext(context);
@@ -607,7 +821,7 @@ public final class DatabaseUtils {
if (cursor == null || cursor.getCount() == 0) {
return resultMap;
}
// Loads and recovers all BatteryHistEntry data from cursor.
// Loads and converts all BatteryHistEntry data from cursor.
while (cursor.moveToNext()) {
final BatteryHistEntry entry = new BatteryHistEntry(cursor);
final long timestamp = entry.mTimestamp;
@@ -620,11 +834,6 @@ public final class DatabaseUtils {
}
batteryHistEntryMap.put(key, entry);
}
try {
cursor.close();
} catch (Exception e) {
Log.e(TAG, "cursor.close() failed", e);
}
}
return resultMap;
}

View File

@@ -44,7 +44,6 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
}
BatteryUsageLogUtils.writeLog(context, Action.EXECUTE_JOB, "");
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
AppUsageDataLoader.enqueueWork(context);
Log.d(TAG, "refresh periodic job from action=" + action);
PeriodicJobManager.getInstance(context).refreshJob(/*fromBoot=*/ false);
DatabaseUtils.clearExpiredDataIfNeeded(context);

View File

@@ -21,12 +21,13 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.SearchIndexableResource;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
@@ -39,11 +40,13 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/** Advanced power usage. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -55,16 +58,17 @@ public class PowerUsageAdvanced extends PowerUsageBase {
@VisibleForTesting
BatteryHistoryPreference mHistPref;
@VisibleForTesting
Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
@VisibleForTesting
final BatteryHistoryLoaderCallbacks mBatteryHistoryLoaderCallbacks =
new BatteryHistoryLoaderCallbacks();
final BatteryLevelDataLoaderCallbacks mBatteryLevelDataLoaderCallbacks =
new BatteryLevelDataLoaderCallbacks();
private boolean mIsChartDataLoaded = false;
private long mResumeTimestamp;
private BatteryChartPreferenceController mBatteryChartPreferenceController;
private Optional<BatteryLevelData> mBatteryLevelData;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ContentObserver mBatteryObserver =
new ContentObserver(new Handler()) {
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
Log.d(TAG, "onBatteryContentChange: " + selfChange);
@@ -79,6 +83,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
super.onCreate(icicle);
mHistPref = findPreference(KEY_BATTERY_CHART);
setBatteryChartPreferenceController();
AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext()));
}
@Override
@@ -109,6 +114,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
super.onPause();
// Resets the flag to reload usage data in onResume() callback.
mIsChartDataLoaded = false;
mBatteryLevelData = null;
final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
if (uri != null) {
getContext().getContentResolver().unregisterContentObserver(mBatteryObserver);
@@ -118,6 +124,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
@Override
public void onResume() {
super.onResume();
mResumeTimestamp = System.currentTimeMillis();
final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
if (uri != null) {
getContext().getContentResolver().registerContentObserver(
@@ -158,21 +165,9 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return controllers;
}
@Override
protected boolean isBatteryHistoryNeeded() {
return true;
}
@Override
protected void refreshUi(@BatteryUpdateType int refreshType) {
final Context context = getContext();
if (context == null) {
return;
}
updatePreference(mHistPref);
if (mBatteryChartPreferenceController != null && mBatteryHistoryMap != null) {
mBatteryChartPreferenceController.setBatteryHistoryMap(mBatteryHistoryMap);
}
// Do nothing
}
@Override
@@ -181,11 +176,32 @@ public class PowerUsageAdvanced extends PowerUsageBase {
bundle.putInt(KEY_REFRESH_TYPE, refreshType);
if (!mIsChartDataLoaded) {
mIsChartDataLoaded = true;
restartLoader(LoaderIndex.BATTERY_HISTORY_LOADER, bundle,
mBatteryHistoryLoaderCallbacks);
restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle,
mBatteryLevelDataLoaderCallbacks);
}
}
private void onBatteryLevelDataUpdate(BatteryLevelData batteryLevelData) {
mBatteryLevelData = Optional.ofNullable(batteryLevelData);
if (mBatteryChartPreferenceController != null) {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(batteryLevelData);
Log.d(TAG, String.format("Battery chart shows in %d millis",
System.currentTimeMillis() - mResumeTimestamp));
}
}
private void onBatteryDiffDataMapUpdate(Map<Long, BatteryDiffData> batteryDiffDataMap) {
if (mBatteryLevelData != null && mBatteryChartPreferenceController != null) {
Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
DataProcessor.generateBatteryUsageMap(
getContext(), batteryDiffDataMap, mBatteryLevelData.orElse(null));
DataProcessor.loadLabelAndIcon(batteryUsageMap);
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(batteryUsageMap);
}
Log.d(TAG, String.format("Battery usage list shows in %d millis",
System.currentTimeMillis() - mResumeTimestamp));
}
private void setBatteryChartPreferenceController() {
if (mHistPref != null && mBatteryChartPreferenceController != null) {
mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
@@ -216,28 +232,31 @@ public class PowerUsageAdvanced extends PowerUsageBase {
}
};
private class BatteryHistoryLoaderCallbacks
implements LoaderManager.LoaderCallbacks<Map<Long, Map<String, BatteryHistEntry>>> {
private int mRefreshType;
private class BatteryLevelDataLoaderCallbacks
implements LoaderManager.LoaderCallbacks<BatteryLevelData> {
@Override
@NonNull
public Loader<Map<Long, Map<String, BatteryHistEntry>>> onCreateLoader(
int id, Bundle bundle) {
mRefreshType = bundle.getInt(KEY_REFRESH_TYPE);
return new BatteryHistoryLoader(getContext());
public Loader<BatteryLevelData> onCreateLoader(int id, Bundle bundle) {
return new AsyncLoaderCompat<BatteryLevelData>(getContext().getApplicationContext()) {
@Override
protected void onDiscardResult(BatteryLevelData result) {}
@Override
public BatteryLevelData loadInBackground() {
return DataProcessManager.getBatteryLevelData(
getContext(), mHandler, /*isFromPeriodJob=*/ false,
map -> PowerUsageAdvanced.this.onBatteryDiffDataMapUpdate(map));
}
};
}
@Override
public void onLoadFinished(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader,
Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mBatteryHistoryMap = batteryHistoryMap;
PowerUsageAdvanced.this.onLoadFinished(mRefreshType);
public void onLoadFinished(Loader<BatteryLevelData> loader,
BatteryLevelData batteryLevelData) {
PowerUsageAdvanced.this.onBatteryLevelDataUpdate(batteryLevelData);
}
@Override
public void onLoaderReset(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader) {
public void onLoaderReset(Loader<BatteryLevelData> loader) {
}
}
}

View File

@@ -32,7 +32,6 @@ import androidx.loader.content.Loader;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
import com.android.settings.fuelgauge.BatteryUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -63,14 +62,14 @@ public abstract class PowerUsageBase extends DashboardFragment {
LoaderIndex.BATTERY_USAGE_STATS_LOADER,
LoaderIndex.BATTERY_INFO_LOADER,
LoaderIndex.BATTERY_TIP_LOADER,
LoaderIndex.BATTERY_HISTORY_LOADER
LoaderIndex.BATTERY_LEVEL_DATA_LOADER
})
public @interface LoaderIndex {
int BATTERY_USAGE_STATS_LOADER = 0;
int BATTERY_INFO_LOADER = 1;
int BATTERY_TIP_LOADER = 2;
int BATTERY_HISTORY_LOADER = 3;
int BATTERY_LEVEL_DATA_LOADER = 3;
}
@Override
@@ -108,7 +107,7 @@ public abstract class PowerUsageBase extends DashboardFragment {
protected void restartBatteryStatsLoader(int refreshType) {
final Bundle bundle = new Bundle();
bundle.putInt(KEY_REFRESH_TYPE, refreshType);
bundle.putBoolean(KEY_INCLUDE_HISTORY, isBatteryHistoryNeeded());
bundle.putBoolean(KEY_INCLUDE_HISTORY, false);
restartLoader(LoaderIndex.BATTERY_USAGE_STATS_LOADER, bundle,
mBatteryUsageStatsLoaderCallbacks);
}
@@ -137,14 +136,6 @@ public abstract class PowerUsageBase extends DashboardFragment {
protected abstract void refreshUi(@BatteryUpdateType int refreshType);
protected abstract boolean isBatteryHistoryNeeded();
protected void updatePreference(BatteryHistoryPreference historyPref) {
final long startTime = System.currentTimeMillis();
historyPref.setBatteryUsageStats(mBatteryUsageStats);
BatteryUtils.logRuntime(TAG, "updatePreference", startTime);
}
private class BatteryUsageStatsLoaderCallbacks
implements LoaderManager.LoaderCallbacks<BatteryUsageStats> {
private int mRefreshType;

View File

@@ -45,7 +45,6 @@ import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
@@ -69,8 +68,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
LayoutPreference mBatteryLayoutPref;
@VisibleForTesting
BatteryInfo mBatteryInfo;
@VisibleForTesting
@@ -208,11 +205,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
return R.string.help_url_battery;
}
@Override
protected boolean isBatteryHistoryNeeded() {
return false;
}
protected void refreshUi(@BatteryUpdateType int refreshType) {
final Context context = getContext();
if (context == null) {
@@ -239,11 +231,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
restartLoader(LoaderIndex.BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
}
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting
void initFeatureProvider() {
final Context context = getContext();

View File

@@ -36,9 +36,16 @@ public interface BatteryEventDao {
@Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC")
List<BatteryEventEntity> getAll();
/** Gets the {@link Cursor} of the last full charge time . */
@Query("SELECT MAX(timestamp) FROM BatteryEventEntity"
+ " WHERE batteryEventType = 3") // BatteryEventType.FULL_CHARGED = 3
Cursor getLastFullChargeTimestamp();
/** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
Cursor getAllAfter(long timestamp);
@Query("SELECT * FROM BatteryEventEntity"
+ " WHERE timestamp > :timestamp AND batteryEventType IN (:batteryEventTypes)"
+ " ORDER BY timestamp DESC")
Cursor getAllAfter(long timestamp, List<Integer> batteryEventTypes);
/** Deletes all recorded data before a specific timestamp. */
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")

View File

@@ -37,16 +37,18 @@ public interface BatteryStateDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<BatteryState> states);
/** Gets the {@link Cursor} of the latest record timestamp no later than the given timestamp. */
@Query("SELECT MAX(timestamp) FROM BatteryState WHERE timestamp <= :timestamp")
Cursor getLatestTimestampBefore(long timestamp);
/** Lists all recorded battery states after a specific timestamp. */
@Query("SELECT * FROM BatteryState WHERE timestamp >= :timestamp ORDER BY timestamp ASC")
Cursor getBatteryStatesAfter(long timestamp);
/** Lists all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC")
List<BatteryState> getAllAfter(long timestamp);
/** Gets the {@link Cursor} of all recorded data since last full charge within 7 days. */
@Query("SELECT * FROM BatteryState WHERE timestamp >= :timestampSixDaysAgo AND timestamp >= "
+ "(SELECT IFNULL((SELECT MAX(timestamp) FROM BatteryState "
+ "WHERE isFullChargeCycleStart = 1), 0)) ORDER BY timestamp ASC")
Cursor getCursorSinceLastFullCharge(long timestampSixDaysAgo);
/** Get the count of distinct timestamp after a specific timestamp. */
@Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp")
int getDistinctTimestampCount(long timestamp);

View File

@@ -25,7 +25,8 @@ import androidx.room.RoomDatabase;
/** A {@link RoomDatabase} for battery usage states history. */
@Database(
entities = {AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class},
entities = {AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class,
BatteryUsageSlotEntity.class},
version = 1)
public abstract class BatteryStateDatabase extends RoomDatabase {
private static final String TAG = "BatteryStateDatabase";
@@ -38,13 +39,15 @@ public abstract class BatteryStateDatabase extends RoomDatabase {
public abstract BatteryEventDao batteryEventDao();
/** Provides DAO for battery state table. */
public abstract BatteryStateDao batteryStateDao();
/** Provides DAO for battery usage slot table. */
public abstract BatteryUsageSlotDao batteryUsageSlotDao();
/** Gets or creates an instance of {@link RoomDatabase}. */
public static BatteryStateDatabase getInstance(Context context) {
if (sBatteryStateDatabase == null) {
sBatteryStateDatabase =
Room.databaseBuilder(
context, BatteryStateDatabase.class, "battery-usage-db-v8")
context, BatteryStateDatabase.class, "battery-usage-db-v9")
// Allows accessing data in the main thread for dumping bugreport.
.allowMainThreadQueries()
.fallbackToDestructiveMigration()

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import android.database.Cursor;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
/** Data access object for accessing {@link BatteryUsageSlotEntity} in the database. */
@Dao
public interface BatteryUsageSlotDao {
/** Inserts a {@link BatteryUsageSlotEntity} data into the database. */
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(BatteryUsageSlotEntity event);
/** Gets all recorded data. */
@Query("SELECT * FROM BatteryUsageSlotEntity ORDER BY timestamp ASC")
List<BatteryUsageSlotEntity> getAll();
/** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryUsageSlotEntity WHERE timestamp >= :timestamp"
+ " ORDER BY timestamp ASC")
Cursor getAllAfter(long timestamp);
/** Deletes all recorded data before a specific timestamp. */
@Query("DELETE FROM BatteryUsageSlotEntity WHERE timestamp <= :timestamp")
void clearAllBefore(long timestamp);
/** Clears all recorded data in the database. */
@Query("DELETE FROM BatteryUsageSlotEntity")
void clearAll();
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import android.content.ContentValues;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.Locale;
/** A {@link Entity} class to save battery usage slot into database. */
@Entity
public class BatteryUsageSlotEntity {
/** Keys for accessing {@link ContentValues}. */
public static final String KEY_TIMESTAMP = "timestamp";
public static final String KEY_BATTERY_USAGE_SLOT = "batteryUsageSlot";
@PrimaryKey(autoGenerate = true)
private long mId;
public final long timestamp;
public final String batteryUsageSlot;
public BatteryUsageSlotEntity(final long timestamp, final String batteryUsageSlot) {
this.timestamp = timestamp;
this.batteryUsageSlot = batteryUsageSlot;
}
/** Sets the auto-generated content ID. */
public void setId(long id) {
this.mId = id;
}
/** Gets the auto-generated content ID. */
public long getId() {
return mId;
}
@Override
public String toString() {
final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(timestamp);
final StringBuilder builder = new StringBuilder()
.append("\nBatteryUsageSlot{")
.append(String.format(Locale.US, "\n\ttimestamp=%s|batteryUsageSlot=%s",
recordAtDateTime, batteryUsageSlot))
.append("\n}");
return builder.toString();
}
/** Creates new {@link BatteryUsageSlotEntity} from {@link ContentValues}. */
public static BatteryUsageSlotEntity create(ContentValues contentValues) {
Builder builder = BatteryUsageSlotEntity.newBuilder();
if (contentValues.containsKey(KEY_TIMESTAMP)) {
builder.setTimestamp(contentValues.getAsLong(KEY_TIMESTAMP));
}
if (contentValues.containsKey(KEY_BATTERY_USAGE_SLOT)) {
builder.setBatteryUsageSlot(contentValues.getAsString(KEY_BATTERY_USAGE_SLOT));
}
return builder.build();
}
/** Creates a new {@link Builder} instance. */
public static Builder newBuilder() {
return new Builder();
}
/** A convenience builder class to improve readability. */
public static class Builder {
private long mTimestamp;
private String mBatteryUsageSlot;
/** Sets the timestamp. */
@CanIgnoreReturnValue
public Builder setTimestamp(final long timestamp) {
mTimestamp = timestamp;
return this;
}
/** Sets the battery usage slot. */
@CanIgnoreReturnValue
public Builder setBatteryUsageSlot(final String batteryUsageSlot) {
mBatteryUsageSlot = batteryUsageSlot;
return this;
}
/** Builds the {@link BatteryUsageSlotEntity}. */
public BatteryUsageSlotEntity build() {
return new BatteryUsageSlotEntity(mTimestamp, mBatteryUsageSlot);
}
private Builder() {}
}
}

View File

@@ -23,6 +23,14 @@ java_library {
srcs: ["battery_event.proto"],
}
java_library {
name: "battery-usage-slot-protos-lite",
proto: {
type: "lite",
},
srcs: ["battery_usage_slot.proto"],
}
java_library {
name: "fuelgauge-usage-state-protos-lite",
proto: {

View File

@@ -8,6 +8,8 @@ enum BatteryEventType {
UNKNOWN_EVENT = 0;
POWER_CONNECTED = 1;
POWER_DISCONNECTED = 2;
FULL_CHARGED = 3;
EVEN_HOUR = 4;
}
message BatteryEvent {

View File

@@ -0,0 +1,32 @@
syntax = "proto2";
option java_multiple_files = true;
option java_package = "com.android.settings.fuelgauge.batteryusage";
option java_outer_classname = "BatteryUsageSlotProto";
message BatteryUsageSlot {
optional int64 start_timestamp = 1;
optional int64 end_timestamp = 2;
optional int32 start_battery_level = 3;
optional int32 end_battery_level = 4;
optional int64 screen_on_time = 5;
repeated BatteryUsageDiff app_usage = 6;
repeated BatteryUsageDiff system_usage = 7;
}
message BatteryUsageDiff {
optional int64 uid = 1;
optional int64 user_id = 2;
optional string package_name = 3;
optional string label = 4;
optional string key = 5;
optional bool is_hidden = 6;
optional int32 component_id = 7;
optional int32 consumer_type = 8;
optional double consume_power = 9;
optional double foreground_usage_consume_power = 10;
optional double background_usage_consume_power = 11;
optional int64 foreground_usage_time = 12;
optional int64 background_usage_time = 13;
optional int64 screen_on_time = 14;
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.UserManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public final class AppUsageDataLoaderTest {
private Context mContext;
@Mock
private ContentResolver mMockContentResolver;
@Mock
private UserManager mUserManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
doReturn(mContext).when(mContext).getApplicationContext();
doReturn(mMockContentResolver).when(mContext).getContentResolver();
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
}
@Test
public void loadAppUsageData_withData_insertFakeDataIntoProvider() {
final List<AppUsageEvent> AppUsageEventList = new ArrayList<>();
final AppUsageEvent appUsageEvent = AppUsageEvent.newBuilder().setUid(0).build();
AppUsageEventList.add(appUsageEvent);
AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
AppUsageDataLoader.sFakeUsageEventsListSupplier = () -> AppUsageEventList;
AppUsageDataLoader.loadAppUsageData(mContext);
verify(mMockContentResolver).bulkInsert(any(), any());
verify(mMockContentResolver).notifyChange(any(), any());
}
@Test
public void loadAppUsageData_nullAppUsageEvents_notInsertDataIntoProvider() {
AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> null;
AppUsageDataLoader.loadAppUsageData(mContext);
verifyNoMoreInteractions(mMockContentResolver);
}
@Test
public void loadAppUsageData_nullUsageEventsList_notInsertDataIntoProvider() {
AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
AppUsageDataLoader.sFakeUsageEventsListSupplier = () -> null;
AppUsageDataLoader.loadAppUsageData(mContext);
verifyNoMoreInteractions(mMockContentResolver);
}
@Test
public void loadAppUsageData_emptyUsageEventsList_notInsertDataIntoProvider() {
AppUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
AppUsageDataLoader.sFakeUsageEventsListSupplier = () -> new ArrayList<>();
AppUsageDataLoader.loadAppUsageData(mContext);
verifyNoMoreInteractions(mMockContentResolver);
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_ALL;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -30,7 +32,6 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -39,6 +40,7 @@ import android.os.Bundle;
import android.os.LocaleList;
import android.os.UserManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.LinearLayout;
@@ -54,7 +56,6 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -112,6 +113,7 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mPrefContext = mContext;
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
BatteryDiffEntry.clearCache();
// Adds fake testing data.
BatteryDiffEntry.sResourceCache.put(
"fakeBatteryDiffEntryKey",
@@ -144,7 +146,7 @@ public final class BatteryChartPreferenceControllerTest {
reset(mHourlyChartView);
setupHourlyChartViewAnimationMock();
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
// Ignore fast refresh ui from the data processor callback.
@@ -176,16 +178,18 @@ public final class BatteryChartPreferenceControllerTest {
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f);
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
verify(mDailyChartView, atLeastOnce()).setViewModel(expectedDailyViewModel);
reset(mDailyChartView);
reset(mHourlyChartView);
setupHourlyChartViewAnimationMock();
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
doReturn(View.GONE).when(mHourlyChartView).getVisibility();
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.refreshUi();
verify(mDailyChartView).setVisibility(View.VISIBLE);
@@ -245,8 +249,7 @@ public final class BatteryChartPreferenceControllerTest {
setupHourlyChartViewAnimationMock();
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
mBatteryChartPreferenceController.mDailyChartIndex = 2;
mBatteryChartPreferenceController.mHourlyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.refreshUi();
verify(mDailyChartView).setVisibility(View.VISIBLE);
verify(mViewPropertyAnimator, atLeastOnce()).alpha(1f);
@@ -272,13 +275,15 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void refreshUi_normalCase_returnTrue() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
}
@Test
public void refreshUi_batteryIndexedMapIsNull_returnTrue() {
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(null);
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
}
@@ -296,38 +301,34 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.mDailyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
}
@Test
public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
}
@Test
public void selectedSlotText_selectADayAllHours_onlyDayText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday");
}
@Test
public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 2;
@@ -337,7 +338,7 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void selectedSlotText_SelectADayAnHour_dayAndHourText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = 8;
@@ -347,7 +348,7 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void selectedSlotText_selectFirstSlot_withMinuteText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0;
@@ -357,7 +358,7 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void selectedSlotText_selectLastSlot_withNowText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 3;
@@ -367,7 +368,7 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void selectedSlotText_selectOnlySlot_withMinuteAndNowText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(1));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(1));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0;
@@ -388,7 +389,7 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
mBatteryChartPreferenceController.onCreate(bundle);
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(25));
assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
.isEqualTo(expectedDailyIndex);
@@ -398,9 +399,7 @@ public final class BatteryChartPreferenceControllerTest {
@Test
public void getTotalHours_getExpectedResult() {
Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = createBatteryHistoryMap(60);
BatteryLevelData batteryLevelData =
DataProcessManager.getBatteryLevelData(mContext, null, batteryHistoryMap, null);
BatteryLevelData batteryLevelData = createBatteryLevelData(60);
final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData);
@@ -413,37 +412,26 @@ public final class BatteryChartPreferenceControllerTest {
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
}
private static Map<Long, Map<String, BatteryHistEntry>> createBatteryHistoryMap(
int numOfHours) {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
for (int index = 0; index < numOfHours; index++) {
final ContentValues values = new ContentValues();
final DeviceBatteryState deviceBatteryState =
DeviceBatteryState
.newBuilder()
.setBatteryLevel(100 - index)
.build();
final BatteryInformation batteryInformation =
BatteryInformation
.newBuilder()
.setDeviceBatteryState(deviceBatteryState)
.setConsumePower(100 - index)
.build();
values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
ConvertUtils.convertBatteryInformationToString(batteryInformation));
values.put(BatteryHistEntry.KEY_PACKAGE_NAME, "package" + index);
final BatteryHistEntry entry = new BatteryHistEntry(values);
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
entryMap.put("fake_entry_key" + index, entry);
long timestamp = generateTimestamp(index);
private static BatteryLevelData createBatteryLevelData(int numOfHours) {
Map<Long, Integer> batteryLevelMap = new ArrayMap<>();
for (int index = 0; index < numOfHours; index += 2) {
final Integer level = 100 - index;
Long timestamp = generateTimestamp(index);
if (index == 0) {
timestamp += DateUtils.MINUTE_IN_MILLIS;
index--;
}
batteryHistoryMap.put(timestamp, entryMap);
batteryLevelMap.put(timestamp, level);
}
DataProcessor.sTestCurrentTimeMillis =
generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2;
return batteryHistoryMap;
long current = generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2;
batteryLevelMap.put(current, 66);
DataProcessor.sTestCurrentTimeMillis = current;
return new BatteryLevelData(batteryLevelMap);
}
private static Map<Integer, Map<Integer, BatteryDiffData>> getEmptyBatteryUsageMap() {
return Map.of(SELECTED_INDEX_ALL, Map.of(SELECTED_INDEX_ALL, new BatteryDiffData(
null, 0, 0, 0, 0, 0, List.of(), List.of(), Set.of(), Set.of(), false)));
}
private BatteryChartPreferenceController createController() {

View File

@@ -147,6 +147,14 @@ public class BatteryDiffDataTest {
/*foregroundUsageTimeInMs=*/ 0L, /*backgroundUsageTimeInMs=*/ 0L, isHidden);
return new BatteryDiffEntry(
context,
batteryHistEntry.mUid,
batteryHistEntry.mUserId,
batteryHistEntry.getKey(),
batteryHistEntry.mIsHidden,
batteryHistEntry.mDrainType,
batteryHistEntry.mPackageName,
batteryHistEntry.mAppLabel,
batteryHistEntry.mConsumerType,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
/*screenOnTimeInMs=*/ 0,
@@ -154,8 +162,7 @@ public class BatteryDiffDataTest {
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0,
batteryHistEntry);
/*cachedUsageConsumePower=*/ 0);
}
private static BatteryHistEntry createBatteryHistEntry(

View File

@@ -95,6 +95,14 @@ public final class BatteryDiffEntryTest {
final BatteryDiffEntry entry =
new BatteryDiffEntry(
mContext,
/*uid=*/ 0,
/*userId=*/ 0,
/*key=*/ "key",
/*isHidden=*/ false,
/*componentId=*/ -1,
/*legacyPackageName=*/ null,
/*legacyLabel=*/ null,
/*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 10001L,
/*backgroundUsageTimeInMs=*/ 20002L,
/*screenOnTimeInMs=*/ 30003L,
@@ -102,8 +110,7 @@ public final class BatteryDiffEntryTest {
/*foregroundUsageConsumePower=*/ 10.0,
/*foregroundServiceUsageConsumePower=*/ 10.0,
/*backgroundUsageConsumePower=*/ 1.0,
/*cachedUsageConsumePower=*/ 1.0,
/*batteryHistEntry=*/ null);
/*cachedUsageConsumePower=*/ 1.0);
entry.setTotalConsumePower(100.0);
assertThat(entry.getPercentage()).isEqualTo(22.0);
@@ -114,6 +121,14 @@ public final class BatteryDiffEntryTest {
final BatteryDiffEntry entry =
new BatteryDiffEntry(
mContext,
/*uid=*/ 0,
/*userId=*/ 0,
/*key=*/ "key",
/*isHidden=*/ false,
/*componentId=*/ -1,
/*legacyPackageName=*/ null,
/*legacyLabel=*/ null,
/*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 10001L,
/*backgroundUsageTimeInMs=*/ 20002L,
/*screenOnTimeInMs=*/ 30003L,
@@ -121,8 +136,7 @@ public final class BatteryDiffEntryTest {
/*foregroundUsageConsumePower=*/ 10.0,
/*foregroundServiceUsageConsumePower=*/ 10.0,
/*backgroundUsageConsumePower=*/ 1.0,
/*cachedUsageConsumePower=*/ 1.0,
/*batteryHistEntry=*/ null);
/*cachedUsageConsumePower=*/ 1.0);
entry.setTotalConsumePower(0);
assertThat(entry.getPercentage()).isEqualTo(0);
@@ -133,7 +147,24 @@ public final class BatteryDiffEntryTest {
final List<BatteryDiffEntry> entryList = new ArrayList<>();
// Generates fake testing data.
BatteryDiffEntry systemAppsBatteryDiffEntry =
new BatteryDiffEntry.SystemAppsBatteryDiffEntry(mContext);
new BatteryDiffEntry(
mContext,
/*uid=*/ 0,
/*userId=*/ 0,
/*key=*/ BatteryDiffEntry.SYSTEM_APPS_KEY,
/*isHidden=*/ false,
/*componentId=*/ -1,
/*legacyPackageName=*/ null,
/*legacyLabel=*/ BatteryDiffEntry.SYSTEM_APPS_KEY,
/*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
/*screenOnTimeInMs=*/ 0,
/*consumePower=*/ 0,
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0);
systemAppsBatteryDiffEntry.mConsumePower = 16;
systemAppsBatteryDiffEntry.setTotalConsumePower(100);
entryList.add(systemAppsBatteryDiffEntry);
@@ -448,17 +479,16 @@ public final class BatteryDiffEntryTest {
private BatteryDiffEntry createBatteryDiffEntry(
int consumerType, long uid, boolean isHidden) {
final ContentValues values = getContentValuesWithType(consumerType);
final BatteryInformation batteryInformation =
BatteryInformation
.newBuilder()
.setIsHidden(isHidden)
.build();
values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
ConvertUtils.convertBatteryInformationToString(batteryInformation));
values.put(BatteryHistEntry.KEY_UID, uid);
return new BatteryDiffEntry(
mContext,
/*uid=*/ uid,
/*userId=*/ 0,
/*key=*/ "key",
/*isHidden=*/ isHidden,
/*componentId=*/ -1,
/*legacyPackageName=*/ null,
/*legacyLabel=*/ null,
/*consumerType*/ consumerType,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
/*screenOnTimeInMs=*/ 0,
@@ -466,14 +496,21 @@ public final class BatteryDiffEntryTest {
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0,
new BatteryHistEntry(values));
/*cachedUsageConsumePower=*/ 0);
}
private BatteryDiffEntry createBatteryDiffEntry(
double consumePower, BatteryHistEntry batteryHistEntry) {
final BatteryDiffEntry entry = new BatteryDiffEntry(
mContext,
batteryHistEntry.mUid,
batteryHistEntry.mUserId,
batteryHistEntry.getKey(),
batteryHistEntry.mIsHidden,
batteryHistEntry.mDrainType,
batteryHistEntry.mPackageName,
batteryHistEntry.mAppLabel,
batteryHistEntry.mConsumerType,
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0,
/*screenOnTimeInMs=*/ 0,
@@ -481,8 +518,7 @@ public final class BatteryDiffEntryTest {
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0,
batteryHistEntry);
/*cachedUsageConsumePower=*/ 0);
entry.setTotalConsumePower(100.0);
return entry;
}

View File

@@ -15,6 +15,10 @@
*/
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isSystemConsumer;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isUidConsumer;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.isUserConsumer;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -147,32 +151,32 @@ public final class BatteryHistEntryTest {
@Test
public void testIsAppEntry_returnExpectedResult() {
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).isAppEntry())
.isFalse();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).isAppEntry())
.isFalse();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).isAppEntry())
.isTrue();
assertThat(isUidConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).mConsumerType)).isFalse();
assertThat(isUidConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).mConsumerType)).isFalse();
assertThat(isUidConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).mConsumerType)).isTrue();
}
@Test
public void testIsUserEntry_returnExpectedResult() {
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).isUserEntry())
.isFalse();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).isUserEntry())
.isTrue();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).isUserEntry())
.isFalse();
assertThat(isUserConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).mConsumerType)).isFalse();
assertThat(isUserConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).mConsumerType)).isTrue();
assertThat(isUserConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).mConsumerType)).isFalse();
}
@Test
public void testIsSystemEntry_returnExpectedResult() {
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).isSystemEntry())
.isTrue();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).isSystemEntry())
.isFalse();
assertThat(createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).isSystemEntry())
.isFalse();
assertThat(isSystemConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY).mConsumerType)).isTrue();
assertThat(isSystemConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_USER_BATTERY).mConsumerType)).isFalse();
assertThat(isSystemConsumer(
createEntry(ConvertUtils.CONSUMER_TYPE_UID_BATTERY).mConsumerType)).isFalse();
}
@Test

View File

@@ -27,7 +27,6 @@ import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryInfo;
import org.junit.Before;
import org.junit.Test;
@@ -42,8 +41,6 @@ public class BatteryHistoryPreferenceTest {
@Mock
private PreferenceViewHolder mViewHolder;
@Mock
private BatteryInfo mBatteryInfo;
@Mock
private TextView mTextView;
@Mock
private BatteryChartView mDailyChartView;
@@ -59,7 +56,6 @@ public class BatteryHistoryPreferenceTest {
LayoutInflater.from(context).inflate(R.layout.battery_chart_graph, null);
mBatteryHistoryPreference = new BatteryHistoryPreference(context, null);
mBatteryHistoryPreference.mBatteryInfo = mBatteryInfo;
mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(itemView));
when(mViewHolder.findViewById(R.id.daily_battery_chart)).thenReturn(mDailyChartView);
when(mViewHolder.findViewById(R.id.hourly_battery_chart)).thenReturn(mHourlyChartView);

View File

@@ -0,0 +1,214 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
public class BatteryLevelDataTest {
@Before
public void setUp() {
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
}
@Test
public void getDailyTimestamps_allDataInOneHour_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640970006000L, // 2022-01-01 01:00:06
1640973608000L // 2022-01-01 01:00:08
);
final List<Long> expectedTimestamps = List.of(
1640970006000L, // 2022-01-01 01:00:06
1640973608000L // 2022-01-01 01:00:08
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_OneHourDataPerDay_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L // 2022-01-02 01:00:00
);
final List<Long> expectedTimestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L // 2022-01-02 01:00:00
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_OneDayData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
1640970000000L, // 2022-01-01 01:00:00
1640973600000L, // 2022-01-01 02:00:00
1640977200000L, // 2022-01-01 03:00:00
1640980800000L // 2022-01-01 04:00:00
);
final List<Long> expectedTimestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
1640980800000L // 2022-01-01 04:00:00
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_MultipleDaysData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641045600000L, // 2022-01-01 22:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641232800000L // 2022-01-04 02:00:00
);
final List<Long> expectedTimestamps = List.of(
1641045600000L, // 2022-01-01 22:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641232800000L // 2022-01-04 02:00:00
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_FirstDayOneHourData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641254400000L // 2022-01-04 08:00:00
);
final List<Long> expectedTimestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641254400000L // 2022-01-04 08:00:00
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_LastDayNoData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641225600000L // 2022-01-04 00:00:00
);
final List<Long> expectedTimestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L // 2022-01-04 00:00:00
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_LastDayOneHourData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641229200000L // 2022-01-04 01:00:00
);
final List<Long> expectedTimestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641229200000L // 2022-01-04 01:00:00
);
assertThat(BatteryLevelData.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void combine_normalFlow_returnExpectedResult() {
final BatteryLevelData batteryLevelData =
new BatteryLevelData(Map.of(1691596800000L, 90, 1691604000000L, 80));
final List<BatteryEvent> batteryLevelRecordEvents = List.of(
BatteryEvent.newBuilder().setTimestamp(1691586000166L).setBatteryLevel(100)
.setType(BatteryEventType.FULL_CHARGED).build(),
BatteryEvent.newBuilder().setTimestamp(1691589600000L).setBatteryLevel(98)
.setType(BatteryEventType.EVEN_HOUR).build());
BatteryLevelData result =
BatteryLevelData.combine(batteryLevelData, batteryLevelRecordEvents);
assertThat(result.getDailyBatteryLevels().getTimestamps())
.isEqualTo(List.of(1691586000166L, 1691596800000L, 1691604000000L));
assertThat(result.getDailyBatteryLevels().getLevels())
.isEqualTo(List.of(100, 90, 80));
assertThat(result.getHourlyBatteryLevelsPerDay())
.hasSize(2);
assertThat(result.getHourlyBatteryLevelsPerDay().get(0).getTimestamps())
.isEqualTo(List.of(1691586000166L, 1691589600000L, 1691596800000L));
assertThat(result.getHourlyBatteryLevelsPerDay().get(0).getLevels())
.isEqualTo(List.of(100, 98, 90));
assertThat(result.getHourlyBatteryLevelsPerDay().get(1).getTimestamps())
.isEqualTo(List.of(1691596800000L, 1691604000000L));
assertThat(result.getHourlyBatteryLevelsPerDay().get(1).getLevels())
.isEqualTo(List.of(90, 80));
}
@Test
public void combine_existingBatteryLevelDataIsNull_returnExpectedResult() {
final List<BatteryEvent> batteryLevelRecordEvents = List.of(
BatteryEvent.newBuilder().setTimestamp(1691586000166L).setBatteryLevel(100)
.setType(BatteryEventType.FULL_CHARGED).build(),
BatteryEvent.newBuilder().setTimestamp(1691589600000L).setBatteryLevel(98)
.setType(BatteryEventType.EVEN_HOUR).build());
BatteryLevelData result =
BatteryLevelData.combine(null, batteryLevelRecordEvents);
assertThat(result.getHourlyBatteryLevelsPerDay())
.hasSize(1);
assertThat(result.getHourlyBatteryLevelsPerDay().get(0).getTimestamps())
.isEqualTo(List.of(1691586000166L, 1691589600000L));
assertThat(result.getHourlyBatteryLevelsPerDay().get(0).getLevels())
.isEqualTo(List.of(100, 98));
}
}

View File

@@ -96,6 +96,14 @@ public final class BatteryUsageBreakdownControllerTest {
mBatteryUsageBreakdownController.mAppListPreferenceGroup = mAppListPreferenceGroup;
mBatteryDiffEntry = new BatteryDiffEntry(
mContext,
/*uid=*/ 0L,
/*userId=*/ 0L,
/*key=*/ "key",
/*isHidden=*/ false,
/*componentId=*/ -1,
/*legacyPackageName=*/ null,
/*legacyLabel=*/ null,
/*consumerType=*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 1,
/*backgroundUsageTimeInMs=*/ 2,
/*screenOnTimeInMs=*/ 0,
@@ -103,13 +111,14 @@ public final class BatteryUsageBreakdownControllerTest {
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 1,
/*backgroundUsageConsumePower=*/ 2,
/*cachedUsageConsumePower=*/ 0,
mBatteryHistEntry);
/*cachedUsageConsumePower=*/ 0);
mBatteryDiffEntry = spy(mBatteryDiffEntry);
mBatteryUsageBreakdownController.mBatteryDiffData =
new BatteryDiffData(mContext, /* screenOnTime= */ 0L,
Arrays.asList(mBatteryDiffEntry), Arrays.asList(), Set.of(), Set.of(),
/* isAccumulated= */ false);
new BatteryDiffData(mContext, /* startTimestamp= */ 0L, /* endTimestamp= */ 0L,
/* startBatteryLevel= */ 0, /* endBatteryLevel= */ 0,
/* screenOnTime= */ 0L, Arrays.asList(mBatteryDiffEntry), Arrays.asList(),
Set.of(), Set.of(), /* isAccumulated= */ false);
BatteryDiffEntry.clearCache();
// Adds fake testing data.
BatteryDiffEntry.sResourceCache.put(
"fakeBatteryDiffEntryKey",
@@ -140,7 +149,7 @@ public final class BatteryUsageBreakdownControllerTest {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
doReturn(null).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
doReturn(false).when(mBatteryDiffEntry).validForRestriction();
@@ -168,7 +177,7 @@ public final class BatteryUsageBreakdownControllerTest {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
mBatteryUsageBreakdownController.addAllPreferences();
@@ -197,7 +206,7 @@ public final class BatteryUsageBreakdownControllerTest {
public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
// Ensures the testing data is correct.
@@ -222,7 +231,7 @@ public final class BatteryUsageBreakdownControllerTest {
@Test
public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
doReturn(false).when(mBatteryHistEntry).isAppEntry();
mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
@@ -238,7 +247,7 @@ public final class BatteryUsageBreakdownControllerTest {
@Test
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
doReturn(true).when(mBatteryHistEntry).isAppEntry();
mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
@@ -394,10 +403,23 @@ public final class BatteryUsageBreakdownControllerTest {
contentValues.put(BatteryHistEntry.KEY_USER_ID, Integer.valueOf(1001));
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(contentValues);
return new BatteryDiffEntry(
mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs, screenOnTimeInMs,
/*consumePower=*/ 0, /*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0, /*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0, batteryHistEntry);
mContext,
batteryHistEntry.mUid,
batteryHistEntry.mUserId,
batteryHistEntry.getKey(),
batteryHistEntry.mIsHidden,
batteryHistEntry.mDrainType,
batteryHistEntry.mPackageName,
batteryHistEntry.mAppLabel,
batteryHistEntry.mConsumerType,
foregroundUsageTimeInMs,
backgroundUsageTimeInMs,
screenOnTimeInMs,
/*consumePower=*/ 0,
/*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0,
/*cachedUsageConsumePower=*/ 0);
}
private BatteryUsageBreakdownController createController() {

View File

@@ -20,11 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.app.Application;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -34,6 +32,7 @@ import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
import com.android.settings.testutils.BatteryTestUtils;
import com.android.settings.testutils.FakeClock;
@@ -41,12 +40,10 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** Tests for {@link BatteryUsageContentProvider}. */
@@ -126,11 +123,29 @@ public final class BatteryUsageContentProviderTest {
() -> mProvider.insert(uri, /*contentValues=*/ null));
}
@Test
public void query_getLastFullChargeTimestamp_returnsExpectedResult() throws Exception {
mProvider.onCreate();
ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, 10001L);
values.put(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE,
BatteryEventType.FULL_CHARGED.getNumber());
values.put(BatteryEventEntity.KEY_BATTERY_LEVEL, 100);
mProvider.insert(DatabaseUtils.BATTERY_EVENT_URI, values);
final Cursor cursor = getCursorOfLastFullChargeTimestamp();
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
final long lastFullChargeTimestamp = cursor.getLong(0);
assertThat(lastFullChargeTimestamp).isEqualTo(10001L);
}
@Test
public void query_batteryState_returnsExpectedResult() throws Exception {
mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52);
final long expiredTimeCutoff = currentTime.toMillis() - 3;
final long expiredTimeCutoff = currentTime.toMillis() - 8;
final Cursor cursor = insertBatteryState(currentTime, Long.toString(expiredTimeCutoff));
@@ -150,19 +165,13 @@ public final class BatteryUsageContentProviderTest {
final String actualPackageName3 = cursor.getString(packageNameIndex);
assertThat(actualPackageName3).isEqualTo(PACKAGE_NAME3);
cursor.close();
// Verifies the broadcast intent.
TimeUnit.SECONDS.sleep(1);
final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents();
assertThat(intents).hasSize(1);
assertThat(intents.get(0).getAction()).isEqualTo(
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
}
@Test
public void query_batteryStateTimestamp_returnsExpectedResult() throws Exception {
mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52);
final long expiredTimeCutoff = currentTime.toMillis() - 1;
final long expiredTimeCutoff = currentTime.toMillis() - 2;
final Cursor cursor = insertBatteryState(currentTime, Long.toString(expiredTimeCutoff));
@@ -178,12 +187,25 @@ public final class BatteryUsageContentProviderTest {
final String actualPackageName2 = cursor.getString(packageNameIndex);
assertThat(actualPackageName2).isEqualTo(PACKAGE_NAME3);
cursor.close();
// Verifies the broadcast intent.
TimeUnit.SECONDS.sleep(1);
final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents();
assertThat(intents).hasSize(1);
assertThat(intents.get(0).getAction()).isEqualTo(
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
}
@Test
public void query_getBatteryStateLatestTimestamp_returnsExpectedResult() throws Exception {
mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52);
insertBatteryState(currentTime, Long.toString(currentTime.toMillis()));
final Cursor cursor1 = getCursorOfBatteryStateLatestTimestamp(currentTime.toMillis() - 5);
assertThat(cursor1.getCount()).isEqualTo(1);
cursor1.moveToFirst();
final long latestTimestamp1 = cursor1.getLong(0);
assertThat(latestTimestamp1).isEqualTo(currentTime.toMillis() - 6);
final Cursor cursor2 = getCursorOfBatteryStateLatestTimestamp(currentTime.toMillis() - 2);
assertThat(cursor2.getCount()).isEqualTo(1);
cursor2.moveToFirst();
final long latestTimestamp2 = cursor2.getLong(0);
assertThat(latestTimestamp2).isEqualTo(currentTime.toMillis() - 2);
}
@Test
@@ -355,7 +377,7 @@ public final class BatteryUsageContentProviderTest {
}
@Test
public void insert_batteryEvent_returnsExpectedResult() {
public void insertAndQuery_batteryEvent_returnsExpectedResult() {
mProvider.onCreate();
ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, 10001L);
@@ -366,7 +388,7 @@ public final class BatteryUsageContentProviderTest {
final Uri uri = mProvider.insert(DatabaseUtils.BATTERY_EVENT_URI, values);
assertThat(uri).isEqualTo(DatabaseUtils.BATTERY_EVENT_URI);
// Verifies the AppUsageEventEntity content.
// Verifies the BatteryEventEntity content.
final List<BatteryEventEntity> entities =
BatteryStateDatabase.getInstance(mContext).batteryEventDao().getAll();
assertThat(entities).hasSize(1);
@@ -374,6 +396,50 @@ public final class BatteryUsageContentProviderTest {
assertThat(entities.get(0).batteryEventType).isEqualTo(
BatteryEventType.POWER_CONNECTED.getNumber());
assertThat(entities.get(0).batteryLevel).isEqualTo(66);
final Cursor cursor1 = getCursorOfBatteryEvents(
0L, List.of(BatteryEventType.POWER_CONNECTED.getNumber()));
assertThat(cursor1.getCount()).isEqualTo(1);
cursor1.moveToFirst();
assertThat(cursor1.getLong(cursor1.getColumnIndex(BatteryEventEntity.KEY_TIMESTAMP)))
.isEqualTo(10001L);
assertThat(
cursor1.getInt(cursor1.getColumnIndex(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)))
.isEqualTo(BatteryEventType.POWER_CONNECTED.getNumber());
assertThat(cursor1.getInt(cursor1.getColumnIndex(BatteryEventEntity.KEY_BATTERY_LEVEL)))
.isEqualTo(66);
final Cursor cursor2 = getCursorOfBatteryEvents(
0L, List.of(BatteryEventType.POWER_DISCONNECTED.getNumber()));
assertThat(cursor2.getCount()).isEqualTo(0);
}
@Test
public void insertAndQuery_batteryUsageSlot_returnsExpectedResult() {
mProvider.onCreate();
ContentValues values = new ContentValues();
values.put(BatteryUsageSlotEntity.KEY_TIMESTAMP, 10001L);
values.put(BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT, "TEST_STRING");
final Uri uri = mProvider.insert(DatabaseUtils.BATTERY_USAGE_SLOT_URI, values);
// Verifies the BatteryUsageSlotEntity content.
assertThat(uri).isEqualTo(DatabaseUtils.BATTERY_USAGE_SLOT_URI);
final List<BatteryUsageSlotEntity> entities =
BatteryStateDatabase.getInstance(mContext).batteryUsageSlotDao().getAll();
assertThat(entities).hasSize(1);
assertThat(entities.get(0).timestamp).isEqualTo(10001L);
assertThat(entities.get(0).batteryUsageSlot).isEqualTo("TEST_STRING");
final Cursor cursor1 = getCursorOfBatteryUsageSlots(10001L);
assertThat(cursor1.getCount()).isEqualTo(1);
cursor1.moveToFirst();
assertThat(cursor1.getLong(cursor1.getColumnIndex(BatteryUsageSlotEntity.KEY_TIMESTAMP)))
.isEqualTo(10001L);
assertThat(cursor1.getString(cursor1.getColumnIndex(
BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT))).isEqualTo("TEST_STRING");
final Cursor cursor2 = getCursorOfBatteryUsageSlots(10002L);
assertThat(cursor2.getCount()).isEqualTo(0);
}
@Test
@@ -404,10 +470,10 @@ public final class BatteryUsageContentProviderTest {
final long currentTimestamp = currentTime.toMillis();
// Inserts some valid testing data.
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, currentTimestamp - 2, PACKAGE_NAME1,
mContext, currentTimestamp - 6, PACKAGE_NAME1,
/*isFullChargeStart=*/ true);
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, currentTimestamp - 1, PACKAGE_NAME2);
mContext, currentTimestamp - 2, PACKAGE_NAME2);
BatteryTestUtils.insertDataToBatteryStateTable(
mContext, currentTimestamp, PACKAGE_NAME3);
@@ -420,17 +486,35 @@ public final class BatteryUsageContentProviderTest {
DatabaseUtils.QUERY_KEY_TIMESTAMP, queryTimestamp)
.build();
final Cursor cursor =
mProvider.query(
batteryStateQueryContentUri,
/*strings=*/ null,
/*s=*/ null,
/*strings1=*/ null,
/*s1=*/ null);
final Cursor cursor = query(batteryStateQueryContentUri);
return cursor;
}
private Cursor getCursorOfLastFullChargeTimestamp() {
final Uri lastFullChargeTimestampContentUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(DatabaseUtils.AUTHORITY)
.appendPath(DatabaseUtils.LAST_FULL_CHARGE_TIMESTAMP_PATH)
.build();
return query(lastFullChargeTimestampContentUri);
}
private Cursor getCursorOfBatteryStateLatestTimestamp(final long queryTimestamp) {
final Uri batteryStateLatestTimestampUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(DatabaseUtils.AUTHORITY)
.appendPath(DatabaseUtils.BATTERY_STATE_LATEST_TIMESTAMP_PATH)
.appendQueryParameter(
DatabaseUtils.QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.build();
return query(batteryStateLatestTimestampUri);
}
private void insertAppUsageEvent() {
mProvider.onCreate();
// Inserts some valid testing data.
@@ -452,12 +536,7 @@ public final class BatteryUsageContentProviderTest {
DatabaseUtils.QUERY_KEY_USERID, Long.toString(userId))
.build();
return mProvider.query(
appUsageLatestTimestampQueryContentUri,
/*strings=*/ null,
/*s=*/ null,
/*strings1=*/ null,
/*s1=*/ null);
return query(appUsageLatestTimestampQueryContentUri);
}
private Cursor getCursorOfAppUsage(final List<Long> userIds, final long queryTimestamp) {
@@ -474,7 +553,43 @@ public final class BatteryUsageContentProviderTest {
.appendQueryParameter(DatabaseUtils.QUERY_KEY_USERID, queryUserIdString)
.build();
return query(appUsageEventUri);
}
private Cursor getCursorOfBatteryEvents(
final long queryTimestamp, final List<Integer> batteryEventTypes) {
final String batteryEventTypesString = batteryEventTypes.stream()
.map(type -> String.valueOf(type))
.collect(Collectors.joining(","));
final Uri batteryEventUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(DatabaseUtils.AUTHORITY)
.appendPath(DatabaseUtils.BATTERY_EVENT_TABLE)
.appendQueryParameter(
DatabaseUtils.QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.appendQueryParameter(
DatabaseUtils.QUERY_BATTERY_EVENT_TYPE, batteryEventTypesString)
.build();
return query(batteryEventUri);
}
private Cursor getCursorOfBatteryUsageSlots(final long queryTimestamp) {
final Uri batteryUsageSlotUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(DatabaseUtils.AUTHORITY)
.appendPath(DatabaseUtils.BATTERY_USAGE_SLOT_TABLE)
.appendQueryParameter(
DatabaseUtils.QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
.build();
return query(batteryUsageSlotUri);
}
private Cursor query(Uri uri) {
return mProvider.query(
appUsageEventUri, /*strings=*/ null, /*s=*/ null, /*strings1=*/ null, /*s1=*/ null);
uri, /*strings=*/ null, /*s=*/ null, /*strings1=*/ null, /*s1=*/ null);
}
}

View File

@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
@@ -31,6 +32,7 @@ import android.content.pm.PackageManager;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UserManager;
import org.junit.Before;
import org.junit.Test;
@@ -43,6 +45,7 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -56,6 +59,8 @@ public final class BatteryUsageDataLoaderTest {
@Mock
private PackageManager mPackageManager;
@Mock
private UserManager mUserManager;
@Mock
private BatteryUsageStats mBatteryUsageStats;
@Mock
private BatteryEntry mMockBatteryEntry;
@@ -70,6 +75,7 @@ public final class BatteryUsageDataLoaderTest {
doReturn(mBatteryStatsManager).when(mContext).getSystemService(
Context.BATTERY_STATS_SERVICE);
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(mMockContentResolver).when(mContext).getContentResolver();
doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
}
@@ -82,7 +88,7 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> batteryEntryList;
BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false);
BatteryUsageDataLoader.loadBatteryStatsData(mContext, /*isFullChargeStart=*/ false);
final int queryFlags = mStatsQueryCaptor.getValue().getFlags();
assertThat(queryFlags
@@ -97,7 +103,7 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> null;
BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false);
BatteryUsageDataLoader.loadBatteryStatsData(mContext, /*isFullChargeStart=*/ false);
verify(mMockContentResolver).insert(any(), any());
}
@@ -108,8 +114,51 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> new ArrayList<>();
BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false);
BatteryUsageDataLoader.loadBatteryStatsData(mContext, /*isFullChargeStart=*/ false);
verify(mMockContentResolver).insert(any(), any());
}
@Test
public void loadAppUsageData_withData_insertFakeDataIntoProvider() {
final List<AppUsageEvent> AppUsageEventList = new ArrayList<>();
final AppUsageEvent appUsageEvent = AppUsageEvent.newBuilder().setUid(0).build();
AppUsageEventList.add(appUsageEvent);
BatteryUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
BatteryUsageDataLoader.sFakeUsageEventsListSupplier = () -> AppUsageEventList;
BatteryUsageDataLoader.loadAppUsageData(mContext);
verify(mMockContentResolver).bulkInsert(any(), any());
verify(mMockContentResolver).notifyChange(any(), any());
}
@Test
public void loadAppUsageData_nullAppUsageEvents_notInsertDataIntoProvider() {
BatteryUsageDataLoader.sFakeAppUsageEventsSupplier = () -> null;
BatteryUsageDataLoader.loadAppUsageData(mContext);
verifyNoMoreInteractions(mMockContentResolver);
}
@Test
public void loadAppUsageData_nullUsageEventsList_notInsertDataIntoProvider() {
BatteryUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
BatteryUsageDataLoader.sFakeUsageEventsListSupplier = () -> null;
BatteryUsageDataLoader.loadAppUsageData(mContext);
verifyNoMoreInteractions(mMockContentResolver);
}
@Test
public void loadAppUsageData_emptyUsageEventsList_notInsertDataIntoProvider() {
BatteryUsageDataLoader.sFakeAppUsageEventsSupplier = () -> new HashMap<>();
BatteryUsageDataLoader.sFakeUsageEventsListSupplier = () -> new ArrayList<>();
BatteryUsageDataLoader.loadAppUsageData(mContext);
verifyNoMoreInteractions(mMockContentResolver);
}
}

View File

@@ -39,6 +39,7 @@ import android.os.UserHandle;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +49,10 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
@@ -212,6 +216,22 @@ public final class ConvertUtilsTest {
assertThat(values.getAsInteger(BatteryEventEntity.KEY_BATTERY_LEVEL)).isEqualTo(66);
}
@Test
public void convertBatteryUsageSlotToContentValues_normalCase_returnsExpectedContentValues() {
final BatteryUsageSlot batteryUsageSlot =
BatteryUsageSlot.newBuilder()
.setStartTimestamp(10001L)
.setEndTimestamp(30003L)
.setStartBatteryLevel(88)
.setEndBatteryLevel(66)
.setScreenOnTime(123L)
.build();
final ContentValues values =
ConvertUtils.convertBatteryUsageSlotToContentValues(batteryUsageSlot);
assertThat(values.getAsLong(BatteryUsageSlotEntity.KEY_TIMESTAMP)).isEqualTo(10001L);
assertThat(BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT).isNotEmpty();
}
@Test
public void convertToBatteryHistEntry_returnsExpectedResult() {
final int expectedType = 3;
@@ -363,7 +383,7 @@ public final class ConvertUtilsTest {
}
@Test
public void convertToAppUsageEventFromCursor_returnExpectedResult() {
public void convertToAppUsageEvent_returnExpectedResult() {
final MatrixCursor cursor = new MatrixCursor(
new String[]{
AppUsageEventEntity.KEY_UID,
@@ -384,7 +404,7 @@ public final class ConvertUtilsTest {
100001L});
cursor.moveToFirst();
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEventFromCursor(cursor);
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(cursor);
assertThat(appUsageEvent.getUid()).isEqualTo(101L);
assertThat(appUsageEvent.getUserId()).isEqualTo(1001L);
@@ -396,7 +416,7 @@ public final class ConvertUtilsTest {
}
@Test
public void convertToAppUsageEventFromCursor_emptyInstanceIdAndRootName_returnExpectedResult() {
public void convertToAppUsageEvent_emptyInstanceIdAndRootName_returnExpectedResult() {
final MatrixCursor cursor = new MatrixCursor(
new String[]{
AppUsageEventEntity.KEY_UID,
@@ -413,7 +433,7 @@ public final class ConvertUtilsTest {
AppUsageEventType.DEVICE_SHUTDOWN.getNumber()});
cursor.moveToFirst();
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEventFromCursor(cursor);
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(cursor);
assertThat(appUsageEvent.getUid()).isEqualTo(101L);
assertThat(appUsageEvent.getUserId()).isEqualTo(1001L);
@@ -433,6 +453,42 @@ public final class ConvertUtilsTest {
assertThat(batteryEvent.getBatteryLevel()).isEqualTo(88);
}
@Test
public void convertToBatteryEventList_normalCase_returnsExpectedResult() {
final BatteryLevelData batteryLevelData = new BatteryLevelData(Map.of(
1691589600000L, 98, 1691596800000L, 90, 1691596812345L, 80));
final List<BatteryEvent> batteryEventList =
ConvertUtils.convertToBatteryEventList(batteryLevelData);
assertThat(batteryEventList).hasSize(2);
assertThat(batteryEventList.get(0).getTimestamp()).isEqualTo(1691589600000L);
assertThat(batteryEventList.get(0).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
assertThat(batteryEventList.get(0).getBatteryLevel()).isEqualTo(98);
assertThat(batteryEventList.get(1).getTimestamp()).isEqualTo(1691596800000L);
assertThat(batteryEventList.get(1).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
assertThat(batteryEventList.get(1).getBatteryLevel()).isEqualTo(90);
}
@Test
public void convertToBatteryUsageSlotList_normalCase_returnsExpectedResult() {
BatteryDiffData batteryDiffData1 = new BatteryDiffData(
mContext, 11L, 12L, 13, 14, 15, List.of(), List.of(), Set.of(), Set.of(), false);
BatteryDiffData batteryDiffData2 = new BatteryDiffData(
mContext, 21L, 22L, 23, 24, 25, List.of(), List.of(), Set.of(), Set.of(), false);
BatteryDiffData batteryDiffData3 = new BatteryDiffData(
mContext, 31L, 32L, 33, 34, 35, List.of(), List.of(), Set.of(), Set.of(), false);
final Map<Long, BatteryDiffData> batteryDiffDataMap = Map.of(
11L, batteryDiffData1, 21L, batteryDiffData2, 31L, batteryDiffData3);
final List<BatteryUsageSlot> batteryUsageSlotList =
ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap);
assertThat(batteryUsageSlotList).hasSize(3);
assertThat(batteryUsageSlotList.stream().map((s) -> s.getScreenOnTime()).sorted().toList())
.isEqualTo(List.of(15L, 25L, 35L));
}
@Test
public void getLocale_nullContext_returnDefaultLocale() {
assertThat(ConvertUtils.getLocale(/*context=*/ null))

View File

@@ -30,8 +30,12 @@ import android.app.usage.UsageEvents;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.BatteryManager;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserManager;
@@ -39,9 +43,12 @@ import android.text.format.DateUtils;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -52,6 +59,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@RunWith(RobolectricTestRunner.class)
public final class DataProcessManagerTest {
@@ -65,7 +73,13 @@ public final class DataProcessManagerTest {
@Mock
private UserManager mUserManager;
@Mock
private BatteryStatsManager mBatteryStatsManager;
@Mock
private BatteryUsageStats mBatteryUsageStats;
@Mock
private Intent mIntent;
@Captor
private ArgumentCaptor<BatteryUsageStatsQuery> mBatteryUsageStatsQueryCaptor;
@Before
public void setUp() {
@@ -77,22 +91,32 @@ public final class DataProcessManagerTest {
doReturn(mUserManager)
.when(mContext)
.getSystemService(UserManager.class);
doReturn(mBatteryStatsManager).when(mContext).getSystemService(
Context.BATTERY_STATS_SERVICE);
doReturn(mBatteryUsageStats).when(
mBatteryStatsManager).getBatteryUsageStats(mBatteryUsageStatsQueryCaptor.capture());
doReturn(mIntent).when(mContext).registerReceiver(any(), any());
doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());
doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt());
mDataProcessManager = new DataProcessManager(
mContext, /*handler=*/ null, /*rawStartTimestamp=*/ 0L,
/*callbackFunction=*/ null, /*hourlyBatteryLevelsPerDay=*/ new ArrayList<>(),
/*lastFullChargeTimestamp=*/ 0L, /*callbackFunction=*/ null,
/*hourlyBatteryLevelsPerDay=*/ new ArrayList<>(),
/*batteryHistoryMap=*/ new HashMap<>());
}
@After
public void cleanUp() {
DatabaseUtils.sFakeSupplier = null;
DataProcessManager.sFakeBatteryHistoryMap = null;
}
@Test
public void constructor_noLevelData() {
final DataProcessManager dataProcessManager =
new DataProcessManager(mContext, /*handler=*/ null, /*callbackFunction=*/ null);
assertThat(dataProcessManager.getShowScreenOnTime()).isFalse();
assertThat(dataProcessManager.getShowBatteryLevel()).isFalse();
}
@Test
@@ -122,16 +146,18 @@ public final class DataProcessManagerTest {
final String packageName = "package";
// Adds the day 1 data.
final List<Long> timestamps1 = List.of(2L, 3L, 4L);
final List<Integer> levels1 = List.of(100, 100, 100);
final Map<Long, Integer> batteryLevelMap1 =
Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps1, levels1));
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1));
// Adds the day 2 data.
hourlyBatteryLevelsPerDay.add(null);
// Adds the day 3 data.
final List<Long> timestamps2 = List.of(5L, 6L);
final List<Integer> levels2 = List.of(100, 100);
final Map<Long, Integer> batteryLevelMap2 =
Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps2, levels2));
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2));
// Fake current usage data.
final UsageEvents.Event event1 =
getUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, /*timestamp=*/ 1, packageName);
@@ -171,10 +197,18 @@ public final class DataProcessManagerTest {
cursor.addRow(new Object[] {
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 6, /*userId=*/ 1,
/*instanceId=*/ 2, packageName});
DatabaseUtils.sFakeSupplier = () -> cursor;
DatabaseUtils.sFakeSupplier = new Supplier<>() {
private int mTimes = 0;
@Override
public Cursor get() {
mTimes++;
return mTimes <= 2 ? null : cursor;
}
};
final DataProcessManager dataProcessManager = new DataProcessManager(
mContext, /*handler=*/ null, /*rawStartTimestamp=*/ 2L, /*callbackFunction=*/ null,
mContext, /*handler=*/ null, /*rawStartTimestamp=*/ 2L,
/*lastFullChargeTimestamp=*/ 1L, /*callbackFunction=*/ null,
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ new HashMap<>());
dataProcessManager.start();
@@ -254,12 +288,13 @@ public final class DataProcessManagerTest {
assertThat(DataProcessManager.getBatteryLevelData(
mContext,
/*handler=*/ null,
/*batteryHistoryMap=*/ null,
/*asyncResponseDelegate=*/ null))
.isNull();
/*isFromPeriodJob=*/ false,
/*asyncResponseDelegate=*/ null)).isNull();
assertThat(DataProcessManager.getBatteryLevelData(
mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
.isNull();
mContext,
/*handler=*/ null,
/*isFromPeriodJob=*/ true,
/*asyncResponseDelegate=*/ null)).isNull();
}
@Test
@@ -270,18 +305,16 @@ public final class DataProcessManagerTest {
DateUtils.HOUR_IN_MILLIS * 2 - 200L,
DateUtils.HOUR_IN_MILLIS * 2 - 100L};
final int[] levels = {100, 99, 98};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
DataProcessManager.sFakeBatteryHistoryMap = createHistoryMap(timestamps, levels);
DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
final BatteryLevelData resultData =
DataProcessManager.getBatteryLevelData(
mContext,
/*handler=*/ null,
batteryHistoryMap,
/*isFromPeriodJob=*/ false,
/*asyncResponseDelegate=*/ null);
final List<Long> expectedDailyTimestamps = List.of(
DateUtils.HOUR_IN_MILLIS * 2 - 300L,
DateUtils.HOUR_IN_MILLIS * 2 - 100L);
@@ -301,15 +334,14 @@ public final class DataProcessManagerTest {
// Timezone GMT+8: 2022-01-01 00:00:00, 2022-01-01 01:00:00
final long[] timestamps = {1640966400000L, 1640970000000L};
final int[] levels = {100, 99};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
DataProcessManager.sFakeBatteryHistoryMap = createHistoryMap(timestamps, levels);
DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
final BatteryLevelData resultData =
DataProcessManager.getBatteryLevelData(
mContext,
/*handler=*/ null,
batteryHistoryMap,
/*isFromPeriodJob=*/ false,
/*asyncResponseDelegate=*/ null);
final List<Long> expectedDailyTimestamps = List.of(

View File

@@ -16,6 +16,9 @@
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.FAKE_PACKAGE_NAME;
import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKNOWN;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.anyInt;
@@ -42,6 +45,7 @@ import android.os.BatteryUsageStats;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.ArrayMap;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -188,16 +192,18 @@ public final class DataProcessorTest {
final String packageName = "com.android.settings";
// Adds the day 1 data.
final List<Long> timestamps1 = List.of(14400000L, 18000000L, 21600000L);
final List<Integer> levels1 = List.of(100, 100, 100);
final Map<Long, Integer> batteryLevelMap1 =
Map.of(timestamps1.get(0), 100, timestamps1.get(1), 100, timestamps1.get(2), 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps1, levels1));
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1));
// Adds the day 2 data.
hourlyBatteryLevelsPerDay.add(null);
// Adds the day 3 data.
final List<Long> timestamps2 = List.of(45200000L, 48800000L);
final List<Integer> levels2 = List.of(100, 100);
final Map<Long, Integer> batteryLevelMap2 =
Map.of(timestamps2.get(0), 100, timestamps2.get(1), 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps2, levels2));
new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2));
final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
// Adds some events before the start timestamp.
appUsageEventList.add(buildAppUsageEvent(
@@ -285,7 +291,7 @@ public final class DataProcessorTest {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>()));
assertThat(DataProcessor.generateAppUsagePeriodMap(
mContext, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>()))
.isNull();
@@ -370,19 +376,6 @@ public final class DataProcessorTest {
DataProcessor.CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)).isTrue();
}
@Test
public void getLevelDataThroughProcessedHistoryMap_notEnoughData_returnNull() {
final long[] timestamps = {100L};
final int[] levels = {100};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
assertThat(
DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap))
.isNull();
}
@Test
public void getLevelDataThroughProcessedHistoryMap_OneDayData_returnExpectedResult() {
// Timezone GMT+8
@@ -441,7 +434,7 @@ public final class DataProcessorTest {
);
final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100);
expectedDailyLevels.add(null);
expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(82);
final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of(
@@ -459,13 +452,13 @@ public final class DataProcessorTest {
);
final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(94);
expectedHourlyLevels2.add(90);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(82);
final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1,
@@ -503,8 +496,8 @@ public final class DataProcessorTest {
);
final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100);
expectedDailyLevels.add(null);
expectedDailyLevels.add(null);
expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(88);
final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of(
@@ -542,32 +535,32 @@ public final class DataProcessorTest {
);
final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(88);
final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1,
@@ -606,8 +599,8 @@ public final class DataProcessorTest {
);
final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100);
expectedDailyLevels.add(null);
expectedDailyLevels.add(null);
expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(88);
final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of(
@@ -638,25 +631,25 @@ public final class DataProcessorTest {
);
final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(88);
final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1,
@@ -734,141 +727,6 @@ public final class DataProcessorTest {
verifyExpectedTimestampSlots(startTimestamp, endTimestamp, expectedTimestamps);
}
@Test
public void getDailyTimestamps_notEnoughData_returnEmptyList() {
assertThat(DataProcessor.getDailyTimestamps(new ArrayList<>())).isEmpty();
assertThat(DataProcessor.getDailyTimestamps(List.of(100L))).isEmpty();
}
@Test
public void getDailyTimestamps_allDataInOneHour_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640970006000L, // 2022-01-01 01:00:06
1640973608000L // 2022-01-01 01:00:08
);
final List<Long> expectedTimestamps = List.of(
1640970006000L, // 2022-01-01 01:00:06
1640973608000L // 2022-01-01 01:00:08
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_OneHourDataPerDay_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L // 2022-01-02 01:00:00
);
final List<Long> expectedTimestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L // 2022-01-02 01:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_OneDayData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
1640970000000L, // 2022-01-01 01:00:00
1640973600000L, // 2022-01-01 02:00:00
1640977200000L, // 2022-01-01 03:00:00
1640980800000L // 2022-01-01 04:00:00
);
final List<Long> expectedTimestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
1640980800000L // 2022-01-01 04:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_MultipleDaysData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641045600000L, // 2022-01-01 22:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641232800000L // 2022-01-04 02:00:00
);
final List<Long> expectedTimestamps = List.of(
1641045600000L, // 2022-01-01 22:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641232800000L // 2022-01-04 02:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_FirstDayOneHourData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641254400000L // 2022-01-04 08:00:00
);
final List<Long> expectedTimestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641254400000L // 2022-01-04 08:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_LastDayNoData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641225600000L // 2022-01-04 00:00:00
);
final List<Long> expectedTimestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L // 2022-01-04 00:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_LastDayOneHourData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641229200000L // 2022-01-04 01:00:00
);
final List<Long> expectedTimestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641229200000L // 2022-01-04 01:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void isFromFullCharge_emptyData_returnFalse() {
assertThat(DataProcessor.isFromFullCharge(null)).isFalse();
@@ -916,20 +774,53 @@ public final class DataProcessorTest {
}
@Test
public void getBatteryUsageMap_emptyHistoryMap_returnNull() {
public void getBatteryDiffDataMap_emptyHistoryMap_returnEmpty() {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>()));
assertThat(DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, new HashMap<>(), /*appUsagePeriodMap=*/ null))
.isNull();
assertThat(DataProcessor.getBatteryDiffDataMap(mContext, hourlyBatteryLevelsPerDay,
new HashMap<>(), /*appUsagePeriodMap=*/ null, Set.of(), Set.of())).isEmpty();
}
@Test
public void getBatteryUsageMap_returnsExpectedResult() {
public void getBatteryDiffDataMap_normalFlow_returnExpectedResult() {
final int userId = mContext.getUserId();
final long[] batteryHistoryKeys = new long[]{
1641045600000L, // 2022-01-01 22:00:00
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = Map.of(
batteryHistoryKeys[0], Map.of(FAKE_PACKAGE_NAME, createBatteryHistEntry(
FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0, 0, 0,
0, 0, 0L, userId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, 0L, 0L, false)),
batteryHistoryKeys[1], Map.of(FAKE_PACKAGE_NAME, createBatteryHistEntry(
FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 5, 0, 0,
0, 0, 0L, userId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, 0L, 0L, false)),
batteryHistoryKeys[2], Map.of(FAKE_PACKAGE_NAME, createBatteryHistEntry(
FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 16, 0, 0,
0, 0, 0L, userId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, 0L, 0L, false)));
final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
appUsagePeriodMap = Map.of(0, Map.of(0, Map.of(Long.valueOf(userId), Map.of(
FAKE_PACKAGE_NAME, List.of(buildAppUsagePeriod(0, 6))))));
Map<Long, BatteryDiffData> batteryDiffDataMap = DataProcessor.getBatteryDiffDataMap(
mContext, batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
appUsagePeriodMap, Set.of(), Set.of());
assertThat(batteryDiffDataMap).hasSize(1);
assertThat(batteryDiffDataMap).containsKey(batteryHistoryKeys[0]);
BatteryDiffData batteryDiffData = batteryDiffDataMap.get(batteryHistoryKeys[0]);
assertThat(batteryDiffData.getStartTimestamp()).isEqualTo(batteryHistoryKeys[0]);
assertThat(batteryDiffData.getEndTimestamp()).isEqualTo(batteryHistoryKeys[2]);
}
@Test
public void generateBatteryUsageMap_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641045600000L, // 2022-01-01 22:00:00
1641049200000L, // 2022-01-01 23:00:00
@@ -940,7 +831,7 @@ public final class DataProcessorTest {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0,
FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0,
/*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
/*uid=*/ 0L, currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
@@ -1030,19 +921,7 @@ public final class DataProcessorTest {
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[4], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
// Adds the day 1 data.
List<Long> timestamps =
List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
// Adds the day 2 data.
timestamps = List.of(batteryHistoryKeys[2], batteryHistoryKeys[4]);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
// Adds app usage data to test screen on time.
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
appUsagePeriodMap = new HashMap<>();
@@ -1066,8 +945,12 @@ public final class DataProcessorTest {
appUsagePeriodMap.get(1).put(0, appUsageMap);
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, appUsagePeriodMap);
DataProcessor.generateBatteryUsageMap(
mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
appUsagePeriodMap, Set.of(), Set.of()),
batteryLevelData);
BatteryDiffData resultDiffData =
resultMap
@@ -1128,7 +1011,7 @@ public final class DataProcessorTest {
}
@Test
public void getBatteryUsageMap_multipleUsers_returnsExpectedResult() {
public void generateBatteryUsageMap_multipleUsers_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
@@ -1217,17 +1100,15 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 30L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap,
/*appUsagePeriodMap=*/ null);
DataProcessor.generateBatteryUsageMap(
mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
/*appUsagePeriodMap=*/ null, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData =
resultMap
@@ -1247,7 +1128,7 @@ public final class DataProcessorTest {
}
@Test
public void getBatteryUsageMap_usageTimeExceed_returnsExpectedResult() {
public void generateBatteryUsageMap_usageTimeExceed_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
@@ -1288,12 +1169,7 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 7200000L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
// Adds app usage data to test screen on time.
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
@@ -1307,8 +1183,12 @@ public final class DataProcessorTest {
appUsagePeriodMap.get(0).put(0, appUsageMap);
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, appUsagePeriodMap);
DataProcessor.generateBatteryUsageMap(
mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
appUsagePeriodMap, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData =
resultMap
@@ -1338,7 +1218,7 @@ public final class DataProcessorTest {
}
@Test
public void getBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() {
public void generateBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
@@ -1403,19 +1283,17 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 20L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
when(mPowerUsageFeatureProvider.getHideApplicationSet())
.thenReturn(Set.of("package1"));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap,
/*appUsagePeriodMap=*/ null);
DataProcessor.generateBatteryUsageMap(
mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
/*appUsagePeriodMap=*/ null, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData =
resultMap
@@ -1431,7 +1309,7 @@ public final class DataProcessorTest {
}
@Test
public void getBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() {
public void generateBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
@@ -1496,19 +1374,17 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 20L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet())
.thenReturn(new HashSet(Arrays.asList((CharSequence) "package2")));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap,
/*appUsagePeriodMap=*/ null);
DataProcessor.generateBatteryUsageMap(
mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
/*appUsagePeriodMap=*/ null, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData =
resultMap
@@ -1523,7 +1399,10 @@ public final class DataProcessorTest {
@Test
public void generateBatteryDiffData_emptyBatteryEntryList_returnNull() {
assertThat(DataProcessor.generateBatteryDiffData(mContext,
DataProcessor.convertToBatteryHistEntry(null, mBatteryUsageStats))).isNull();
System.currentTimeMillis(),
DataProcessor.convertToBatteryHistEntry(null, mBatteryUsageStats),
/* systemAppsPackageNames= */ Set.of(),
/* systemAppsUids= */ Set.of())).isNull();
}
@Test
@@ -1574,7 +1453,10 @@ public final class DataProcessorTest {
.when(mMockBatteryEntry4).getPowerComponentId();
final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData(mContext,
DataProcessor.convertToBatteryHistEntry(batteryEntryList, mBatteryUsageStats));
System.currentTimeMillis(),
DataProcessor.convertToBatteryHistEntry(batteryEntryList, mBatteryUsageStats),
/* systemAppsPackageNames= */ Set.of(),
/* systemAppsUids= */ Set.of());
assertBatteryDiffEntry(
batteryDiffData.getAppDiffEntryList().get(0), 0, /*uid=*/ 2L,
@@ -2041,9 +1923,9 @@ public final class DataProcessorTest {
final double backgroundUsageConsumePower, final double cachedUsageConsumePower,
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs,
final long screenOnTimeInMs) {
assertThat(entry.mBatteryHistEntry.mUserId).isEqualTo(userId);
assertThat(entry.mBatteryHistEntry.mUid).isEqualTo(uid);
assertThat(entry.mBatteryHistEntry.mConsumerType).isEqualTo(consumerType);
assertThat(entry.mUserId).isEqualTo(userId);
assertThat(entry.mUid).isEqualTo(uid);
assertThat(entry.mConsumerType).isEqualTo(consumerType);
assertThat(entry.getPercentage()).isEqualTo(consumePercentage);
assertThat(entry.mForegroundUsageConsumePower).isEqualTo(foregroundUsageConsumePower);
assertThat(entry.mForegroundServiceUsageConsumePower)
@@ -2054,4 +1936,12 @@ public final class DataProcessorTest {
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
assertThat(entry.mScreenOnTimeInMs).isEqualTo(screenOnTimeInMs);
}
private BatteryLevelData generateBatteryLevelData(long[] timestamps) {
Map<Long, Integer> batteryLevelMap = new ArrayMap<>();
for (long timestamp : timestamps) {
batteryLevelMap.put(timestamp, 100);
}
return new BatteryLevelData(batteryLevelMap);
}
}

View File

@@ -173,8 +173,8 @@ public final class DatabaseUtilsTest {
doReturn(null).when(mContext).registerReceiver(any(), any());
assertThat(
DatabaseUtils.sendBatteryEntryData(
mContext, /*batteryEntryList=*/ null, mBatteryUsageStats,
/*isFullChargeStart=*/ false))
mContext, System.currentTimeMillis(), /*batteryEntryList=*/ null,
mBatteryUsageStats, /*isFullChargeStart=*/ false))
.isNull();
}
@@ -193,7 +193,10 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData(
mContext, batteryEntryList, mBatteryUsageStats,
mContext,
System.currentTimeMillis(),
batteryEntryList,
mBatteryUsageStats,
/*isFullChargeStart=*/ false);
assertThat(valuesList).hasSize(2);
@@ -216,6 +219,7 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData(
mContext,
System.currentTimeMillis(),
new ArrayList<>(),
mBatteryUsageStats,
/*isFullChargeStart=*/ false);
@@ -235,6 +239,7 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData(
mContext,
System.currentTimeMillis(),
/*batteryEntryList=*/ null,
mBatteryUsageStats,
/*isFullChargeStart=*/ false);
@@ -254,6 +259,7 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData(
mContext,
System.currentTimeMillis(),
/*batteryEntryList=*/ null,
/*batteryUsageStats=*/ null,
/*isFullChargeStart=*/ false);
@@ -359,7 +365,7 @@ public final class DatabaseUtilsTest {
}
@Test
public void getHistoryMapSinceLastFullCharge_emptyCursorContent_returnEmptyMap() {
public void getHistoryMap_emptyCursorContent_returnEmptyMap() {
final MatrixCursor cursor = new MatrixCursor(
new String[] {
BatteryHistEntry.KEY_UID,
@@ -367,36 +373,33 @@ public final class DatabaseUtilsTest {
BatteryHistEntry.KEY_TIMESTAMP});
DatabaseUtils.sFakeSupplier = () -> cursor;
assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge(
mContext, /*calendar=*/ null)).isEmpty();
assertThat(DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0)).isEmpty();
}
@Test
public void getHistoryMapSinceLastFullCharge_nullCursor_returnEmptyMap() {
public void getHistoryMap_nullCursor_returnEmptyMap() {
DatabaseUtils.sFakeSupplier = () -> null;
assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge(
mContext, /*calendar=*/ null)).isEmpty();
assertThat(DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0)).isEmpty();
}
@Test
public void getHistoryMapSinceLastFullCharge_returnExpectedMap() {
public void getHistoryMap_returnExpectedMap() {
final Long timestamp1 = Long.valueOf(1001L);
final Long timestamp2 = Long.valueOf(1002L);
final MatrixCursor cursor = getMatrixCursor();
// Adds fake data into the cursor.
cursor.addRow(new Object[] {
"app name1", timestamp1, 1, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
"app name1", timestamp1, 1, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, true});
cursor.addRow(new Object[] {
"app name2", timestamp2, 2, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
"app name2", timestamp2, 2, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, false});
cursor.addRow(new Object[] {
"app name3", timestamp2, 3, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
"app name3", timestamp2, 3, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, false});
cursor.addRow(new Object[] {
"app name4", timestamp2, 4, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
"app name4", timestamp2, 4, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, false});
DatabaseUtils.sFakeSupplier = () -> cursor;
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
DatabaseUtils.getHistoryMapSinceLastFullCharge(
mContext, /*calendar=*/ null);
DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, timestamp1);
assertThat(batteryHistMap).hasSize(2);
// Verifies the BatteryHistEntry data for timestamp1.
@@ -412,7 +415,7 @@ public final class DatabaseUtilsTest {
}
@Test
public void getHistoryMapSinceLastFullCharge_withWorkProfile_returnExpectedMap()
public void getHistoryMap_withWorkProfile_returnExpectedMap()
throws PackageManager.NameNotFoundException {
doReturn("com.fake.package").when(mContext).getPackageName();
doReturn(mMockContext).when(mContext).createPackageContextAsUser(
@@ -425,8 +428,7 @@ public final class DatabaseUtilsTest {
DatabaseUtils.sFakeSupplier = () -> getMatrixCursor();
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
DatabaseUtils.getHistoryMapSinceLastFullCharge(
mContext, /*calendar=*/ null);
DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0);
assertThat(batteryHistMap).isEmpty();
}
@@ -571,6 +573,7 @@ public final class DatabaseUtilsTest {
BatteryHistEntry.KEY_PACKAGE_NAME,
BatteryHistEntry.KEY_TIMESTAMP,
BatteryHistEntry.KEY_UID,
BatteryHistEntry.KEY_CONSUMER_TYPE});
BatteryHistEntry.KEY_CONSUMER_TYPE,
BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START});
}
}

View File

@@ -134,11 +134,6 @@ public class PowerUsageBaseTest {
return 0;
}
@Override
protected boolean isBatteryHistoryNeeded() {
return false;
}
@Override
protected void refreshUi(int refreshType) {
// Do nothing

View File

@@ -16,6 +16,10 @@
package com.android.settings.fuelgauge.batteryusage.db;
import static com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity.KEY_BATTERY_EVENT_TYPE;
import static com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity.KEY_BATTERY_LEVEL;
import static com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity.KEY_TIMESTAMP;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
@@ -31,9 +35,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.List;
/** Tests for {@link BatteryEventDao}. */
@RunWith(RobolectricTestRunner.class)
public final class BatteryEventDaoTest {
private static final long TIMESTAMP1 = System.currentTimeMillis();
private static final long TIMESTAMP2 = TIMESTAMP1 + 2;
private Context mContext;
private BatteryStateDatabase mDatabase;
private BatteryEventDao mBatteryEventDao;
@@ -51,8 +60,44 @@ public final class BatteryEventDaoTest {
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
}
@Test
public void getAllAfter_returnExpectedResult() {
public void getLastFullChargeTimestamp_normalFlow_expectedBehavior() throws Exception {
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(TIMESTAMP1)
.setBatteryEventType(3)
.setBatteryLevel(100)
.build());
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(TIMESTAMP2)
.setBatteryEventType(4)
.setBatteryLevel(96)
.build());
final Cursor cursor = mBatteryEventDao.getLastFullChargeTimestamp();
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
assertThat(cursor.getLong(0)).isEqualTo(TIMESTAMP1);
}
@Test
public void getLastFullChargeTimestamp_noLastFullChargeTime_returns0() throws Exception {
mBatteryEventDao.clearAll();
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(TIMESTAMP2)
.setBatteryEventType(4)
.setBatteryLevel(96)
.build());
final Cursor cursor = mBatteryEventDao.getLastFullChargeTimestamp();
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
assertThat(cursor.getLong(0)).isEqualTo(0L);
}
@Test
public void getAllAfter_normalFlow_returnExpectedResult() {
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(100L)
.setBatteryEventType(1)
@@ -64,17 +109,44 @@ public final class BatteryEventDaoTest {
.setBatteryLevel(88)
.build());
final Cursor cursor = mBatteryEventDao.getAllAfter(160L);
final Cursor cursor = mBatteryEventDao.getAllAfter(160L, List.of(1, 2));
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
assertThat(cursor.getLong(cursor.getColumnIndex(BatteryEventEntity.KEY_TIMESTAMP)))
assertThat(cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP)))
.isEqualTo(200L);
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)))
assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_EVENT_TYPE)))
.isEqualTo(2);
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_LEVEL)))
assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_LEVEL)))
.isEqualTo(88);
mBatteryEventDao.clearAll();
assertThat(mBatteryEventDao.getAll()).isEmpty();
}
@Test
public void getAllAfter_filterBatteryTypes_returnExpectedResult() {
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(100L)
.setBatteryEventType(1)
.setBatteryLevel(66)
.build());
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(200L)
.setBatteryEventType(2)
.setBatteryLevel(88)
.build());
final Cursor cursor = mBatteryEventDao.getAllAfter(0L, List.of(1));
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
assertThat(cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP)))
.isEqualTo(100L);
assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_EVENT_TYPE)))
.isEqualTo(1);
assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_LEVEL)))
.isEqualTo(66);
mBatteryEventDao.clearAll();
assertThat(mBatteryEventDao.getAll()).isEmpty();
}
}

View File

@@ -37,9 +37,10 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
public final class BatteryStateDaoTest {
private static final int CURSOR_COLUMN_SIZE = 9;
private static final long TIMESTAMP1 = System.currentTimeMillis();
private static final long TIMESTAMP2 = System.currentTimeMillis() + 2;
private static final long TIMESTAMP3 = System.currentTimeMillis() + 4;
private static final long CURRENT = System.currentTimeMillis();
private static final long TIMESTAMP1 = CURRENT;
private static final long TIMESTAMP2 = CURRENT + 2;
private static final long TIMESTAMP3 = CURRENT + 4;
private static final String PACKAGE_NAME1 = "com.android.apps.settings";
private static final String PACKAGE_NAME2 = "com.android.apps.calendar";
private static final String PACKAGE_NAME3 = "com.android.apps.gmail";
@@ -67,7 +68,7 @@ public final class BatteryStateDaoTest {
}
@Test
public void batteryStateDao_insertAll() throws Exception {
public void insertAll_normalFlow_expectedBehavior() throws Exception {
final List<BatteryState> states = mBatteryStateDao.getAllAfter(TIMESTAMP1);
assertThat(states).hasSize(2);
// Verifies the queried battery states.
@@ -76,8 +77,26 @@ public final class BatteryStateDaoTest {
}
@Test
public void batteryStateDao_getCursorSinceLastFullCharge() throws Exception {
final Cursor cursor1 = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP1);
public void getLatestTimestamp_normalFlow_expectedBehavior() throws Exception {
final Cursor cursor1 = mBatteryStateDao.getLatestTimestampBefore(TIMESTAMP1 - 1);
assertThat(cursor1.getCount()).isEqualTo(1);
cursor1.moveToFirst();
assertThat(cursor1.getLong(0)).isEqualTo(0L);
final Cursor cursor2 = mBatteryStateDao.getLatestTimestampBefore(TIMESTAMP2);
assertThat(cursor2.getCount()).isEqualTo(1);
cursor2.moveToFirst();
assertThat(cursor2.getLong(0)).isEqualTo(TIMESTAMP2);
final Cursor cursor3 = mBatteryStateDao.getLatestTimestampBefore(TIMESTAMP3 + 1);
assertThat(cursor3.getCount()).isEqualTo(1);
cursor3.moveToFirst();
assertThat(cursor3.getLong(0)).isEqualTo(TIMESTAMP3);
}
@Test
public void getBatteryStatesAfter_normalFlow_expectedBehavior() throws Exception {
final Cursor cursor1 = mBatteryStateDao.getBatteryStatesAfter(TIMESTAMP1);
assertThat(cursor1.getCount()).isEqualTo(3);
assertThat(cursor1.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state.
@@ -90,7 +109,7 @@ public final class BatteryStateDaoTest {
cursor1.moveToNext();
assertThat(cursor1.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
final Cursor cursor2 = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP3);
final Cursor cursor2 = mBatteryStateDao.getBatteryStatesAfter(TIMESTAMP3);
assertThat(cursor2.getCount()).isEqualTo(1);
assertThat(cursor2.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state.
@@ -99,25 +118,7 @@ public final class BatteryStateDaoTest {
}
@Test
public void batteryStateDao_getCursorSinceLastFullCharge_noFullChargeData_returnSevenDaysData()
throws Exception {
mBatteryStateDao.clearAll();
BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP3, PACKAGE_NAME3);
BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP2, PACKAGE_NAME2);
BatteryTestUtils.insertDataToBatteryStateTable(mContext, TIMESTAMP1, PACKAGE_NAME1);
final Cursor cursor = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP2);
assertThat(cursor.getCount()).isEqualTo(2);
assertThat(cursor.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state.
cursor.moveToFirst();
assertThat(cursor.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME2);
// Verifies the queried third battery state.
cursor.moveToNext();
assertThat(cursor.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
}
@Test
public void batteryStateDao_clearAllBefore() throws Exception {
public void clearAllBefore_normalFlow_expectedBehavior() throws Exception {
mBatteryStateDao.clearAllBefore(TIMESTAMP2);
final List<BatteryState> states = mBatteryStateDao.getAllAfter(0);
@@ -127,20 +128,20 @@ public final class BatteryStateDaoTest {
}
@Test
public void batteryStateDao_clearAll() throws Exception {
public void clearAll_normalFlow_expectedBehavior() throws Exception {
assertThat(mBatteryStateDao.getAllAfter(0)).hasSize(3);
mBatteryStateDao.clearAll();
assertThat(mBatteryStateDao.getAllAfter(0)).isEmpty();
}
@Test
public void getInstance_createNewInstance() throws Exception {
public void getInstance_createNewInstance_returnsExpectedResult() throws Exception {
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
assertThat(BatteryStateDatabase.getInstance(mContext)).isNotNull();
}
@Test
public void getDistinctTimestampCount_returnsExpectedResult() {
public void getDistinctTimestampCount_normalFlow_returnsExpectedResult() {
assertThat(mBatteryStateDao.getDistinctTimestampCount(/*timestamp=*/ 0))
.isEqualTo(3);
assertThat(mBatteryStateDao.getDistinctTimestampCount(TIMESTAMP1))
@@ -148,7 +149,7 @@ public final class BatteryStateDaoTest {
}
@Test
public void getDistinctTimestamps_returnsExpectedResult() {
public void getDistinctTimestamps_normalFlow_returnsExpectedResult() {
final List<Long> timestamps =
mBatteryStateDao.getDistinctTimestamps(/*timestamp=*/ 0);

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.database.Cursor;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.BatteryTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.List;
/** Tests for {@link BatteryUsageSlotDao}. */
@RunWith(RobolectricTestRunner.class)
public final class BatteryUsageSlotDaoTest {
private static final int CURSOR_COLUMN_SIZE = 3;
private static final long CURRENT = System.currentTimeMillis();
private static final long TIMESTAMP1 = CURRENT;
private static final long TIMESTAMP2 = CURRENT + 2;
private static final String BATTERY_USAGE_SLOT_STRING1 = "BATTERY_USAGE_SLOT_STRING1";
private static final String BATTERY_USAGE_SLOT_STRING2 = "BATTERY_USAGE_SLOT_STRING2";
private Context mContext;
private BatteryStateDatabase mDatabase;
private BatteryUsageSlotDao mBatteryUsageSlotDao;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
mBatteryUsageSlotDao = mDatabase.batteryUsageSlotDao();
mBatteryUsageSlotDao.insert(
new BatteryUsageSlotEntity(TIMESTAMP1, BATTERY_USAGE_SLOT_STRING1));
mBatteryUsageSlotDao.insert(
new BatteryUsageSlotEntity(TIMESTAMP2, BATTERY_USAGE_SLOT_STRING2));
}
@After
public void closeDb() {
mDatabase.close();
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
}
@Test
public void getAll_normalFlow_expectedBehavior() throws Exception {
final List<BatteryUsageSlotEntity> entities = mBatteryUsageSlotDao.getAll();
assertThat(entities).hasSize(2);
assertThat(entities.get(0).timestamp).isEqualTo(TIMESTAMP1);
assertThat(entities.get(0).batteryUsageSlot).isEqualTo(BATTERY_USAGE_SLOT_STRING1);
assertThat(entities.get(1).timestamp).isEqualTo(TIMESTAMP2);
assertThat(entities.get(1).batteryUsageSlot).isEqualTo(BATTERY_USAGE_SLOT_STRING2);
}
@Test
public void getAllAfter_normalFlow_expectedBehavior() throws Exception {
final Cursor cursor1 = mBatteryUsageSlotDao.getAllAfter(TIMESTAMP1);
assertThat(cursor1.getCount()).isEqualTo(2);
assertThat(cursor1.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
cursor1.moveToFirst();
assertThat(cursor1.getLong(1 /*timestamp*/)).isEqualTo(TIMESTAMP1);
cursor1.moveToNext();
assertThat(cursor1.getLong(1 /*timestamp*/)).isEqualTo(TIMESTAMP2);
final Cursor cursor2 = mBatteryUsageSlotDao.getAllAfter(TIMESTAMP1 + 1);
assertThat(cursor2.getCount()).isEqualTo(1);
assertThat(cursor2.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
cursor2.moveToFirst();
assertThat(cursor2.getLong(1 /*timestamp*/)).isEqualTo(TIMESTAMP2);
}
@Test
public void clearAllBefore_normalFlow_expectedBehavior() throws Exception {
mBatteryUsageSlotDao.clearAllBefore(TIMESTAMP1);
final List<BatteryUsageSlotEntity> entities = mBatteryUsageSlotDao.getAll();
assertThat(entities).hasSize(1);
assertThat(entities.get(0).timestamp).isEqualTo(TIMESTAMP2);
assertThat(entities.get(0).batteryUsageSlot).isEqualTo(BATTERY_USAGE_SLOT_STRING2);
}
@Test
public void clearAll_normalFlow_expectedBehavior() throws Exception {
mBatteryUsageSlotDao.clearAll();
assertThat(mBatteryUsageSlotDao.getAll()).isEmpty();
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.batteryusage.db;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Tests for {@link BatteryUsageSlotEntity}. */
@RunWith(RobolectricTestRunner.class)
public final class BatteryUsageSlotEntityTest {
@Test
public void testBuilder_returnsExpectedResult() {
final long timestamp = 10001L;
final String batteryUsageSlotString = "batteryUsageSlotString";
BatteryUsageSlotEntity entity = BatteryUsageSlotEntity
.newBuilder()
.setTimestamp(timestamp)
.setBatteryUsageSlot(batteryUsageSlotString)
.build();
// Verifies the app relative information.
assertThat(entity.timestamp).isEqualTo(timestamp);
assertThat(entity.batteryUsageSlot).isEqualTo(batteryUsageSlotString);
}
}