Merge "Battery usage page latency improvement (1-8)" into udc-qpr-dev am: 1dad0bf856

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/24211971

Change-Id: I5b3b38a62fba34203e0195cdcc510c68f8e12652
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Zaiyue Xue
2023-08-18 02:39:34 +00:00
committed by Automerger Merge Worker
49 changed files with 2713 additions and 1443 deletions

View File

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

View File

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

@@ -132,7 +132,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap; Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
private boolean mIs24HourFormat; private boolean mIs24HourFormat;
private boolean mHourlyChartVisible = true;
private View mBatteryChartViewGroup; private View mBatteryChartViewGroup;
private TextView mChartSummaryTextView; private TextView mChartSummaryTextView;
private BatteryChartViewModel mDailyViewModel; private BatteryChartViewModel mDailyViewModel;
@@ -245,20 +244,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mOnBatteryTipsUpdatedListener = listener; mOnBatteryTipsUpdatedListener = listener;
} }
void setBatteryHistoryMap( void onBatteryLevelDataUpdate(final BatteryLevelData batteryLevelData) {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) { Log.d(TAG, "onBatteryLevelDataUpdate: " + batteryLevelData);
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);
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
mPrefContext, mPrefContext,
SettingsEnums.ACTION_BATTERY_HISTORY_LOADED, SettingsEnums.ACTION_BATTERY_HISTORY_LOADED,
@@ -289,6 +276,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
refreshUi(); refreshUi();
} }
void onBatteryUsageMapUpdate(Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
Log.d(TAG, "onBatteryUsageMapUpdate: " + batteryUsageMap);
mBatteryUsageMap = batteryUsageMap;
logScreenUsageTime();
refreshUi();
}
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView, void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) { @NonNull final BatteryChartView hourlyChartView) {
final View parentView = (View) dailyChartView.getParent(); final View parentView = (View) dailyChartView.getParent();
@@ -512,10 +506,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
private void animateBatteryHourlyChartView(final boolean visible) { private void animateBatteryHourlyChartView(final boolean visible) {
if (mHourlyChartView == null || mHourlyChartVisible == visible) { if (mHourlyChartView == null
|| (mHourlyChartView.getVisibility() == View.VISIBLE) == visible) {
return; return;
} }
mHourlyChartVisible = visible;
if (visible) { if (visible) {
mHourlyChartView.setVisibility(View.VISIBLE); mHourlyChartView.setVisibility(View.VISIBLE);
@@ -672,10 +666,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return null; return null;
} }
for (BatteryDiffEntry entry : entries) { for (BatteryDiffEntry entry : entries) {
final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry; if (!entry.isSystemEntry()
if (batteryHistEntry != null && entry.mUserId == userId
&& batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
&& batteryHistEntry.mUserId == userId
&& packageName.equals(entry.getPackageName())) { && packageName.equals(entry.getPackageName())) {
return entry; 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.Utils.formatPercentage;
import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS; 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.abs;
import static java.lang.Math.round; import static java.lang.Math.round;
@@ -615,8 +616,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private static boolean isTrapezoidValid( private static boolean isTrapezoidValid(
@NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) {
return viewModel.getLevel(trapezoidIndex) != null return viewModel.getLevel(trapezoidIndex) != BATTERY_LEVEL_UNKNOWN
&& viewModel.getLevel(trapezoidIndex + 1) != null; && viewModel.getLevel(trapezoidIndex + 1) != BATTERY_LEVEL_UNKNOWN;
} }
private static boolean isTrapezoidIndexValid( private static boolean isTrapezoidIndexValid(

View File

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

View File

@@ -15,7 +15,6 @@
*/ */
package com.android.settings.fuelgauge.batteryusage; package com.android.settings.fuelgauge.batteryusage;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -24,6 +23,7 @@ import android.graphics.drawable.Drawable;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -45,12 +45,29 @@ public class BatteryDiffEntry {
static final Map<String, BatteryEntry.NameAndIcon> sResourceCache = new HashMap<>(); static final Map<String, BatteryEntry.NameAndIcon> sResourceCache = new HashMap<>();
// Whether a specific item is valid to launch restriction page? // Whether a specific item is valid to launch restriction page?
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) @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. */ /** 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()); (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 mForegroundUsageTimeInMs;
public long mBackgroundUsageTimeInMs; public long mBackgroundUsageTimeInMs;
public long mScreenOnTimeInMs; public long mScreenOnTimeInMs;
@@ -59,8 +76,6 @@ public class BatteryDiffEntry {
public double mForegroundServiceUsageConsumePower; public double mForegroundServiceUsageConsumePower;
public double mBackgroundUsageConsumePower; public double mBackgroundUsageConsumePower;
public double mCachedUsageConsumePower; public double mCachedUsageConsumePower;
// A BatteryHistEntry corresponding to this diff usage data.
public final BatteryHistEntry mBatteryHistEntry;
protected Context mContext; protected Context mContext;
@@ -83,6 +98,14 @@ public class BatteryDiffEntry {
public BatteryDiffEntry( public BatteryDiffEntry(
Context context, Context context,
long uid,
long userId,
String key,
boolean isHidden,
int componentId,
String legacyPackageName,
String legacyLabel,
int consumerType,
long foregroundUsageTimeInMs, long foregroundUsageTimeInMs,
long backgroundUsageTimeInMs, long backgroundUsageTimeInMs,
long screenOnTimeInMs, long screenOnTimeInMs,
@@ -90,21 +113,36 @@ public class BatteryDiffEntry {
double foregroundUsageConsumePower, double foregroundUsageConsumePower,
double foregroundServiceUsageConsumePower, double foregroundServiceUsageConsumePower,
double backgroundUsageConsumePower, double backgroundUsageConsumePower,
double cachedUsageConsumePower, double cachedUsageConsumePower) {
BatteryHistEntry batteryHistEntry) {
mContext = context; 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; mConsumePower = consumePower;
mForegroundUsageConsumePower = foregroundUsageConsumePower; mForegroundUsageConsumePower = foregroundUsageConsumePower;
mForegroundServiceUsageConsumePower = foregroundServiceUsageConsumePower; mForegroundServiceUsageConsumePower = foregroundServiceUsageConsumePower;
mBackgroundUsageConsumePower = backgroundUsageConsumePower; mBackgroundUsageConsumePower = backgroundUsageConsumePower;
mCachedUsageConsumePower = cachedUsageConsumePower; mCachedUsageConsumePower = cachedUsageConsumePower;
mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
mScreenOnTimeInMs = screenOnTimeInMs;
mBatteryHistEntry = batteryHistEntry;
mUserManager = context.getSystemService(UserManager.class); 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. */ /** Sets the total consumed power in a specific time slot. */
public void setTotalConsumePower(double totalConsumePower) { public void setTotalConsumePower(double totalConsumePower) {
mTotalConsumePower = totalConsumePower; mTotalConsumePower = totalConsumePower;
@@ -135,13 +173,22 @@ public class BatteryDiffEntry {
/** Gets the key for sorting */ /** Gets the key for sorting */
public double getSortingKey() { public double getSortingKey() {
return getPercentage() + getAdjustPercentageOffset(); return getKey() != null && SPECIAL_ENTRY_MAP.containsKey(getKey())
? -1 : getPercentage() + getAdjustPercentageOffset();
} }
/** Clones a new instance. */ /** Clones a new instance. */
public BatteryDiffEntry clone() { public BatteryDiffEntry clone() {
return new BatteryDiffEntry( return new BatteryDiffEntry(
this.mContext, this.mContext,
this.mUid,
this.mUserId,
this.mKey,
this.mIsHidden,
this.mComponentId,
this.mLegacyPackageName,
this.mLegacyLabel,
this.mConsumerType,
this.mForegroundUsageTimeInMs, this.mForegroundUsageTimeInMs,
this.mBackgroundUsageTimeInMs, this.mBackgroundUsageTimeInMs,
this.mScreenOnTimeInMs, this.mScreenOnTimeInMs,
@@ -149,17 +196,14 @@ public class BatteryDiffEntry {
this.mForegroundUsageConsumePower, this.mForegroundUsageConsumePower,
this.mForegroundServiceUsageConsumePower, this.mForegroundServiceUsageConsumePower,
this.mBackgroundUsageConsumePower, this.mBackgroundUsageConsumePower,
this.mCachedUsageConsumePower, this.mCachedUsageConsumePower);
this.mBatteryHistEntry /*same instance*/);
} }
/** Gets the app label name for this entry. */ /** Gets the app label name for this entry. */
public String getAppLabel() { public String getAppLabel() {
loadLabelAndIcon(); loadLabelAndIcon();
// Returns default applicationn label if we cannot find it. // Returns default application label if we cannot find it.
return mAppLabel == null || mAppLabel.length() == 0 return mAppLabel == null || mAppLabel.length() == 0 ? mLegacyLabel : mAppLabel;
? mBatteryHistEntry.mAppLabel
: mAppLabel;
} }
/** Gets the app icon {@link Drawable} for this entry. */ /** 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. */ /** Gets the searching package name for UID battery type. */
public String getPackageName() { public String getPackageName() {
final String packageName = mDefaultPackageName != null final String packageName = mDefaultPackageName != null
? mDefaultPackageName : mBatteryHistEntry.mPackageName; ? mDefaultPackageName : mLegacyPackageName;
if (packageName == null) { if (packageName == null) {
return packageName; return packageName;
} }
@@ -198,10 +242,10 @@ public class BatteryDiffEntry {
/** Whether the current BatteryDiffEntry is system component or not. */ /** Whether the current BatteryDiffEntry is system component or not. */
public boolean isSystemEntry() { public boolean isSystemEntry() {
if (mBatteryHistEntry.mIsHidden) { if (mIsHidden) {
return false; return false;
} }
switch (mBatteryHistEntry.mConsumerType) { switch (mConsumerType) {
case ConvertUtils.CONSUMER_TYPE_USER_BATTERY: case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY: case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
return true; return true;
@@ -236,12 +280,22 @@ public class BatteryDiffEntry {
updateRestrictionFlagState(); updateRestrictionFlagState();
sValidForRestriction.put(getKey(), Boolean.valueOf(mValidForRestriction)); 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. // Loads application icon and label based on consumer type.
switch (mBatteryHistEntry.mConsumerType) { switch (mConsumerType) {
case ConvertUtils.CONSUMER_TYPE_USER_BATTERY: case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
final BatteryEntry.NameAndIcon nameAndIconForUser = final BatteryEntry.NameAndIcon nameAndIconForUser =
BatteryEntry.getNameAndIconFromUserId( BatteryEntry.getNameAndIconFromUserId(mContext, (int) mUserId);
mContext, (int) mBatteryHistEntry.mUserId);
if (nameAndIconForUser != null) { if (nameAndIconForUser != null) {
mAppIcon = nameAndIconForUser.mIcon; mAppIcon = nameAndIconForUser.mIcon;
mAppLabel = nameAndIconForUser.mName; mAppLabel = nameAndIconForUser.mName;
@@ -252,8 +306,7 @@ public class BatteryDiffEntry {
break; break;
case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY: case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
final BatteryEntry.NameAndIcon nameAndIconForSystem = final BatteryEntry.NameAndIcon nameAndIconForSystem =
BatteryEntry.getNameAndIconFromPowerComponent( BatteryEntry.getNameAndIconFromPowerComponent(mContext, mComponentId);
mContext, mBatteryHistEntry.mDrainType);
if (nameAndIconForSystem != null) { if (nameAndIconForSystem != null) {
mAppLabel = nameAndIconForSystem.mName; mAppLabel = nameAndIconForSystem.mName;
if (nameAndIconForSystem.mIconId != 0) { if (nameAndIconForSystem.mIconId != 0) {
@@ -283,12 +336,12 @@ public class BatteryDiffEntry {
} }
String getKey() { String getKey() {
return mBatteryHistEntry.getKey(); return mKey;
} }
@VisibleForTesting @VisibleForTesting
void updateRestrictionFlagState() { void updateRestrictionFlagState() {
if (!mBatteryHistEntry.isAppEntry()) { if (isSystemEntry()) {
mValidForRestriction = false; mValidForRestriction = false;
return; return;
} }
@@ -348,7 +401,7 @@ public class BatteryDiffEntry {
return; return;
} }
final int uid = (int) mBatteryHistEntry.mUid; final int uid = (int) mUid;
final String[] packages = packageManager.getPackagesForUid(uid); final String[] packages = packageManager.getPackagesForUid(uid);
// Loads special defined application label and icon if available. // Loads special defined application label and icon if available.
if (packages == null || packages.length == 0) { if (packages == null || packages.length == 0) {
@@ -394,8 +447,7 @@ public class BatteryDiffEntry {
StringUtil.formatElapsedTime(mContext, (double) mScreenOnTimeInMs, StringUtil.formatElapsedTime(mContext, (double) mScreenOnTimeInMs,
/*withSeconds=*/ true, /*collapseTimeUnit=*/ false))) /*withSeconds=*/ true, /*collapseTimeUnit=*/ false)))
.append(String.format("\n\tpackage:%s|%s uid:%d userId:%d", .append(String.format("\n\tpackage:%s|%s uid:%d userId:%d",
mBatteryHistEntry.mPackageName, getPackageName(), mLegacyPackageName, getPackageName(), mUid, mUserId));
mBatteryHistEntry.mUid, mBatteryHistEntry.mUserId));
return builder.toString(); return builder.toString();
} }
@@ -406,130 +458,8 @@ public class BatteryDiffEntry {
} }
private Drawable getBadgeIconForUser(Drawable icon) { 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 : return userId == UserHandle.USER_OWNER ? icon :
mUserManager.getBadgedIconForUser(icon, new UserHandle(userId)); 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; 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}. */ /** Gets an identifier to represent this {@link BatteryHistEntry}. */
public String getKey() { public String getKey() {
if (mKey == null) { 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; package com.android.settings.fuelgauge.batteryusage;
import android.content.Context; import android.content.Context;
import android.os.BatteryUsageStats;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
/** /**
@@ -36,9 +32,6 @@ import com.android.settings.fuelgauge.BatteryUtils;
public class BatteryHistoryPreference extends Preference { public class BatteryHistoryPreference extends Preference {
private static final String TAG = "BatteryHistoryPreference"; private static final String TAG = "BatteryHistoryPreference";
@VisibleForTesting
BatteryInfo mBatteryInfo;
private BatteryChartView mDailyChartView; private BatteryChartView mDailyChartView;
private BatteryChartView mHourlyChartView; private BatteryChartView mHourlyChartView;
private BatteryChartPreferenceController mChartPreferenceController; private BatteryChartPreferenceController mChartPreferenceController;
@@ -49,13 +42,6 @@ public class BatteryHistoryPreference extends Preference {
setSelectable(false); setSelectable(false);
} }
void setBatteryUsageStats(@NonNull BatteryUsageStats batteryUsageStats) {
BatteryInfo.getBatteryInfo(getContext(), info -> {
mBatteryInfo = info;
notifyChanged();
}, batteryUsageStats, false);
}
void setChartPreferenceController(BatteryChartPreferenceController controller) { void setChartPreferenceController(BatteryChartPreferenceController controller) {
mChartPreferenceController = controller; mChartPreferenceController = controller;
if (mDailyChartView != null && mHourlyChartView != null) { if (mDailyChartView != null && mHourlyChartView != null) {
@@ -67,9 +53,6 @@ public class BatteryHistoryPreference extends Preference {
public void onBindViewHolder(PreferenceViewHolder view) { public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view); super.onBindViewHolder(view);
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
if (mBatteryInfo == null) {
return;
}
final TextView companionTextView = (TextView) view.findViewById(R.id.companion_text); final TextView companionTextView = (TextView) view.findViewById(R.id.companion_text);
mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart); mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
mDailyChartView.setCompanionTextView(companionTextView); mDailyChartView.setCompanionTextView(companionTextView);

View File

@@ -16,15 +16,28 @@
package com.android.settings.fuelgauge.batteryusage; 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.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.util.Preconditions; import androidx.core.util.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** Wraps the battery timestamp and level data used for battery usage chart. */ /** Wraps the battery timestamp and level data used for battery usage chart. */
public final class BatteryLevelData { 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. */ /** A container for the battery timestamp and level data. */
public static final class PeriodBatteryLevelData { public static final class PeriodBatteryLevelData {
// The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when // 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; private final List<Integer> mLevels;
public PeriodBatteryLevelData( public PeriodBatteryLevelData(
@NonNull List<Long> timestamps, @NonNull List<Integer> levels) { @NonNull Map<Long, Integer> batteryLevelMap,
Preconditions.checkArgument(timestamps.size() == levels.size(), @NonNull List<Long> timestamps) {
/* errorMessage= */ "Timestamp: " + timestamps.size() + ", Level: "
+ levels.size());
mTimestamps = 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() { 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. // The size of hourly data must be the size of daily data - 1.
private final List<PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay; private final List<PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
public BatteryLevelData( public BatteryLevelData(@NonNull Map<Long, Integer> batteryLevelMap) {
@NonNull PeriodBatteryLevelData dailyBatteryLevels, final int mapSize = batteryLevelMap.size();
@NonNull List<PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) { Preconditions.checkArgument(mapSize >= MIN_SIZE, "batteryLevelMap size:" + mapSize);
final long dailySize = dailyBatteryLevels.getTimestamps().size();
final long hourlySize = hourlyBatteryLevelsPerDay.size(); final List<Long> timestampList = new ArrayList<>(batteryLevelMap.keySet());
Preconditions.checkArgument(hourlySize == dailySize - 1, Collections.sort(timestampList);
/* errorMessage= */ "DailySize: " + dailySize + ", HourlySize: " + hourlySize); final List<Long> dailyTimestamps = getDailyTimestamps(timestampList);
mDailyBatteryLevels = dailyBatteryLevels; final List<List<Long>> hourlyTimestamps = getHourlyTimestamps(dailyTimestamps);
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
mDailyBatteryLevels = new PeriodBatteryLevelData(batteryLevelMap, dailyTimestamps);
mHourlyBatteryLevelsPerDay = new ArrayList<>(hourlyTimestamps.size());
for (List<Long> hourlyTimestampsPerDay : hourlyTimestamps) {
mHourlyBatteryLevelsPerDay.add(
new PeriodBatteryLevelData(batteryLevelMap, hourlyTimestampsPerDay));
}
} }
public PeriodBatteryLevelData getDailyBatteryLevels() { public PeriodBatteryLevelData getDailyBatteryLevels() {
@@ -94,5 +115,69 @@ public final class BatteryLevelData {
Objects.toString(mDailyBatteryLevels), Objects.toString(mDailyBatteryLevels),
Objects.toString(mHourlyBatteryLevelsPerDay)); 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 PowerGaugePreference powerPref = (PowerGaugePreference) preference;
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry(); final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry; final String packageName = diffEntry.getPackageName();
final String packageName = histEntry.mPackageName;
final boolean isAppEntry = histEntry.isAppEntry();
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
/* attribution */ SettingsEnums.OPEN_BATTERY_USAGE, /* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
/* action */ isAppEntry /* action */ diffEntry.isSystemEntry()
? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM ? SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM
: SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM, : SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE, /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName, TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
(int) Math.round(diffEntry.getPercentage())); (int) Math.round(diffEntry.getPercentage()));
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s", 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( AdvancedPowerUsageDetail.startBatteryDetailPage(
mActivity, mFragment, diffEntry, powerPref.getPercentage(), mSlotTimestamp); mActivity, mFragment, diffEntry, powerPref.getPercentage(), mSlotTimestamp);
return true; return true;

View File

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

View File

@@ -16,9 +16,12 @@
package com.android.settings.fuelgauge.batteryusage; package com.android.settings.fuelgauge.batteryusage;
import android.app.usage.UsageEvents;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.BatteryUsageStats; import android.os.BatteryUsageStats;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -27,6 +30,7 @@ import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils; import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
/** Load battery usage data in the background. */ /** Load battery usage data in the background. */
@@ -36,6 +40,10 @@ public final class BatteryUsageDataLoader {
// For testing only. // For testing only.
@VisibleForTesting @VisibleForTesting
static Supplier<List<BatteryEntry>> sFakeBatteryEntryListSupplier; static Supplier<List<BatteryEntry>> sFakeBatteryEntryListSupplier;
@VisibleForTesting
static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier;
@VisibleForTesting
static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier;
private BatteryUsageDataLoader() { private BatteryUsageDataLoader() {
} }
@@ -48,9 +56,9 @@ public final class BatteryUsageDataLoader {
} }
@VisibleForTesting @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, ""); BatteryUsageLogUtils.writeLog(context, Action.FETCH_USAGE_DATA, "");
final long start = System.currentTimeMillis(); final long currentTime = System.currentTimeMillis();
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context); final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
final List<BatteryEntry> batteryEntryList = final List<BatteryEntry> batteryEntryList =
sFakeBatteryEntryListSupplier != null ? sFakeBatteryEntryListSupplier.get() sFakeBatteryEntryListSupplier != null ? sFakeBatteryEntryListSupplier.get()
@@ -59,25 +67,81 @@ public final class BatteryUsageDataLoader {
if (batteryEntryList == null || batteryEntryList.isEmpty()) { if (batteryEntryList == null || batteryEntryList.isEmpty()) {
Log.w(TAG, "getBatteryEntryList() returns null or empty content"); 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)); Log.d(TAG, String.format("getBatteryUsageStats() in %d/ms", elapsedTime));
if (isFullChargeStart) { if (isFullChargeStart) {
DatabaseUtils.recordDateTime( DatabaseUtils.recordDateTime(
context, DatabaseUtils.KEY_LAST_LOAD_FULL_CHARGE_TIME); context, DatabaseUtils.KEY_LAST_LOAD_FULL_CHARGE_TIME);
DatabaseUtils.sendBatteryEventData(context, ConvertUtils.convertToBatteryEvent(
currentTime, BatteryEventType.FULL_CHARGED, 100));
} }
// Uploads the BatteryEntry data into database. // Uploads the BatteryEntry data into database.
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
context, batteryEntryList, batteryUsageStats, isFullChargeStart); context, currentTime, batteryEntryList, batteryUsageStats, isFullChargeStart);
DataProcessor.closeBatteryUsageStats(batteryUsageStats); 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( private static void loadUsageDataSafely(
final Context context, final boolean isFullChargeStart) { final Context context, final boolean isFullChargeStart) {
try { 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) { } 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.Base64;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity; 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
/** A utility class to convert data into another types. */ /** A utility class to convert data into another types. */
@@ -75,7 +81,22 @@ public final class ConvertUtils {
private 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( public static ContentValues convertBatteryEntryToContentValues(
final BatteryEntry entry, final BatteryEntry entry,
final BatteryUsageStats batteryUsageStats, final BatteryUsageStats batteryUsageStats,
@@ -118,7 +139,7 @@ public final class ConvertUtils {
return values; return values;
} }
/** Converts {@link AppUsageEvent} to content values */ /** Converts {@link AppUsageEvent} to {@link ContentValues} */
public static ContentValues convertAppUsageEventToContentValues(final AppUsageEvent event) { public static ContentValues convertAppUsageEventToContentValues(final AppUsageEvent event) {
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
values.put(AppUsageEventEntity.KEY_UID, event.getUid()); values.put(AppUsageEventEntity.KEY_UID, event.getUid());
@@ -131,7 +152,7 @@ public final class ConvertUtils {
return values; return values;
} }
/** Converts {@link BatteryEvent} to content values */ /** Converts {@link BatteryEvent} to {@link ContentValues} */
public static ContentValues convertBatteryEventToContentValues(final BatteryEvent event) { public static ContentValues convertBatteryEventToContentValues(final BatteryEvent event) {
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, event.getTimestamp()); values.put(BatteryEventEntity.KEY_TIMESTAMP, event.getTimestamp());
@@ -140,6 +161,16 @@ public final class ConvertUtils {
return values; 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. */ /** Gets the encoded string from {@link BatteryInformation} instance. */
public static String convertBatteryInformationToString( public static String convertBatteryInformationToString(
final BatteryInformation batteryInformation) { final BatteryInformation batteryInformation) {
@@ -183,7 +214,7 @@ public final class ConvertUtils {
/*isFullChargeStart=*/ false)); /*isFullChargeStart=*/ false));
} }
/** Converts to {@link AppUsageEvent} from {@link Event} */ /** Converts from {@link Event} to {@link AppUsageEvent} */
@Nullable @Nullable
public static AppUsageEvent convertToAppUsageEvent( public static AppUsageEvent convertToAppUsageEvent(
Context context, IUsageStatsManager usageStatsManager, final Event event, Context context, IUsageStatsManager usageStatsManager, final Event event,
@@ -234,8 +265,8 @@ public final class ConvertUtils {
return appUsageEventBuilder.build(); return appUsageEventBuilder.build();
} }
/** Converts to {@link AppUsageEvent} from {@link Cursor} */ /** Converts from {@link Cursor} to {@link AppUsageEvent} */
public static AppUsageEvent convertToAppUsageEventFromCursor(final Cursor cursor) { public static AppUsageEvent convertToAppUsageEvent(final Cursor cursor) {
final AppUsageEvent.Builder eventBuilder = AppUsageEvent.newBuilder(); final AppUsageEvent.Builder eventBuilder = AppUsageEvent.newBuilder();
eventBuilder.setTimestamp(getLongFromCursor(cursor, AppUsageEventEntity.KEY_TIMESTAMP)); eventBuilder.setTimestamp(getLongFromCursor(cursor, AppUsageEventEntity.KEY_TIMESTAMP));
eventBuilder.setType( eventBuilder.setType(
@@ -253,7 +284,7 @@ public final class ConvertUtils {
return eventBuilder.build(); return eventBuilder.build();
} }
/** Converts to {@link BatteryEvent} from {@link BatteryEventType} */ /** Converts from {@link BatteryEventType} to {@link BatteryEvent} */
public static BatteryEvent convertToBatteryEvent( public static BatteryEvent convertToBatteryEvent(
long timestamp, BatteryEventType type, int batteryLevel) { long timestamp, BatteryEventType type, int batteryLevel) {
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder(); final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
@@ -263,8 +294,8 @@ public final class ConvertUtils {
return eventBuilder.build(); return eventBuilder.build();
} }
/** Converts to {@link BatteryEvent} from {@link Cursor} */ /** Converts from {@link Cursor} to {@link BatteryEvent} */
public static BatteryEvent convertToBatteryEventFromCursor(final Cursor cursor) { public static BatteryEvent convertToBatteryEvent(final Cursor cursor) {
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder(); final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
eventBuilder.setTimestamp(getLongFromCursor(cursor, BatteryEventEntity.KEY_TIMESTAMP)); eventBuilder.setTimestamp(getLongFromCursor(cursor, BatteryEventEntity.KEY_TIMESTAMP));
eventBuilder.setType( eventBuilder.setType(
@@ -276,6 +307,42 @@ public final class ConvertUtils {
return eventBuilder.build(); 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 /** Converts UTC timestamp to local time string for logging only, so use the US locale for
* better readability in debugging. */ * better readability in debugging. */
public static String utcToLocalTimeForLogging(long timestamp) { 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( private static BatteryInformation constructBatteryInformation(
final BatteryEntry entry, final BatteryEntry entry,
final BatteryUsageStats batteryUsageStats, final BatteryUsageStats batteryUsageStats,

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,12 +21,13 @@ import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.provider.SearchIndexableResource; import android.provider.SearchIndexableResource;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader; import androidx.loader.content.Loader;
@@ -39,11 +40,13 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/** Advanced power usage. */ /** Advanced power usage. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -55,16 +58,17 @@ public class PowerUsageAdvanced extends PowerUsageBase {
@VisibleForTesting @VisibleForTesting
BatteryHistoryPreference mHistPref; BatteryHistoryPreference mHistPref;
@VisibleForTesting @VisibleForTesting
Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap; final BatteryLevelDataLoaderCallbacks mBatteryLevelDataLoaderCallbacks =
@VisibleForTesting new BatteryLevelDataLoaderCallbacks();
final BatteryHistoryLoaderCallbacks mBatteryHistoryLoaderCallbacks =
new BatteryHistoryLoaderCallbacks();
private boolean mIsChartDataLoaded = false; private boolean mIsChartDataLoaded = false;
private long mResumeTimestamp;
private BatteryChartPreferenceController mBatteryChartPreferenceController; private BatteryChartPreferenceController mBatteryChartPreferenceController;
private Optional<BatteryLevelData> mBatteryLevelData;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ContentObserver mBatteryObserver = private final ContentObserver mBatteryObserver =
new ContentObserver(new Handler()) { new ContentObserver(mHandler) {
@Override @Override
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
Log.d(TAG, "onBatteryContentChange: " + selfChange); Log.d(TAG, "onBatteryContentChange: " + selfChange);
@@ -79,6 +83,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
super.onCreate(icicle); super.onCreate(icicle);
mHistPref = findPreference(KEY_BATTERY_CHART); mHistPref = findPreference(KEY_BATTERY_CHART);
setBatteryChartPreferenceController(); setBatteryChartPreferenceController();
AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext()));
} }
@Override @Override
@@ -109,6 +114,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
super.onPause(); super.onPause();
// Resets the flag to reload usage data in onResume() callback. // Resets the flag to reload usage data in onResume() callback.
mIsChartDataLoaded = false; mIsChartDataLoaded = false;
mBatteryLevelData = null;
final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI; final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
if (uri != null) { if (uri != null) {
getContext().getContentResolver().unregisterContentObserver(mBatteryObserver); getContext().getContentResolver().unregisterContentObserver(mBatteryObserver);
@@ -118,6 +124,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
mResumeTimestamp = System.currentTimeMillis();
final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI; final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
if (uri != null) { if (uri != null) {
getContext().getContentResolver().registerContentObserver( getContext().getContentResolver().registerContentObserver(
@@ -158,21 +165,9 @@ public class PowerUsageAdvanced extends PowerUsageBase {
return controllers; return controllers;
} }
@Override
protected boolean isBatteryHistoryNeeded() {
return true;
}
@Override @Override
protected void refreshUi(@BatteryUpdateType int refreshType) { protected void refreshUi(@BatteryUpdateType int refreshType) {
final Context context = getContext(); // Do nothing
if (context == null) {
return;
}
updatePreference(mHistPref);
if (mBatteryChartPreferenceController != null && mBatteryHistoryMap != null) {
mBatteryChartPreferenceController.setBatteryHistoryMap(mBatteryHistoryMap);
}
} }
@Override @Override
@@ -181,11 +176,32 @@ public class PowerUsageAdvanced extends PowerUsageBase {
bundle.putInt(KEY_REFRESH_TYPE, refreshType); bundle.putInt(KEY_REFRESH_TYPE, refreshType);
if (!mIsChartDataLoaded) { if (!mIsChartDataLoaded) {
mIsChartDataLoaded = true; mIsChartDataLoaded = true;
restartLoader(LoaderIndex.BATTERY_HISTORY_LOADER, bundle, restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle,
mBatteryHistoryLoaderCallbacks); 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() { private void setBatteryChartPreferenceController() {
if (mHistPref != null && mBatteryChartPreferenceController != null) { if (mHistPref != null && mBatteryChartPreferenceController != null) {
mHistPref.setChartPreferenceController(mBatteryChartPreferenceController); mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
@@ -216,28 +232,31 @@ public class PowerUsageAdvanced extends PowerUsageBase {
} }
}; };
private class BatteryHistoryLoaderCallbacks private class BatteryLevelDataLoaderCallbacks
implements LoaderManager.LoaderCallbacks<Map<Long, Map<String, BatteryHistEntry>>> { implements LoaderManager.LoaderCallbacks<BatteryLevelData> {
private int mRefreshType;
@Override @Override
@NonNull public Loader<BatteryLevelData> onCreateLoader(int id, Bundle bundle) {
public Loader<Map<Long, Map<String, BatteryHistEntry>>> onCreateLoader( return new AsyncLoaderCompat<BatteryLevelData>(getContext().getApplicationContext()) {
int id, Bundle bundle) { @Override
mRefreshType = bundle.getInt(KEY_REFRESH_TYPE); protected void onDiscardResult(BatteryLevelData result) {}
return new BatteryHistoryLoader(getContext());
@Override
public BatteryLevelData loadInBackground() {
return DataProcessManager.getBatteryLevelData(
getContext(), mHandler, /*isFromPeriodJob=*/ false,
map -> PowerUsageAdvanced.this.onBatteryDiffDataMapUpdate(map));
}
};
} }
@Override @Override
public void onLoadFinished(Loader<Map<Long, Map<String, BatteryHistEntry>>> loader, public void onLoadFinished(Loader<BatteryLevelData> loader,
Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) { BatteryLevelData batteryLevelData) {
mBatteryHistoryMap = batteryHistoryMap; PowerUsageAdvanced.this.onBatteryLevelDataUpdate(batteryLevelData);
PowerUsageAdvanced.this.onLoadFinished(mRefreshType);
} }
@Override @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.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.BatteryBroadcastReceiver; import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
import com.android.settings.fuelgauge.BatteryUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -63,14 +62,14 @@ public abstract class PowerUsageBase extends DashboardFragment {
LoaderIndex.BATTERY_USAGE_STATS_LOADER, LoaderIndex.BATTERY_USAGE_STATS_LOADER,
LoaderIndex.BATTERY_INFO_LOADER, LoaderIndex.BATTERY_INFO_LOADER,
LoaderIndex.BATTERY_TIP_LOADER, LoaderIndex.BATTERY_TIP_LOADER,
LoaderIndex.BATTERY_HISTORY_LOADER LoaderIndex.BATTERY_LEVEL_DATA_LOADER
}) })
public @interface LoaderIndex { public @interface LoaderIndex {
int BATTERY_USAGE_STATS_LOADER = 0; int BATTERY_USAGE_STATS_LOADER = 0;
int BATTERY_INFO_LOADER = 1; int BATTERY_INFO_LOADER = 1;
int BATTERY_TIP_LOADER = 2; int BATTERY_TIP_LOADER = 2;
int BATTERY_HISTORY_LOADER = 3; int BATTERY_LEVEL_DATA_LOADER = 3;
} }
@Override @Override
@@ -108,7 +107,7 @@ public abstract class PowerUsageBase extends DashboardFragment {
protected void restartBatteryStatsLoader(int refreshType) { protected void restartBatteryStatsLoader(int refreshType) {
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putInt(KEY_REFRESH_TYPE, refreshType); 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, restartLoader(LoaderIndex.BATTERY_USAGE_STATS_LOADER, bundle,
mBatteryUsageStatsLoaderCallbacks); mBatteryUsageStatsLoaderCallbacks);
} }
@@ -137,14 +136,6 @@ public abstract class PowerUsageBase extends DashboardFragment {
protected abstract void refreshUi(@BatteryUpdateType int refreshType); 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 private class BatteryUsageStatsLoaderCallbacks
implements LoaderManager.LoaderCallbacks<BatteryUsageStats> { implements LoaderManager.LoaderCallbacks<BatteryUsageStats> {
private int mRefreshType; 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.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List; import java.util.List;
@@ -69,8 +68,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
@VisibleForTesting @VisibleForTesting
BatteryUtils mBatteryUtils; BatteryUtils mBatteryUtils;
@VisibleForTesting @VisibleForTesting
LayoutPreference mBatteryLayoutPref;
@VisibleForTesting
BatteryInfo mBatteryInfo; BatteryInfo mBatteryInfo;
@VisibleForTesting @VisibleForTesting
@@ -208,11 +205,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
return R.string.help_url_battery; return R.string.help_url_battery;
} }
@Override
protected boolean isBatteryHistoryNeeded() {
return false;
}
protected void refreshUi(@BatteryUpdateType int refreshType) { protected void refreshUi(@BatteryUpdateType int refreshType) {
final Context context = getContext(); final Context context = getContext();
if (context == null) { if (context == null) {
@@ -239,11 +231,6 @@ public class PowerUsageSummary extends PowerUsageBase implements
restartLoader(LoaderIndex.BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks); restartLoader(LoaderIndex.BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
} }
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting @VisibleForTesting
void initFeatureProvider() { void initFeatureProvider() {
final Context context = getContext(); final Context context = getContext();

View File

@@ -36,9 +36,16 @@ public interface BatteryEventDao {
@Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC") @Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC")
List<BatteryEventEntity> getAll(); 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. */ /** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC") @Query("SELECT * FROM BatteryEventEntity"
Cursor getAllAfter(long timestamp); + " 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. */ /** Deletes all recorded data before a specific timestamp. */
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp") @Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")

View File

@@ -37,16 +37,18 @@ public interface BatteryStateDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<BatteryState> states); 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. */ /** Lists all recorded data after a specific timestamp. */
@Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC") @Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC")
List<BatteryState> getAllAfter(long timestamp); 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. */ /** Get the count of distinct timestamp after a specific timestamp. */
@Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp") @Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp")
int getDistinctTimestampCount(long timestamp); int getDistinctTimestampCount(long timestamp);

View File

@@ -25,7 +25,8 @@ import androidx.room.RoomDatabase;
/** A {@link RoomDatabase} for battery usage states history. */ /** A {@link RoomDatabase} for battery usage states history. */
@Database( @Database(
entities = {AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class}, entities = {AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class,
BatteryUsageSlotEntity.class},
version = 1) version = 1)
public abstract class BatteryStateDatabase extends RoomDatabase { public abstract class BatteryStateDatabase extends RoomDatabase {
private static final String TAG = "BatteryStateDatabase"; private static final String TAG = "BatteryStateDatabase";
@@ -38,13 +39,15 @@ public abstract class BatteryStateDatabase extends RoomDatabase {
public abstract BatteryEventDao batteryEventDao(); public abstract BatteryEventDao batteryEventDao();
/** Provides DAO for battery state table. */ /** Provides DAO for battery state table. */
public abstract BatteryStateDao batteryStateDao(); public abstract BatteryStateDao batteryStateDao();
/** Provides DAO for battery usage slot table. */
public abstract BatteryUsageSlotDao batteryUsageSlotDao();
/** Gets or creates an instance of {@link RoomDatabase}. */ /** Gets or creates an instance of {@link RoomDatabase}. */
public static BatteryStateDatabase getInstance(Context context) { public static BatteryStateDatabase getInstance(Context context) {
if (sBatteryStateDatabase == null) { if (sBatteryStateDatabase == null) {
sBatteryStateDatabase = sBatteryStateDatabase =
Room.databaseBuilder( 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. // Allows accessing data in the main thread for dumping bugreport.
.allowMainThreadQueries() .allowMainThreadQueries()
.fallbackToDestructiveMigration() .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"], srcs: ["battery_event.proto"],
} }
java_library {
name: "battery-usage-slot-protos-lite",
proto: {
type: "lite",
},
srcs: ["battery_usage_slot.proto"],
}
java_library { java_library {
name: "fuelgauge-usage-state-protos-lite", name: "fuelgauge-usage-state-protos-lite",
proto: { proto: {

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,7 +27,6 @@ import android.widget.TextView;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryInfo;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -42,8 +41,6 @@ public class BatteryHistoryPreferenceTest {
@Mock @Mock
private PreferenceViewHolder mViewHolder; private PreferenceViewHolder mViewHolder;
@Mock @Mock
private BatteryInfo mBatteryInfo;
@Mock
private TextView mTextView; private TextView mTextView;
@Mock @Mock
private BatteryChartView mDailyChartView; private BatteryChartView mDailyChartView;
@@ -59,7 +56,6 @@ public class BatteryHistoryPreferenceTest {
LayoutInflater.from(context).inflate(R.layout.battery_chart_graph, null); LayoutInflater.from(context).inflate(R.layout.battery_chart_graph, null);
mBatteryHistoryPreference = new BatteryHistoryPreference(context, null); mBatteryHistoryPreference = new BatteryHistoryPreference(context, null);
mBatteryHistoryPreference.mBatteryInfo = mBatteryInfo;
mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(itemView)); mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(itemView));
when(mViewHolder.findViewById(R.id.daily_battery_chart)).thenReturn(mDailyChartView); when(mViewHolder.findViewById(R.id.daily_battery_chart)).thenReturn(mDailyChartView);
when(mViewHolder.findViewById(R.id.hourly_battery_chart)).thenReturn(mHourlyChartView); 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; mBatteryUsageBreakdownController.mAppListPreferenceGroup = mAppListPreferenceGroup;
mBatteryDiffEntry = new BatteryDiffEntry( mBatteryDiffEntry = new BatteryDiffEntry(
mContext, mContext,
/*uid=*/ 0L,
/*userId=*/ 0L,
/*key=*/ "key",
/*isHidden=*/ false,
/*componentId=*/ -1,
/*legacyPackageName=*/ null,
/*legacyLabel=*/ null,
/*consumerType=*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 1, /*foregroundUsageTimeInMs=*/ 1,
/*backgroundUsageTimeInMs=*/ 2, /*backgroundUsageTimeInMs=*/ 2,
/*screenOnTimeInMs=*/ 0, /*screenOnTimeInMs=*/ 0,
@@ -103,13 +111,14 @@ public final class BatteryUsageBreakdownControllerTest {
/*foregroundUsageConsumePower=*/ 0, /*foregroundUsageConsumePower=*/ 0,
/*foregroundServiceUsageConsumePower=*/ 1, /*foregroundServiceUsageConsumePower=*/ 1,
/*backgroundUsageConsumePower=*/ 2, /*backgroundUsageConsumePower=*/ 2,
/*cachedUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0);
mBatteryHistEntry);
mBatteryDiffEntry = spy(mBatteryDiffEntry); mBatteryDiffEntry = spy(mBatteryDiffEntry);
mBatteryUsageBreakdownController.mBatteryDiffData = mBatteryUsageBreakdownController.mBatteryDiffData =
new BatteryDiffData(mContext, /* screenOnTime= */ 0L, new BatteryDiffData(mContext, /* startTimestamp= */ 0L, /* endTimestamp= */ 0L,
Arrays.asList(mBatteryDiffEntry), Arrays.asList(), Set.of(), Set.of(), /* startBatteryLevel= */ 0, /* endBatteryLevel= */ 0,
/* isAccumulated= */ false); /* screenOnTime= */ 0L, Arrays.asList(mBatteryDiffEntry), Arrays.asList(),
Set.of(), Set.of(), /* isAccumulated= */ false);
BatteryDiffEntry.clearCache();
// Adds fake testing data. // Adds fake testing data.
BatteryDiffEntry.sResourceCache.put( BatteryDiffEntry.sResourceCache.put(
"fakeBatteryDiffEntryKey", "fakeBatteryDiffEntryKey",
@@ -140,7 +149,7 @@ public final class BatteryUsageBreakdownControllerTest {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel(); 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(null).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
doReturn(false).when(mBatteryDiffEntry).validForRestriction(); doReturn(false).when(mBatteryDiffEntry).validForRestriction();
@@ -168,7 +177,7 @@ public final class BatteryUsageBreakdownControllerTest {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel(); doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey(); doReturn(PREF_KEY).when(mBatteryDiffEntry).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
mBatteryUsageBreakdownController.addAllPreferences(); mBatteryUsageBreakdownController.addAllPreferences();
@@ -197,7 +206,7 @@ public final class BatteryUsageBreakdownControllerTest {
public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() { public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0); 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(PREF_KEY).when(mPowerGaugePreference).getKey();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
// Ensures the testing data is correct. // Ensures the testing data is correct.
@@ -222,7 +231,7 @@ public final class BatteryUsageBreakdownControllerTest {
@Test @Test
public void handlePreferenceTreeClick_forAppEntry_returnTrue() { public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
doReturn(false).when(mBatteryHistEntry).isAppEntry(); mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY;
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick( assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
@@ -238,7 +247,7 @@ public final class BatteryUsageBreakdownControllerTest {
@Test @Test
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() { public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
doReturn(true).when(mBatteryHistEntry).isAppEntry(); mBatteryDiffEntry.mConsumerType = ConvertUtils.CONSUMER_TYPE_UID_BATTERY;
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick( assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
@@ -394,10 +403,23 @@ public final class BatteryUsageBreakdownControllerTest {
contentValues.put(BatteryHistEntry.KEY_USER_ID, Integer.valueOf(1001)); contentValues.put(BatteryHistEntry.KEY_USER_ID, Integer.valueOf(1001));
final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(contentValues); final BatteryHistEntry batteryHistEntry = new BatteryHistEntry(contentValues);
return new BatteryDiffEntry( return new BatteryDiffEntry(
mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs, screenOnTimeInMs, mContext,
/*consumePower=*/ 0, /*foregroundUsageConsumePower=*/ 0, batteryHistEntry.mUid,
/*foregroundServiceUsageConsumePower=*/ 0, /*backgroundUsageConsumePower=*/ 0, batteryHistEntry.mUserId,
/*cachedUsageConsumePower=*/ 0, batteryHistEntry); 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() { private BatteryUsageBreakdownController createController() {

View File

@@ -20,11 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import android.app.Application;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; 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.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState; import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; 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.BatteryTestUtils;
import com.android.settings.testutils.FakeClock; import com.android.settings.testutils.FakeClock;
@@ -41,12 +40,10 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** Tests for {@link BatteryUsageContentProvider}. */ /** Tests for {@link BatteryUsageContentProvider}. */
@@ -126,11 +123,29 @@ public final class BatteryUsageContentProviderTest {
() -> mProvider.insert(uri, /*contentValues=*/ null)); () -> 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 @Test
public void query_batteryState_returnsExpectedResult() throws Exception { public void query_batteryState_returnsExpectedResult() throws Exception {
mProvider.onCreate(); mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52); 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)); final Cursor cursor = insertBatteryState(currentTime, Long.toString(expiredTimeCutoff));
@@ -150,19 +165,13 @@ public final class BatteryUsageContentProviderTest {
final String actualPackageName3 = cursor.getString(packageNameIndex); final String actualPackageName3 = cursor.getString(packageNameIndex);
assertThat(actualPackageName3).isEqualTo(PACKAGE_NAME3); assertThat(actualPackageName3).isEqualTo(PACKAGE_NAME3);
cursor.close(); 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 @Test
public void query_batteryStateTimestamp_returnsExpectedResult() throws Exception { public void query_batteryStateTimestamp_returnsExpectedResult() throws Exception {
mProvider.onCreate(); mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52); 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)); final Cursor cursor = insertBatteryState(currentTime, Long.toString(expiredTimeCutoff));
@@ -178,12 +187,25 @@ public final class BatteryUsageContentProviderTest {
final String actualPackageName2 = cursor.getString(packageNameIndex); final String actualPackageName2 = cursor.getString(packageNameIndex);
assertThat(actualPackageName2).isEqualTo(PACKAGE_NAME3); assertThat(actualPackageName2).isEqualTo(PACKAGE_NAME3);
cursor.close(); cursor.close();
// Verifies the broadcast intent. }
TimeUnit.SECONDS.sleep(1);
final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents(); @Test
assertThat(intents).hasSize(1); public void query_getBatteryStateLatestTimestamp_returnsExpectedResult() throws Exception {
assertThat(intents.get(0).getAction()).isEqualTo( mProvider.onCreate();
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK); 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 @Test
@@ -355,7 +377,7 @@ public final class BatteryUsageContentProviderTest {
} }
@Test @Test
public void insert_batteryEvent_returnsExpectedResult() { public void insertAndQuery_batteryEvent_returnsExpectedResult() {
mProvider.onCreate(); mProvider.onCreate();
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(BatteryEventEntity.KEY_TIMESTAMP, 10001L); values.put(BatteryEventEntity.KEY_TIMESTAMP, 10001L);
@@ -366,7 +388,7 @@ public final class BatteryUsageContentProviderTest {
final Uri uri = mProvider.insert(DatabaseUtils.BATTERY_EVENT_URI, values); final Uri uri = mProvider.insert(DatabaseUtils.BATTERY_EVENT_URI, values);
assertThat(uri).isEqualTo(DatabaseUtils.BATTERY_EVENT_URI); assertThat(uri).isEqualTo(DatabaseUtils.BATTERY_EVENT_URI);
// Verifies the AppUsageEventEntity content. // Verifies the BatteryEventEntity content.
final List<BatteryEventEntity> entities = final List<BatteryEventEntity> entities =
BatteryStateDatabase.getInstance(mContext).batteryEventDao().getAll(); BatteryStateDatabase.getInstance(mContext).batteryEventDao().getAll();
assertThat(entities).hasSize(1); assertThat(entities).hasSize(1);
@@ -374,6 +396,50 @@ public final class BatteryUsageContentProviderTest {
assertThat(entities.get(0).batteryEventType).isEqualTo( assertThat(entities.get(0).batteryEventType).isEqualTo(
BatteryEventType.POWER_CONNECTED.getNumber()); BatteryEventType.POWER_CONNECTED.getNumber());
assertThat(entities.get(0).batteryLevel).isEqualTo(66); 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 @Test
@@ -404,10 +470,10 @@ public final class BatteryUsageContentProviderTest {
final long currentTimestamp = currentTime.toMillis(); final long currentTimestamp = currentTime.toMillis();
// Inserts some valid testing data. // Inserts some valid testing data.
BatteryTestUtils.insertDataToBatteryStateTable( BatteryTestUtils.insertDataToBatteryStateTable(
mContext, currentTimestamp - 2, PACKAGE_NAME1, mContext, currentTimestamp - 6, PACKAGE_NAME1,
/*isFullChargeStart=*/ true); /*isFullChargeStart=*/ true);
BatteryTestUtils.insertDataToBatteryStateTable( BatteryTestUtils.insertDataToBatteryStateTable(
mContext, currentTimestamp - 1, PACKAGE_NAME2); mContext, currentTimestamp - 2, PACKAGE_NAME2);
BatteryTestUtils.insertDataToBatteryStateTable( BatteryTestUtils.insertDataToBatteryStateTable(
mContext, currentTimestamp, PACKAGE_NAME3); mContext, currentTimestamp, PACKAGE_NAME3);
@@ -420,17 +486,35 @@ public final class BatteryUsageContentProviderTest {
DatabaseUtils.QUERY_KEY_TIMESTAMP, queryTimestamp) DatabaseUtils.QUERY_KEY_TIMESTAMP, queryTimestamp)
.build(); .build();
final Cursor cursor = final Cursor cursor = query(batteryStateQueryContentUri);
mProvider.query(
batteryStateQueryContentUri,
/*strings=*/ null,
/*s=*/ null,
/*strings1=*/ null,
/*s1=*/ null);
return cursor; 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() { private void insertAppUsageEvent() {
mProvider.onCreate(); mProvider.onCreate();
// Inserts some valid testing data. // Inserts some valid testing data.
@@ -452,12 +536,7 @@ public final class BatteryUsageContentProviderTest {
DatabaseUtils.QUERY_KEY_USERID, Long.toString(userId)) DatabaseUtils.QUERY_KEY_USERID, Long.toString(userId))
.build(); .build();
return mProvider.query( return query(appUsageLatestTimestampQueryContentUri);
appUsageLatestTimestampQueryContentUri,
/*strings=*/ null,
/*s=*/ null,
/*strings1=*/ null,
/*s1=*/ null);
} }
private Cursor getCursorOfAppUsage(final List<Long> userIds, final long queryTimestamp) { private Cursor getCursorOfAppUsage(final List<Long> userIds, final long queryTimestamp) {
@@ -474,7 +553,43 @@ public final class BatteryUsageContentProviderTest {
.appendQueryParameter(DatabaseUtils.QUERY_KEY_USERID, queryUserIdString) .appendQueryParameter(DatabaseUtils.QUERY_KEY_USERID, queryUserIdString)
.build(); .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( 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.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.ContentResolver; import android.content.ContentResolver;
@@ -31,6 +32,7 @@ import android.content.pm.PackageManager;
import android.os.BatteryStatsManager; import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats; import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery; import android.os.BatteryUsageStatsQuery;
import android.os.UserManager;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -43,6 +45,7 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@@ -56,6 +59,8 @@ public final class BatteryUsageDataLoaderTest {
@Mock @Mock
private PackageManager mPackageManager; private PackageManager mPackageManager;
@Mock @Mock
private UserManager mUserManager;
@Mock
private BatteryUsageStats mBatteryUsageStats; private BatteryUsageStats mBatteryUsageStats;
@Mock @Mock
private BatteryEntry mMockBatteryEntry; private BatteryEntry mMockBatteryEntry;
@@ -70,6 +75,7 @@ public final class BatteryUsageDataLoaderTest {
doReturn(mBatteryStatsManager).when(mContext).getSystemService( doReturn(mBatteryStatsManager).when(mContext).getSystemService(
Context.BATTERY_STATS_SERVICE); Context.BATTERY_STATS_SERVICE);
doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
doReturn(mMockContentResolver).when(mContext).getContentResolver(); doReturn(mMockContentResolver).when(mContext).getContentResolver();
doReturn(new Intent()).when(mContext).registerReceiver(any(), any()); doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
} }
@@ -82,7 +88,7 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats); .thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> batteryEntryList; BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> batteryEntryList;
BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false); BatteryUsageDataLoader.loadBatteryStatsData(mContext, /*isFullChargeStart=*/ false);
final int queryFlags = mStatsQueryCaptor.getValue().getFlags(); final int queryFlags = mStatsQueryCaptor.getValue().getFlags();
assertThat(queryFlags assertThat(queryFlags
@@ -97,7 +103,7 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats); .thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> null; BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> null;
BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false); BatteryUsageDataLoader.loadBatteryStatsData(mContext, /*isFullChargeStart=*/ false);
verify(mMockContentResolver).insert(any(), any()); verify(mMockContentResolver).insert(any(), any());
} }
@@ -108,8 +114,51 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats); .thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> new ArrayList<>(); BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> new ArrayList<>();
BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false); BatteryUsageDataLoader.loadBatteryStatsData(mContext, /*isFullChargeStart=*/ false);
verify(mMockContentResolver).insert(any(), any()); 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.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity; import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -48,7 +49,10 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@@ -212,6 +216,22 @@ public final class ConvertUtilsTest {
assertThat(values.getAsInteger(BatteryEventEntity.KEY_BATTERY_LEVEL)).isEqualTo(66); 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 @Test
public void convertToBatteryHistEntry_returnsExpectedResult() { public void convertToBatteryHistEntry_returnsExpectedResult() {
final int expectedType = 3; final int expectedType = 3;
@@ -363,7 +383,7 @@ public final class ConvertUtilsTest {
} }
@Test @Test
public void convertToAppUsageEventFromCursor_returnExpectedResult() { public void convertToAppUsageEvent_returnExpectedResult() {
final MatrixCursor cursor = new MatrixCursor( final MatrixCursor cursor = new MatrixCursor(
new String[]{ new String[]{
AppUsageEventEntity.KEY_UID, AppUsageEventEntity.KEY_UID,
@@ -384,7 +404,7 @@ public final class ConvertUtilsTest {
100001L}); 100001L});
cursor.moveToFirst(); cursor.moveToFirst();
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEventFromCursor(cursor); final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(cursor);
assertThat(appUsageEvent.getUid()).isEqualTo(101L); assertThat(appUsageEvent.getUid()).isEqualTo(101L);
assertThat(appUsageEvent.getUserId()).isEqualTo(1001L); assertThat(appUsageEvent.getUserId()).isEqualTo(1001L);
@@ -396,7 +416,7 @@ public final class ConvertUtilsTest {
} }
@Test @Test
public void convertToAppUsageEventFromCursor_emptyInstanceIdAndRootName_returnExpectedResult() { public void convertToAppUsageEvent_emptyInstanceIdAndRootName_returnExpectedResult() {
final MatrixCursor cursor = new MatrixCursor( final MatrixCursor cursor = new MatrixCursor(
new String[]{ new String[]{
AppUsageEventEntity.KEY_UID, AppUsageEventEntity.KEY_UID,
@@ -413,7 +433,7 @@ public final class ConvertUtilsTest {
AppUsageEventType.DEVICE_SHUTDOWN.getNumber()}); AppUsageEventType.DEVICE_SHUTDOWN.getNumber()});
cursor.moveToFirst(); cursor.moveToFirst();
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEventFromCursor(cursor); final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(cursor);
assertThat(appUsageEvent.getUid()).isEqualTo(101L); assertThat(appUsageEvent.getUid()).isEqualTo(101L);
assertThat(appUsageEvent.getUserId()).isEqualTo(1001L); assertThat(appUsageEvent.getUserId()).isEqualTo(1001L);
@@ -433,6 +453,42 @@ public final class ConvertUtilsTest {
assertThat(batteryEvent.getBatteryLevel()).isEqualTo(88); 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 @Test
public void getLocale_nullContext_returnDefaultLocale() { public void getLocale_nullContext_returnDefaultLocale() {
assertThat(ConvertUtils.getLocale(/*context=*/ null)) assertThat(ConvertUtils.getLocale(/*context=*/ null))

View File

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

View File

@@ -16,6 +16,9 @@
package com.android.settings.fuelgauge.batteryusage; 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 com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyInt;
@@ -42,6 +45,7 @@ import android.os.BatteryUsageStats;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserManager; import android.os.UserManager;
import android.util.ArrayMap;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
@@ -188,16 +192,18 @@ public final class DataProcessorTest {
final String packageName = "com.android.settings"; final String packageName = "com.android.settings";
// Adds the day 1 data. // Adds the day 1 data.
final List<Long> timestamps1 = List.of(14400000L, 18000000L, 21600000L); 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( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps1, levels1)); new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap1, timestamps1));
// Adds the day 2 data. // Adds the day 2 data.
hourlyBatteryLevelsPerDay.add(null); hourlyBatteryLevelsPerDay.add(null);
// Adds the day 3 data. // Adds the day 3 data.
final List<Long> timestamps2 = List.of(45200000L, 48800000L); 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( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps2, levels2)); new BatteryLevelData.PeriodBatteryLevelData(batteryLevelMap2, timestamps2));
final List<AppUsageEvent> appUsageEventList = new ArrayList<>(); final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
// Adds some events before the start timestamp. // Adds some events before the start timestamp.
appUsageEventList.add(buildAppUsageEvent( appUsageEventList.add(buildAppUsageEvent(
@@ -285,7 +291,7 @@ public final class DataProcessorTest {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>(); new ArrayList<>();
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>())); new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>()));
assertThat(DataProcessor.generateAppUsagePeriodMap( assertThat(DataProcessor.generateAppUsagePeriodMap(
mContext, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>())) mContext, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>()))
.isNull(); .isNull();
@@ -370,19 +376,6 @@ public final class DataProcessorTest {
DataProcessor.CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)).isTrue(); 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 @Test
public void getLevelDataThroughProcessedHistoryMap_OneDayData_returnExpectedResult() { public void getLevelDataThroughProcessedHistoryMap_OneDayData_returnExpectedResult() {
// Timezone GMT+8 // Timezone GMT+8
@@ -441,7 +434,7 @@ public final class DataProcessorTest {
); );
final List<Integer> expectedDailyLevels = new ArrayList<>(); final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100); expectedDailyLevels.add(100);
expectedDailyLevels.add(null); expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(82); expectedDailyLevels.add(82);
final List<List<Long>> expectedHourlyTimestamps = List.of( final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of( List.of(
@@ -459,13 +452,13 @@ public final class DataProcessorTest {
); );
final List<Integer> expectedHourlyLevels1 = new ArrayList<>(); final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100); expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>(); final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(94); expectedHourlyLevels2.add(94);
expectedHourlyLevels2.add(90); expectedHourlyLevels2.add(90);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(82); expectedHourlyLevels2.add(82);
final List<List<Integer>> expectedHourlyLevels = List.of( final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1, expectedHourlyLevels1,
@@ -503,8 +496,8 @@ public final class DataProcessorTest {
); );
final List<Integer> expectedDailyLevels = new ArrayList<>(); final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100); expectedDailyLevels.add(100);
expectedDailyLevels.add(null); expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(null); expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(88); expectedDailyLevels.add(88);
final List<List<Long>> expectedHourlyTimestamps = List.of( final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of( List.of(
@@ -542,32 +535,32 @@ public final class DataProcessorTest {
); );
final List<Integer> expectedHourlyLevels1 = new ArrayList<>(); final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100); expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>(); final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels3 = new ArrayList<>(); final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
expectedHourlyLevels3.add(null); expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(null); expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(null); expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(88); expectedHourlyLevels3.add(88);
final List<List<Integer>> expectedHourlyLevels = List.of( final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1, expectedHourlyLevels1,
@@ -606,8 +599,8 @@ public final class DataProcessorTest {
); );
final List<Integer> expectedDailyLevels = new ArrayList<>(); final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100); expectedDailyLevels.add(100);
expectedDailyLevels.add(null); expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(null); expectedDailyLevels.add(BATTERY_LEVEL_UNKNOWN);
expectedDailyLevels.add(88); expectedDailyLevels.add(88);
final List<List<Long>> expectedHourlyTimestamps = List.of( final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of( List.of(
@@ -638,25 +631,25 @@ public final class DataProcessorTest {
); );
final List<Integer> expectedHourlyLevels1 = new ArrayList<>(); final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100); expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null); expectedHourlyLevels1.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>(); final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels2.add(null); expectedHourlyLevels2.add(BATTERY_LEVEL_UNKNOWN);
final List<Integer> expectedHourlyLevels3 = new ArrayList<>(); final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
expectedHourlyLevels3.add(null); expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(null); expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(null); expectedHourlyLevels3.add(BATTERY_LEVEL_UNKNOWN);
expectedHourlyLevels3.add(88); expectedHourlyLevels3.add(88);
final List<List<Integer>> expectedHourlyLevels = List.of( final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1, expectedHourlyLevels1,
@@ -734,141 +727,6 @@ public final class DataProcessorTest {
verifyExpectedTimestampSlots(startTimestamp, endTimestamp, expectedTimestamps); 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 @Test
public void isFromFullCharge_emptyData_returnFalse() { public void isFromFullCharge_emptyData_returnFalse() {
assertThat(DataProcessor.isFromFullCharge(null)).isFalse(); assertThat(DataProcessor.isFromFullCharge(null)).isFalse();
@@ -916,20 +774,53 @@ public final class DataProcessorTest {
} }
@Test @Test
public void getBatteryUsageMap_emptyHistoryMap_returnNull() { public void getBatteryDiffDataMap_emptyHistoryMap_returnEmpty() {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>(); new ArrayList<>();
hourlyBatteryLevelsPerDay.add( hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>())); new BatteryLevelData.PeriodBatteryLevelData(new ArrayMap<>(), new ArrayList<>()));
assertThat(DataProcessor.getBatteryUsageMap( assertThat(DataProcessor.getBatteryDiffDataMap(mContext, hourlyBatteryLevelsPerDay,
mContext, hourlyBatteryLevelsPerDay, new HashMap<>(), /*appUsagePeriodMap=*/ null)) new HashMap<>(), /*appUsagePeriodMap=*/ null, Set.of(), Set.of())).isEmpty();
.isNull();
} }
@Test @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[]{ final long[] batteryHistoryKeys = new long[]{
1641045600000L, // 2022-01-01 22:00:00 1641045600000L, // 2022-01-01 22:00:00
1641049200000L, // 2022-01-01 23: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 Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId(); final int currentUserId = mContext.getUserId();
final BatteryHistEntry fakeEntry = createBatteryHistEntry( final BatteryHistEntry fakeEntry = createBatteryHistEntry(
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0, FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0,
/*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0, /*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
/*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0, /*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
/*uid=*/ 0L, currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*uid=*/ 0L, currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
@@ -1030,19 +921,7 @@ public final class DataProcessorTest {
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry); entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[4], entryMap); batteryHistoryMap.put(batteryHistoryKeys[4], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
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));
// Adds app usage data to test screen on time. // Adds app usage data to test screen on time.
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
appUsagePeriodMap = new HashMap<>(); appUsagePeriodMap = new HashMap<>();
@@ -1066,8 +945,12 @@ public final class DataProcessorTest {
appUsagePeriodMap.get(1).put(0, appUsageMap); appUsagePeriodMap.get(1).put(0, appUsageMap);
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap( DataProcessor.generateBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, appUsagePeriodMap); mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
appUsagePeriodMap, Set.of(), Set.of()),
batteryLevelData);
BatteryDiffData resultDiffData = BatteryDiffData resultDiffData =
resultMap resultMap
@@ -1128,7 +1011,7 @@ public final class DataProcessorTest {
} }
@Test @Test
public void getBatteryUsageMap_multipleUsers_returnsExpectedResult() { public void generateBatteryUsageMap_multipleUsers_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{ final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00 1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00 1641056400000L, // 2022-01-02 01:00:00
@@ -1217,17 +1100,15 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 30L, /*isHidden=*/ false); /*backgroundUsageTimeInMs=*/ 30L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap); batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
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 Map<Integer, Map<Integer, BatteryDiffData>> resultMap = final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap( DataProcessor.generateBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, mContext,
/*appUsagePeriodMap=*/ null); DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
/*appUsagePeriodMap=*/ null, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData = final BatteryDiffData resultDiffData =
resultMap resultMap
@@ -1247,7 +1128,7 @@ public final class DataProcessorTest {
} }
@Test @Test
public void getBatteryUsageMap_usageTimeExceed_returnsExpectedResult() { public void generateBatteryUsageMap_usageTimeExceed_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{ final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00 1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00 1641056400000L, // 2022-01-02 01:00:00
@@ -1288,12 +1169,7 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 7200000L, /*isHidden=*/ false); /*backgroundUsageTimeInMs=*/ 7200000L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap); batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
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));
// Adds app usage data to test screen on time. // Adds app usage data to test screen on time.
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> 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); appUsagePeriodMap.get(0).put(0, appUsageMap);
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap( DataProcessor.generateBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, appUsagePeriodMap); mContext,
DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
appUsagePeriodMap, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData = final BatteryDiffData resultDiffData =
resultMap resultMap
@@ -1338,7 +1218,7 @@ public final class DataProcessorTest {
} }
@Test @Test
public void getBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() { public void generateBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{ final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00 1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00 1641056400000L, // 2022-01-02 01:00:00
@@ -1403,19 +1283,17 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 20L, /*isHidden=*/ false); /*backgroundUsageTimeInMs=*/ 20L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap); batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
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));
when(mPowerUsageFeatureProvider.getHideApplicationSet()) when(mPowerUsageFeatureProvider.getHideApplicationSet())
.thenReturn(Set.of("package1")); .thenReturn(Set.of("package1"));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap( DataProcessor.generateBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, mContext,
/*appUsagePeriodMap=*/ null); DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
/*appUsagePeriodMap=*/ null, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData = final BatteryDiffData resultDiffData =
resultMap resultMap
@@ -1431,7 +1309,7 @@ public final class DataProcessorTest {
} }
@Test @Test
public void getBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() { public void generateBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{ final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00 1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00 1641056400000L, // 2022-01-02 01:00:00
@@ -1496,19 +1374,17 @@ public final class DataProcessorTest {
/*backgroundUsageTimeInMs=*/ 20L, /*isHidden=*/ false); /*backgroundUsageTimeInMs=*/ 20L, /*isHidden=*/ false);
entryMap.put(entry.getKey(), entry); entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap); batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay = final BatteryLevelData batteryLevelData = generateBatteryLevelData(batteryHistoryKeys);
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));
when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet()) when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet())
.thenReturn(new HashSet(Arrays.asList((CharSequence) "package2"))); .thenReturn(new HashSet(Arrays.asList((CharSequence) "package2")));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap( DataProcessor.generateBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap, mContext,
/*appUsagePeriodMap=*/ null); DataProcessor.getBatteryDiffDataMap(mContext,
batteryLevelData.getHourlyBatteryLevelsPerDay(), batteryHistoryMap,
/*appUsagePeriodMap=*/ null, Set.of(), Set.of()),
batteryLevelData);
final BatteryDiffData resultDiffData = final BatteryDiffData resultDiffData =
resultMap resultMap
@@ -1523,7 +1399,10 @@ public final class DataProcessorTest {
@Test @Test
public void generateBatteryDiffData_emptyBatteryEntryList_returnNull() { public void generateBatteryDiffData_emptyBatteryEntryList_returnNull() {
assertThat(DataProcessor.generateBatteryDiffData(mContext, assertThat(DataProcessor.generateBatteryDiffData(mContext,
DataProcessor.convertToBatteryHistEntry(null, mBatteryUsageStats))).isNull(); System.currentTimeMillis(),
DataProcessor.convertToBatteryHistEntry(null, mBatteryUsageStats),
/* systemAppsPackageNames= */ Set.of(),
/* systemAppsUids= */ Set.of())).isNull();
} }
@Test @Test
@@ -1574,7 +1453,10 @@ public final class DataProcessorTest {
.when(mMockBatteryEntry4).getPowerComponentId(); .when(mMockBatteryEntry4).getPowerComponentId();
final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData(mContext, final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData(mContext,
DataProcessor.convertToBatteryHistEntry(batteryEntryList, mBatteryUsageStats)); System.currentTimeMillis(),
DataProcessor.convertToBatteryHistEntry(batteryEntryList, mBatteryUsageStats),
/* systemAppsPackageNames= */ Set.of(),
/* systemAppsUids= */ Set.of());
assertBatteryDiffEntry( assertBatteryDiffEntry(
batteryDiffData.getAppDiffEntryList().get(0), 0, /*uid=*/ 2L, batteryDiffData.getAppDiffEntryList().get(0), 0, /*uid=*/ 2L,
@@ -2041,9 +1923,9 @@ public final class DataProcessorTest {
final double backgroundUsageConsumePower, final double cachedUsageConsumePower, final double backgroundUsageConsumePower, final double cachedUsageConsumePower,
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs, final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs,
final long screenOnTimeInMs) { final long screenOnTimeInMs) {
assertThat(entry.mBatteryHistEntry.mUserId).isEqualTo(userId); assertThat(entry.mUserId).isEqualTo(userId);
assertThat(entry.mBatteryHistEntry.mUid).isEqualTo(uid); assertThat(entry.mUid).isEqualTo(uid);
assertThat(entry.mBatteryHistEntry.mConsumerType).isEqualTo(consumerType); assertThat(entry.mConsumerType).isEqualTo(consumerType);
assertThat(entry.getPercentage()).isEqualTo(consumePercentage); assertThat(entry.getPercentage()).isEqualTo(consumePercentage);
assertThat(entry.mForegroundUsageConsumePower).isEqualTo(foregroundUsageConsumePower); assertThat(entry.mForegroundUsageConsumePower).isEqualTo(foregroundUsageConsumePower);
assertThat(entry.mForegroundServiceUsageConsumePower) assertThat(entry.mForegroundServiceUsageConsumePower)
@@ -2054,4 +1936,12 @@ public final class DataProcessorTest {
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs); assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
assertThat(entry.mScreenOnTimeInMs).isEqualTo(screenOnTimeInMs); 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()); doReturn(null).when(mContext).registerReceiver(any(), any());
assertThat( assertThat(
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, /*batteryEntryList=*/ null, mBatteryUsageStats, mContext, System.currentTimeMillis(), /*batteryEntryList=*/ null,
/*isFullChargeStart=*/ false)) mBatteryUsageStats, /*isFullChargeStart=*/ false))
.isNull(); .isNull();
} }
@@ -193,7 +193,10 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList = final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, batteryEntryList, mBatteryUsageStats, mContext,
System.currentTimeMillis(),
batteryEntryList,
mBatteryUsageStats,
/*isFullChargeStart=*/ false); /*isFullChargeStart=*/ false);
assertThat(valuesList).hasSize(2); assertThat(valuesList).hasSize(2);
@@ -216,6 +219,7 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList = final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, mContext,
System.currentTimeMillis(),
new ArrayList<>(), new ArrayList<>(),
mBatteryUsageStats, mBatteryUsageStats,
/*isFullChargeStart=*/ false); /*isFullChargeStart=*/ false);
@@ -235,6 +239,7 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList = final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, mContext,
System.currentTimeMillis(),
/*batteryEntryList=*/ null, /*batteryEntryList=*/ null,
mBatteryUsageStats, mBatteryUsageStats,
/*isFullChargeStart=*/ false); /*isFullChargeStart=*/ false);
@@ -254,6 +259,7 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList = final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, mContext,
System.currentTimeMillis(),
/*batteryEntryList=*/ null, /*batteryEntryList=*/ null,
/*batteryUsageStats=*/ null, /*batteryUsageStats=*/ null,
/*isFullChargeStart=*/ false); /*isFullChargeStart=*/ false);
@@ -359,7 +365,7 @@ public final class DatabaseUtilsTest {
} }
@Test @Test
public void getHistoryMapSinceLastFullCharge_emptyCursorContent_returnEmptyMap() { public void getHistoryMap_emptyCursorContent_returnEmptyMap() {
final MatrixCursor cursor = new MatrixCursor( final MatrixCursor cursor = new MatrixCursor(
new String[] { new String[] {
BatteryHistEntry.KEY_UID, BatteryHistEntry.KEY_UID,
@@ -367,36 +373,33 @@ public final class DatabaseUtilsTest {
BatteryHistEntry.KEY_TIMESTAMP}); BatteryHistEntry.KEY_TIMESTAMP});
DatabaseUtils.sFakeSupplier = () -> cursor; DatabaseUtils.sFakeSupplier = () -> cursor;
assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge( assertThat(DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0)).isEmpty();
mContext, /*calendar=*/ null)).isEmpty();
} }
@Test @Test
public void getHistoryMapSinceLastFullCharge_nullCursor_returnEmptyMap() { public void getHistoryMap_nullCursor_returnEmptyMap() {
DatabaseUtils.sFakeSupplier = () -> null; DatabaseUtils.sFakeSupplier = () -> null;
assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge( assertThat(DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0)).isEmpty();
mContext, /*calendar=*/ null)).isEmpty();
} }
@Test @Test
public void getHistoryMapSinceLastFullCharge_returnExpectedMap() { public void getHistoryMap_returnExpectedMap() {
final Long timestamp1 = Long.valueOf(1001L); final Long timestamp1 = Long.valueOf(1001L);
final Long timestamp2 = Long.valueOf(1002L); final Long timestamp2 = Long.valueOf(1002L);
final MatrixCursor cursor = getMatrixCursor(); final MatrixCursor cursor = getMatrixCursor();
// Adds fake data into the cursor. // Adds fake data into the cursor.
cursor.addRow(new Object[] { 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[] { 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[] { 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[] { 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; DatabaseUtils.sFakeSupplier = () -> cursor;
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap = final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
DatabaseUtils.getHistoryMapSinceLastFullCharge( DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, timestamp1);
mContext, /*calendar=*/ null);
assertThat(batteryHistMap).hasSize(2); assertThat(batteryHistMap).hasSize(2);
// Verifies the BatteryHistEntry data for timestamp1. // Verifies the BatteryHistEntry data for timestamp1.
@@ -412,7 +415,7 @@ public final class DatabaseUtilsTest {
} }
@Test @Test
public void getHistoryMapSinceLastFullCharge_withWorkProfile_returnExpectedMap() public void getHistoryMap_withWorkProfile_returnExpectedMap()
throws PackageManager.NameNotFoundException { throws PackageManager.NameNotFoundException {
doReturn("com.fake.package").when(mContext).getPackageName(); doReturn("com.fake.package").when(mContext).getPackageName();
doReturn(mMockContext).when(mContext).createPackageContextAsUser( doReturn(mMockContext).when(mContext).createPackageContextAsUser(
@@ -425,8 +428,7 @@ public final class DatabaseUtilsTest {
DatabaseUtils.sFakeSupplier = () -> getMatrixCursor(); DatabaseUtils.sFakeSupplier = () -> getMatrixCursor();
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap = final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
DatabaseUtils.getHistoryMapSinceLastFullCharge( DatabaseUtils.getHistoryMapSinceQueryTimestamp(mContext, 0);
mContext, /*calendar=*/ null);
assertThat(batteryHistMap).isEmpty(); assertThat(batteryHistMap).isEmpty();
} }
@@ -571,6 +573,7 @@ public final class DatabaseUtilsTest {
BatteryHistEntry.KEY_PACKAGE_NAME, BatteryHistEntry.KEY_PACKAGE_NAME,
BatteryHistEntry.KEY_TIMESTAMP, BatteryHistEntry.KEY_TIMESTAMP,
BatteryHistEntry.KEY_UID, 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; return 0;
} }
@Override
protected boolean isBatteryHistoryNeeded() {
return false;
}
@Override @Override
protected void refreshUi(int refreshType) { protected void refreshUi(int refreshType) {
// Do nothing // Do nothing

View File

@@ -16,6 +16,10 @@
package com.android.settings.fuelgauge.batteryusage.db; 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 static com.google.common.truth.Truth.assertThat;
import android.content.Context; import android.content.Context;
@@ -31,9 +35,14 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import java.util.List;
/** Tests for {@link BatteryEventDao}. */ /** Tests for {@link BatteryEventDao}. */
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public final class BatteryEventDaoTest { public final class BatteryEventDaoTest {
private static final long TIMESTAMP1 = System.currentTimeMillis();
private static final long TIMESTAMP2 = TIMESTAMP1 + 2;
private Context mContext; private Context mContext;
private BatteryStateDatabase mDatabase; private BatteryStateDatabase mDatabase;
private BatteryEventDao mBatteryEventDao; private BatteryEventDao mBatteryEventDao;
@@ -51,8 +60,44 @@ public final class BatteryEventDaoTest {
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null); BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
} }
@Test @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() mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
.setTimestamp(100L) .setTimestamp(100L)
.setBatteryEventType(1) .setBatteryEventType(1)
@@ -64,17 +109,44 @@ public final class BatteryEventDaoTest {
.setBatteryLevel(88) .setBatteryLevel(88)
.build()); .build());
final Cursor cursor = mBatteryEventDao.getAllAfter(160L); final Cursor cursor = mBatteryEventDao.getAllAfter(160L, List.of(1, 2));
assertThat(cursor.getCount()).isEqualTo(1); assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst(); cursor.moveToFirst();
assertThat(cursor.getLong(cursor.getColumnIndex(BatteryEventEntity.KEY_TIMESTAMP))) assertThat(cursor.getLong(cursor.getColumnIndex(KEY_TIMESTAMP)))
.isEqualTo(200L); .isEqualTo(200L);
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE))) assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_EVENT_TYPE)))
.isEqualTo(2); .isEqualTo(2);
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_LEVEL))) assertThat(cursor.getInt(cursor.getColumnIndex(KEY_BATTERY_LEVEL)))
.isEqualTo(88); .isEqualTo(88);
mBatteryEventDao.clearAll(); mBatteryEventDao.clearAll();
assertThat(mBatteryEventDao.getAll()).isEmpty(); 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) @RunWith(RobolectricTestRunner.class)
public final class BatteryStateDaoTest { public final class BatteryStateDaoTest {
private static final int CURSOR_COLUMN_SIZE = 9; private static final int CURSOR_COLUMN_SIZE = 9;
private static final long TIMESTAMP1 = System.currentTimeMillis(); private static final long CURRENT = System.currentTimeMillis();
private static final long TIMESTAMP2 = System.currentTimeMillis() + 2; private static final long TIMESTAMP1 = CURRENT;
private static final long TIMESTAMP3 = System.currentTimeMillis() + 4; 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_NAME1 = "com.android.apps.settings";
private static final String PACKAGE_NAME2 = "com.android.apps.calendar"; private static final String PACKAGE_NAME2 = "com.android.apps.calendar";
private static final String PACKAGE_NAME3 = "com.android.apps.gmail"; private static final String PACKAGE_NAME3 = "com.android.apps.gmail";
@@ -67,7 +68,7 @@ public final class BatteryStateDaoTest {
} }
@Test @Test
public void batteryStateDao_insertAll() throws Exception { public void insertAll_normalFlow_expectedBehavior() throws Exception {
final List<BatteryState> states = mBatteryStateDao.getAllAfter(TIMESTAMP1); final List<BatteryState> states = mBatteryStateDao.getAllAfter(TIMESTAMP1);
assertThat(states).hasSize(2); assertThat(states).hasSize(2);
// Verifies the queried battery states. // Verifies the queried battery states.
@@ -76,8 +77,26 @@ public final class BatteryStateDaoTest {
} }
@Test @Test
public void batteryStateDao_getCursorSinceLastFullCharge() throws Exception { public void getLatestTimestamp_normalFlow_expectedBehavior() throws Exception {
final Cursor cursor1 = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP1); 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.getCount()).isEqualTo(3);
assertThat(cursor1.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE); assertThat(cursor1.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state. // Verifies the queried first battery state.
@@ -90,7 +109,7 @@ public final class BatteryStateDaoTest {
cursor1.moveToNext(); cursor1.moveToNext();
assertThat(cursor1.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME3); 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.getCount()).isEqualTo(1);
assertThat(cursor2.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE); assertThat(cursor2.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state. // Verifies the queried first battery state.
@@ -99,25 +118,7 @@ public final class BatteryStateDaoTest {
} }
@Test @Test
public void batteryStateDao_getCursorSinceLastFullCharge_noFullChargeData_returnSevenDaysData() public void clearAllBefore_normalFlow_expectedBehavior() throws Exception {
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 {
mBatteryStateDao.clearAllBefore(TIMESTAMP2); mBatteryStateDao.clearAllBefore(TIMESTAMP2);
final List<BatteryState> states = mBatteryStateDao.getAllAfter(0); final List<BatteryState> states = mBatteryStateDao.getAllAfter(0);
@@ -127,20 +128,20 @@ public final class BatteryStateDaoTest {
} }
@Test @Test
public void batteryStateDao_clearAll() throws Exception { public void clearAll_normalFlow_expectedBehavior() throws Exception {
assertThat(mBatteryStateDao.getAllAfter(0)).hasSize(3); assertThat(mBatteryStateDao.getAllAfter(0)).hasSize(3);
mBatteryStateDao.clearAll(); mBatteryStateDao.clearAll();
assertThat(mBatteryStateDao.getAllAfter(0)).isEmpty(); assertThat(mBatteryStateDao.getAllAfter(0)).isEmpty();
} }
@Test @Test
public void getInstance_createNewInstance() throws Exception { public void getInstance_createNewInstance_returnsExpectedResult() throws Exception {
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null); BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
assertThat(BatteryStateDatabase.getInstance(mContext)).isNotNull(); assertThat(BatteryStateDatabase.getInstance(mContext)).isNotNull();
} }
@Test @Test
public void getDistinctTimestampCount_returnsExpectedResult() { public void getDistinctTimestampCount_normalFlow_returnsExpectedResult() {
assertThat(mBatteryStateDao.getDistinctTimestampCount(/*timestamp=*/ 0)) assertThat(mBatteryStateDao.getDistinctTimestampCount(/*timestamp=*/ 0))
.isEqualTo(3); .isEqualTo(3);
assertThat(mBatteryStateDao.getDistinctTimestampCount(TIMESTAMP1)) assertThat(mBatteryStateDao.getDistinctTimestampCount(TIMESTAMP1))
@@ -148,7 +149,7 @@ public final class BatteryStateDaoTest {
} }
@Test @Test
public void getDistinctTimestamps_returnsExpectedResult() { public void getDistinctTimestamps_normalFlow_returnsExpectedResult() {
final List<Long> timestamps = final List<Long> timestamps =
mBatteryStateDao.getDistinctTimestamps(/*timestamp=*/ 0); 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);
}
}