From f8b46c01e834c70f29f20abfd02998efc0965384 Mon Sep 17 00:00:00 2001 From: YK Hung Date: Sun, 16 Jun 2024 16:55:27 +0000 Subject: [PATCH] AiCore reattribute feature Bug: 346706894 Bug: 344438848 Test: atest SettingsRoboTests:com.android.settings.fuelgauge.batteryusage Flag: EXEMPT bug fix Change-Id: Ifb18c2d156d11fcfdc67cff575ba800c4a6cc0fe Merged-In: Icc9a475a71f189e72bf06f9a0b4c23380a90a603 --- Android.bp | 6 +- .../fuelgauge/PowerUsageFeatureProvider.java | 15 +++ .../PowerUsageFeatureProviderImpl.java | 20 +++ .../batteryusage/BatteryDiffData.java | 12 +- .../fuelgauge/batteryusage/ConvertUtils.java | 14 ++ .../batteryusage/DataProcessManager.java | 24 +++- .../fuelgauge/batteryusage/DatabaseUtils.java | 2 + .../bugreport/BugReportContentProvider.java | 1 + .../batteryusage/bugreport/LogUtils.java | 32 +++++ .../batteryusage/db/BatteryReattributeDao.kt | 50 +++++++ .../db/BatteryReattributeEntity.java | 73 +++++++++++ .../batteryusage/db/BatteryStateDatabase.java | 13 +- .../settings/fuelgauge/protos/Android.bp | 36 +----- .../protos/battery_reattribute.proto | 13 ++ .../batteryusage/ConvertUtilsTest.java | 22 ++++ .../batteryusage/DataProcessManagerTest.java | 2 + .../batteryusage/bugreport/LogUtilsTest.java | 122 ++++++++++++++++++ .../db/BatteryReattributeDaoTest.java | 118 +++++++++++++++++ .../db/BatteryReattributeEntityTest.java | 54 ++++++++ 19 files changed, 575 insertions(+), 54 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt create mode 100644 src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java create mode 100644 src/com/android/settings/fuelgauge/protos/battery_reattribute.proto create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java diff --git a/Android.bp b/Android.bp index b716117b096..cc10bc30ea7 100644 --- a/Android.bp +++ b/Android.bp @@ -94,15 +94,11 @@ android_library { "SettingsLibActivityEmbedding", "aconfig_settings_flags_lib", "accessibility_settings_flags_lib", - "app-usage-event-protos-lite", - "battery-event-protos-lite", - "battery-usage-slot-protos-lite", "contextualcards", "development_settings_flag_lib", "factory_reset_flags_lib", "fuelgauge-log-protos-lite", - "fuelgauge-usage-state-protos-lite", - "power-anomaly-event-protos-lite", + "fuelgauge-protos-lite", "settings-contextual-card-protos-lite", "settings-log-bridge-protos-lite", "settings-logtags", diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index a04d8f8ad12..d40e42a4305 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -22,11 +22,16 @@ import android.os.Bundle; import android.util.ArrayMap; import android.util.SparseIntArray; +import androidx.annotation.NonNull; + +import com.android.settings.fuelgauge.batteryusage.BatteryDiffData; +import com.android.settings.fuelgauge.batteryusage.BatteryEvent; import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType; import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList; import com.android.settingslib.fuelgauge.Estimate; import java.util.List; +import java.util.Map; import java.util.Set; /** Feature Provider used in power usage */ @@ -146,4 +151,14 @@ public interface PowerUsageFeatureProvider { /** Whether the app optimization mode is valid to restore */ boolean isValidToRestoreOptimizationMode(ArrayMap deviceInfoMap); + + /** Whether the battery usage reattribute is eabled or not. */ + boolean isBatteryUsageReattributeEnabled(); + + /** Collect and process battery reattribute data if needed. */ + boolean processBatteryReattributeData( + @NonNull Context context, + @NonNull Map batteryDiffDataMap, + @NonNull List batteryEventList, + final boolean isFromPeriodJob); } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 75ebabbd4ec..69688e2ce09 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -26,13 +26,19 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseIntArray; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.util.ArrayUtils; +import com.android.settings.fuelgauge.batteryusage.BatteryDiffData; +import com.android.settings.fuelgauge.batteryusage.BatteryEvent; import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType; import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList; import com.android.settingslib.fuelgauge.Estimate; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; /** Implementation of {@code PowerUsageFeatureProvider} */ @@ -228,4 +234,18 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider public boolean isValidToRestoreOptimizationMode(ArrayMap deviceInfoMap) { return false; } + + @Override + public boolean isBatteryUsageReattributeEnabled() { + return false; + } + + @Override + public boolean processBatteryReattributeData( + @NonNull Context context, + @NonNull Map batteryDiffDataMap, + @NonNull List batteryEventList, + final boolean isFromPeriodJob) { + return false; + } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java index eebf1f5915a..ccaf227e0e3 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java @@ -77,11 +77,13 @@ public class BatteryDiffData { processAndSortEntries(mSystemEntries); } - long getStartTimestamp() { + /** Gets the start timestamp. */ + public long getStartTimestamp() { return mStartTimestamp; } - long getEndTimestamp() { + /** Gets the end timestamp. */ + public long getEndTimestamp() { return mEndTimestamp; } @@ -97,7 +99,8 @@ public class BatteryDiffData { return mScreenOnTime; } - List getAppDiffEntryList() { + /** Gets the {@link BatteryDiffEntry} list for apps. */ + public List getAppDiffEntryList() { return mAppEntries; } @@ -293,8 +296,7 @@ public class BatteryDiffData { * Sets total consume power, and adjusts the percentages to ensure the total round percentage * could be 100%, and then sorts entries based on the sorting key. */ - @VisibleForTesting - static void processAndSortEntries(final List batteryDiffEntries) { + public static void processAndSortEntries(final List batteryDiffEntries) { if (batteryDiffEntries.isEmpty()) { return; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java index df9f0634e04..57c0596a9ef 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java @@ -201,6 +201,20 @@ public final class ConvertUtils { return defaultInstance; } + /** Gets the encoded string from {@link BatteryReattribute} instance. */ + @NonNull + public static String encodeBatteryReattribute( + @NonNull BatteryReattribute batteryReattribute) { + return Base64.encodeToString(batteryReattribute.toByteArray(), Base64.DEFAULT); + } + + /** Gets the decoded {@link BatteryReattribute} instance from string. */ + @NonNull + public static BatteryReattribute decodeBatteryReattribute(@NonNull String content) { + return BatteryUtils.parseProtoFromString( + content, BatteryReattribute.getDefaultInstance()); + } + /** Converts to {@link BatteryHistEntry} */ public static BatteryHistEntry convertToBatteryHistEntry( BatteryEntry entry, BatteryUsageStats batteryUsageStats) { diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java index b3bcb47ecc5..5dae7bf397b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java @@ -31,6 +31,8 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.Utils; +import com.android.settings.fuelgauge.PowerUsageFeatureProvider; +import com.android.settings.overlay.FeatureFactory; import java.util.ArrayList; import java.util.Calendar; @@ -80,6 +82,7 @@ public class DataProcessManager { // Raw start timestamp with round to the nearest hour. private final long mRawStartTimestamp; private final long mLastFullChargeTimestamp; + private final boolean mIsFromPeriodJob; private final Context mContext; private final Handler mHandler; private final UserManager mUserManager; @@ -123,6 +126,7 @@ public class DataProcessManager { DataProcessManager( Context context, Handler handler, + final boolean isFromPeriodJob, final long rawStartTimestamp, final long lastFullChargeTimestamp, @NonNull final OnBatteryDiffDataMapLoadedListener callbackFunction, @@ -130,6 +134,7 @@ public class DataProcessManager { @NonNull final Map> batteryHistoryMap) { mContext = context.getApplicationContext(); mHandler = handler; + mIsFromPeriodJob = isFromPeriodJob; mUserManager = mContext.getSystemService(UserManager.class); mRawStartTimestamp = rawStartTimestamp; mLastFullChargeTimestamp = lastFullChargeTimestamp; @@ -147,6 +152,7 @@ public class DataProcessManager { mHandler = handler; mUserManager = mContext.getSystemService(UserManager.class); mCallbackFunction = callbackFunction; + mIsFromPeriodJob = false; mRawStartTimestamp = 0L; mLastFullChargeTimestamp = 0L; mHourlyBatteryLevelsPerDay = null; @@ -158,14 +164,9 @@ public class DataProcessManager { /** Starts the async tasks to load battery history data and app usage data. */ public void start() { - start(/* isFromPeriodJob= */ false); - } - - /** Starts the async tasks to load battery history data and app usage data. */ - public void start(boolean isFromPeriodJob) { // If we have battery level data, load the battery history map and app usage simultaneously. if (mHourlyBatteryLevelsPerDay != null) { - if (isFromPeriodJob) { + if (mIsFromPeriodJob) { mIsCurrentBatteryHistoryLoaded = true; mIsCurrentAppUsageLoaded = true; mIsBatteryUsageSlotLoaded = true; @@ -519,6 +520,14 @@ public class DataProcessManager { mAppUsagePeriodMap, getSystemAppsPackageNames(), getSystemAppsUids())); + // Process the reattributate data for the following two cases: + // 1) the latest slot for the timestamp "until now" + // 2) walkthrough all BatteryDiffData again to handle "re-compute" case + final PowerUsageFeatureProvider featureProvider = + FeatureFactory.getFeatureFactory() + .getPowerUsageFeatureProvider(); + featureProvider.processBatteryReattributeData( + mContext, batteryDiffDataMap, mBatteryEventList, mIsFromPeriodJob); Log.d( TAG, @@ -684,12 +693,13 @@ public class DataProcessManager { new DataProcessManager( context, handler, + isFromPeriodJob, startTimestamp, lastFullChargeTime, onBatteryDiffDataMapLoadedListener, batteryLevelData.getHourlyBatteryLevelsPerDay(), processedBatteryHistoryMap) - .start(isFromPeriodJob); + .start(); return batteryLevelData; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java index a41e9bd0388..176aaf1c45d 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java @@ -430,6 +430,7 @@ public final class DatabaseUtils { database.batteryEventDao().clearAll(); database.batteryStateDao().clearAll(); database.batteryUsageSlotDao().clearAll(); + database.batteryReattributeDao().clearAll(); } catch (RuntimeException e) { Log.e(TAG, "clearAll() failed", e); } @@ -450,6 +451,7 @@ public final class DatabaseUtils { database.batteryEventDao().clearAllBefore(earliestTimestamp); database.batteryStateDao().clearAllBefore(earliestTimestamp); database.batteryUsageSlotDao().clearAllBefore(earliestTimestamp); + database.batteryReattributeDao().clearAllBefore(earliestTimestamp); } catch (RuntimeException e) { Log.e(TAG, "clearAllBefore() failed", e); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java index ff953e77f86..c7a37186c8e 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java +++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java @@ -54,6 +54,7 @@ public final class BugReportContentProvider extends ContentProvider { return; } writer.println("dump BatteryUsage and AppUsage states:"); + LogUtils.dumpBatteryReattributeDatabaseHist(context, writer); LogUtils.dumpBatteryUsageDatabaseHist(context, writer); LogUtils.dumpAppUsageDatabaseHist(context, writer); LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer); diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java index 0ac8ccaf483..2d3e4a0cf7e 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java @@ -19,6 +19,8 @@ package com.android.settings.fuelgauge.batteryusage.bugreport; import android.content.Context; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batteryusage.BatteryUsageSlot; import com.android.settings.fuelgauge.batteryusage.ConvertUtils; @@ -27,11 +29,14 @@ import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity; import com.android.settings.fuelgauge.batteryusage.db.BatteryEventDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity; +import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeDao; +import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeEntity; import com.android.settings.fuelgauge.batteryusage.db.BatteryState; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity; +import com.android.settings.overlay.FeatureFactory; import java.io.PrintWriter; import java.time.Clock; @@ -116,6 +121,33 @@ public final class LogUtils { dumpListItems(writer, entities, entity -> entity); } + static void dumpBatteryReattributeDatabaseHist(Context context, PrintWriter writer) { + try { + dumpBatteryReattributeDatabaseHist( + BatteryStateDatabase.getInstance(context).batteryReattributeDao(), + writer); + } catch (Exception e) { + Log.e(TAG, "failed to run dumpBatteryReattributeDatabaseHist()", e); + } + } + + @VisibleForTesting + static void dumpBatteryReattributeDatabaseHist( + BatteryReattributeDao batteryReattributeDao, PrintWriter writer) { + if (!FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider() + .isBatteryUsageReattributeEnabled()) { + writer.println("\n\tBatteryReattribute is disabled!"); + return; + } + writer.println("\n\tBatteryReattribute DatabaseHistory:"); + final List entities = + batteryReattributeDao.getAllAfter( + Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis()); + if (entities != null && !entities.isEmpty()) { + dumpListItems(writer, entities, entity -> entity); + } + } + private static void dumpListItems( PrintWriter writer, List itemList, Function itemConverter) { final AtomicInteger counter = new AtomicInteger(0); diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt new file mode 100644 index 00000000000..79c9d0039a5 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 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 androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; + +import java.util.List; + +/** DAO for accessing {@link BatteryReattributeEntity} in the database. */ +@Dao +public interface BatteryReattributeDao { + + /** Inserts a {@link BatteryReattributeEntity} data into the database. */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(event: BatteryReattributeEntity) + + /** Gets all recorded data after a specific timestamp. */ + @Query( + "SELECT * FROM BatteryReattributeEntity WHERE " + + "timestampStart >= :timestampStart ORDER BY timestampStart DESC") + fun getAllAfter(timestampStart: Long): List + + /** Deletes all recorded data before a specific timestamp. */ + @Query("DELETE FROM BatteryReattributeEntity WHERE timestampStart <= :timestampStart") + fun clearAllBefore(timestampStart: Long) + + /** Deletes all recorded data after a specific timestamp. */ + @Query("DELETE FROM BatteryReattributeEntity WHERE timestampStart >= :timestampStart") + fun clearAllAfter(timestampStart: Long) + + /** Clears all recorded data in the database. */ + @Query("DELETE FROM BatteryReattributeEntity") fun clearAll() +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java new file mode 100644 index 00000000000..1a0c0872d24 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 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.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging; + +import com.android.settings.fuelgauge.batteryusage.BatteryReattribute; +import com.android.settings.fuelgauge.batteryusage.ConvertUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +/** A {@link Entity} for battery usage reattribution data in the database. */ +@Entity +public class BatteryReattributeEntity { + + /** The start timestamp of this record data. */ + @PrimaryKey + public final long timestampStart; + + /** The end timestamp of this record data. */ + public final long timestampEnd; + + /** The battery usage reattribution data for corresponding uids. */ + public final String reattributeData; + + public BatteryReattributeEntity(@NonNull BatteryReattribute batteryReattribute) { + this( + batteryReattribute.getTimestampStart(), + batteryReattribute.getTimestampEnd(), + ConvertUtils.encodeBatteryReattribute(batteryReattribute)); + } + + @VisibleForTesting + BatteryReattributeEntity( + long timestampStart, long timestampEnd, @NonNull String reattributeData) { + this.timestampStart = timestampStart; + this.timestampEnd = timestampEnd; + this.reattributeData = reattributeData; + } + + @NonNull + @Override + public String toString() { + final BatteryReattribute batteryReattribute = + ConvertUtils.decodeBatteryReattribute(reattributeData); + final StringBuilder builder = new StringBuilder() + .append("\nBatteryReattributeEntity{") + .append("\n\t" + utcToLocalTimeForLogging(timestampStart)) + .append("\n\t" + utcToLocalTimeForLogging(timestampEnd)) + .append("\n\t" + batteryReattribute); + if (batteryReattribute != null) { + builder.append("\n\t" + batteryReattribute.getReattributeDataMap()); + } + return builder.append("\n}").toString(); + } +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java index 7504775d74e..8e3d6e37e82 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java @@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batteryusage.db; import android.content.Context; import android.util.Log; +import androidx.annotation.NonNull; import androidx.room.Database; import androidx.room.Room; import androidx.room.RoomDatabase; @@ -29,11 +30,13 @@ import androidx.room.RoomDatabase; AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class, - BatteryUsageSlotEntity.class + BatteryUsageSlotEntity.class, + BatteryReattributeEntity.class }, - version = 1) + version = 2) public abstract class BatteryStateDatabase extends RoomDatabase { private static final String TAG = "BatteryStateDatabase"; + private static final String DB_FILE_NAME = "battery-usage-db-v10"; private static BatteryStateDatabase sBatteryStateDatabase; @@ -49,11 +52,15 @@ public abstract class BatteryStateDatabase extends RoomDatabase { /** Provides DAO for battery usage slot table. */ public abstract BatteryUsageSlotDao batteryUsageSlotDao(); + /** Provides DAO for battery reattribution table. */ + @NonNull + public abstract BatteryReattributeDao batteryReattributeDao(); + /** Gets or creates an instance of {@link RoomDatabase}. */ public static BatteryStateDatabase getInstance(Context context) { if (sBatteryStateDatabase == null) { sBatteryStateDatabase = - Room.databaseBuilder(context, BatteryStateDatabase.class, "battery-usage-db-v9") + Room.databaseBuilder(context, BatteryStateDatabase.class, DB_FILE_NAME) // Allows accessing data in the main thread for dumping bugreport. .allowMainThreadQueries() .fallbackToDestructiveMigration() diff --git a/src/com/android/settings/fuelgauge/protos/Android.bp b/src/com/android/settings/fuelgauge/protos/Android.bp index 462962b63c5..40fb987e62c 100644 --- a/src/com/android/settings/fuelgauge/protos/Android.bp +++ b/src/com/android/settings/fuelgauge/protos/Android.bp @@ -9,41 +9,9 @@ package { } java_library { - name: "app-usage-event-protos-lite", + name: "fuelgauge-protos-lite", proto: { type: "lite", }, - srcs: ["app_usage_event.proto"], -} - -java_library { - name: "battery-event-protos-lite", - proto: { - type: "lite", - }, - srcs: ["battery_event.proto"], -} - -java_library { - name: "battery-usage-slot-protos-lite", - proto: { - type: "lite", - }, - srcs: ["battery_usage_slot.proto"], -} - -java_library { - name: "fuelgauge-usage-state-protos-lite", - proto: { - type: "lite", - }, - srcs: ["fuelgauge_usage_state.proto"], -} - -java_library { - name: "power-anomaly-event-protos-lite", - proto: { - type: "lite", - }, - srcs: ["power_anomaly_event.proto"], + srcs: ["*.proto"], } diff --git a/src/com/android/settings/fuelgauge/protos/battery_reattribute.proto b/src/com/android/settings/fuelgauge/protos/battery_reattribute.proto new file mode 100644 index 00000000000..8185a22c68d --- /dev/null +++ b/src/com/android/settings/fuelgauge/protos/battery_reattribute.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "com.android.settings.fuelgauge.batteryusage"; +option java_outer_classname = "BatteryReaatributeProto"; + +// Battery usage reattribute data for a specific timestamp slot. +message BatteryReattribute { + optional int64 timestamp_start = 1; + optional int64 timestamp_end = 2; + // Battery reattribute data for uid and its corresponding ratio. + map reattribute_data = 3; +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java index 5ce449b5d1d..ffd4c8f3f8c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java @@ -670,4 +670,26 @@ public final class ConvertUtilsTest { /* taskRootPackageName= */ "")) .isEqualTo(packageName); } + + @Test + public void decodeBatteryReattribute_returnExpectedResult() { + final BatteryReattribute batteryReattribute = + BatteryReattribute.newBuilder() + .setTimestampStart(100L) + .setTimestampEnd(200L) + .putReattributeData(1001, 0.2f) + .putReattributeData(2001, 0.8f) + .build(); + + final BatteryReattribute decodeResult = ConvertUtils.decodeBatteryReattribute( + ConvertUtils.encodeBatteryReattribute(batteryReattribute)); + + assertThat(decodeResult.getTimestampStart()).isEqualTo(100L); + assertThat(decodeResult.getTimestampEnd()).isEqualTo(200L); + final Map reattributeDataMap = decodeResult.getReattributeDataMap(); + // Verify the reattribute data in the map. + assertThat(reattributeDataMap).hasSize(2); + assertThat(reattributeDataMap.get(1001)).isEqualTo(0.2f); + assertThat(reattributeDataMap.get(2001)).isEqualTo(0.8f); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java index 6227790b3dd..b100254801e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java @@ -100,6 +100,7 @@ public final class DataProcessManagerTest { new DataProcessManager( mContext, /* handler= */ null, + /* isFromPeriodJob= */ false, /* rawStartTimestamp= */ 0L, /* lastFullChargeTimestamp= */ 0L, /* callbackFunction= */ null, @@ -239,6 +240,7 @@ public final class DataProcessManagerTest { new DataProcessManager( mContext, /* handler= */ null, + /* isFromPeriodJob= */ false, /* rawStartTimestamp= */ 2L, /* lastFullChargeTimestamp= */ 1L, /* callbackFunction= */ null, diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java new file mode 100644 index 00000000000..9b459c57131 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge.batteryusage.bugreport; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.fuelgauge.PowerUsageFeatureProvider; +import com.android.settings.fuelgauge.batteryusage.BatteryReattribute; +import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeDao; +import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeEntity; +import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; +import com.android.settings.testutils.BatteryTestUtils; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.After; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.robolectric.RobolectricTestRunner; + +import java.io.PrintWriter; +import java.io.StringWriter; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(RobolectricTestRunner.class) +public final class LogUtilsTest { + + private StringWriter mTestStringWriter; + private PrintWriter mTestPrintWriter; + private Context mContext; + private BatteryStateDatabase mDatabase; + private BatteryReattributeDao mBatteryReattributeDao; + private PowerUsageFeatureProvider mPowerUsageFeatureProvider; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mTestStringWriter = new StringWriter(); + mTestPrintWriter = new PrintWriter(mTestStringWriter); + mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext); + mBatteryReattributeDao = mDatabase.batteryReattributeDao(); + mPowerUsageFeatureProvider = FakeFeatureFactory.setupForTest().powerUsageFeatureProvider; + when(mPowerUsageFeatureProvider.isBatteryUsageReattributeEnabled()).thenReturn(true); + } + + @After + public void cleanUp() { + mBatteryReattributeDao.clearAll(); + } + + @Test + public void dumpBatteryReattributeDatabaseHist_noData_printExpectedResult() { + LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter); + + assertThat(mTestStringWriter.toString()) + .contains("BatteryReattribute DatabaseHistory:"); + } + + @Test + public void dumpBatteryReattributeDatabaseHist_printExpectedResult() { + final long currentTimeMillis = System.currentTimeMillis(); + // Insert the first testing data. + final BatteryReattribute batteryReattribute1 = + BatteryReattribute.newBuilder() + .setTimestampStart(currentTimeMillis - 20000) + .setTimestampEnd(currentTimeMillis - 10000) + .putReattributeData(1001, 0.1f) + .putReattributeData(1002, 0.99f) + .build(); + mBatteryReattributeDao.insert(new BatteryReattributeEntity(batteryReattribute1)); + // Insert the second testing data. + final BatteryReattribute batteryReattribute2 = + BatteryReattribute.newBuilder() + .setTimestampStart(currentTimeMillis - 40000) + .setTimestampEnd(currentTimeMillis - 20000) + .putReattributeData(1003, 1f) + .build(); + mBatteryReattributeDao.insert(new BatteryReattributeEntity(batteryReattribute2)); + + LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter); + + final String result = mTestStringWriter.toString(); + assertThat(result).contains("BatteryReattribute DatabaseHistory:"); + assertThat(result).contains(batteryReattribute1.toString()); + assertThat(result).contains(batteryReattribute2.toString()); + } + + @Test + public void dumpBatteryReattributeDatabaseHist_featureDisable_notPrintData() { + mBatteryReattributeDao.insert(new BatteryReattributeEntity( + BatteryReattribute.getDefaultInstance())); + when(mPowerUsageFeatureProvider.isBatteryUsageReattributeEnabled()).thenReturn(false); + + LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter); + + final String result = mTestStringWriter.toString(); + assertThat(result).contains("BatteryReattribute is disabled!"); + assertThat(result.contains("BatteryReattribute DatabaseHistory:")).isFalse(); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java new file mode 100644 index 00000000000..8cb0e120a3f --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2024 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 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 BatteryReattributeDao}. */ +@RunWith(RobolectricTestRunner.class) +public final class BatteryReattributeDaoTest { + + private Context mContext; + private BatteryStateDatabase mDatabase; + private BatteryReattributeDao mBatteryReattributeDao; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext); + mBatteryReattributeDao = mDatabase.batteryReattributeDao(); + insert(100L, 200L, "reattributeData1"); + insert(300L, 400L, "reattributeData3"); + insert(200L, 300L, "reattributeData2"); + insert(400L, 500L, "reattributeData4"); + // Ensure there was data inserted into the database. + assertThat(getAllEntityData()).isNotEmpty(); + } + + @Test + public void getAllAfter_returnExpectedEntityData() { + final List entityDataList = + mBatteryReattributeDao.getAllAfter(/* timestampStart= */ 300L); + + assertThat(entityDataList).hasSize(2); + assertEntity(entityDataList.get(0), 400L, 500L, "reattributeData4"); + assertEntity(entityDataList.get(1), 300L, 400L, "reattributeData3"); + } + + @Test + public void clearAll_clearAllData() { + mBatteryReattributeDao.clearAll(); + + assertThat(getAllEntityData()).isEmpty(); + } + + @Test + public void clearAllBefore_clearAllExpectedData() { + mBatteryReattributeDao.clearAllBefore(/* timestampStart= */ 300L); + + final List entityDataList = getAllEntityData(); + assertThat(entityDataList).hasSize(1); + assertEntity(entityDataList.get(0), 400L, 500L, "reattributeData4"); + } + + @Test + public void clearAllAfter_clearAllExpectedData() { + mBatteryReattributeDao.clearAllAfter(/* timestampStart= */ 300L); + + final List entityDataList = getAllEntityData(); + assertThat(entityDataList).hasSize(2); + assertEntity(entityDataList.get(0), 200L, 300L, "reattributeData2"); + assertEntity(entityDataList.get(1), 100L, 200L, "reattributeData1"); + } + + @Test + public void insert_samePrimaryKeyEntityData_replaceIntoNewEntityData() { + // Verify the original data before update. + assertEntity(getAllEntityData().get(0), 400L, 500L, "reattributeData4"); + + insert(400L, 600L, "reattribute4Update"); + + // Verify the new update entity data. + assertEntity(getAllEntityData().get(0), 400L, 600L, "reattribute4Update"); + } + + private void insert(long timestampStart, long timestampEnd, String reattributeData) { + mBatteryReattributeDao.insert( + new BatteryReattributeEntity( + timestampStart, timestampEnd, reattributeData)); + } + + private List getAllEntityData() { + return mBatteryReattributeDao.getAllAfter(/* timestampStart= */ 0L); + } + + private static void assertEntity(BatteryReattributeEntity entity, long timestampStart, + long timestampEnd, String reattributeData) { + assertThat(entity.timestampStart).isEqualTo(timestampStart); + assertThat(entity.timestampEnd).isEqualTo(timestampEnd); + assertThat(entity.reattributeData).isEqualTo(reattributeData); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java new file mode 100644 index 00000000000..04912aacbdb --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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 com.android.settings.fuelgauge.batteryusage.BatteryReattribute; +import com.android.settings.fuelgauge.batteryusage.ConvertUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link BatteryReattributeEntity}. */ +@RunWith(RobolectricTestRunner.class) +public final class BatteryReattributeEntityTest { + + @Test + public void constructor_createExpectedData() { + final BatteryReattribute batteryReattribute = + BatteryReattribute.newBuilder() + .setTimestampStart(100L) + .setTimestampEnd(200L) + .putReattributeData(1001, 0.2f) + .putReattributeData(2001, 0.8f) + .build(); + + final BatteryReattributeEntity batteryReattributeEntity = + new BatteryReattributeEntity(batteryReattribute); + + assertThat(batteryReattributeEntity.timestampStart) + .isEqualTo(batteryReattribute.getTimestampStart()); + assertThat(batteryReattributeEntity.timestampEnd) + .isEqualTo(batteryReattribute.getTimestampEnd()); + // Verify the BatteryReattribute data. + final BatteryReattribute decodeResult = + ConvertUtils.decodeBatteryReattribute(batteryReattributeEntity.reattributeData); + assertThat(decodeResult).isEqualTo(batteryReattribute); + } +}