Loading res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -6646,6 +6646,8 @@ <string name="battery_not_usage_24hr">No usage for past 24 hr</string> <!-- Description for no usage time but have battery usage [CHAR LIMIT=120] --> <string name="battery_usage_without_time"></string> <!-- Description for other users aggregated battery usage data [CHAR LIMIT=120] --> <string name="battery_usage_other_users">Other users</string> <!-- Graph subtext displayed to user when enhanced battery estimate is being used [CHAR LIMIT=120] --> <string name="advanced_battery_graph_subtext">Battery left estimate is based on your device usage</string> src/com/android/settings/fuelgauge/BatteryUtils.java +2 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,8 @@ public class BatteryUtils { public static final int UID_REMOVED_APPS = -4; /** Special UID value for data usage by tethering. */ public static final int UID_TETHERING = -5; /** Special UID for aggregated other users. */ public static final long UID_OTHER_USERS = Long.MIN_VALUE; @Retention(RetentionPolicy.SOURCE) @IntDef({StatusType.SCREEN_USAGE, Loading src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java +14 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,9 @@ public class BatteryDiffEntry { /** Gets the app label name for this entry. */ public String getAppLabel() { if (isOtherUsers()) { return mContext.getString(R.string.battery_usage_other_users); } loadLabelAndIcon(); // Returns default applicationn label if we cannot find it. return mAppLabel == null || mAppLabel.length() == 0 Loading @@ -122,6 +125,9 @@ public class BatteryDiffEntry { /** Gets the app icon {@link Drawable} for this entry. */ public Drawable getAppIcon() { if (isOtherUsers()) { return mContext.getDrawable(R.drawable.ic_power_system); } loadLabelAndIcon(); return mAppIcon != null && mAppIcon.getConstantState() != null ? mAppIcon.getConstantState().newDrawable() Loading Loading @@ -156,6 +162,9 @@ public class BatteryDiffEntry { /** Whether the current BatteryDiffEntry is system component or not. */ public boolean isSystemEntry() { if (isOtherUsers()) { return true; } switch (mBatteryHistEntry.mConsumerType) { case ConvertUtils.CONSUMER_TYPE_USER_BATTERY: case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY: Loading @@ -175,6 +184,11 @@ public class BatteryDiffEntry { return false; } private boolean isOtherUsers() { return mBatteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY && mBatteryHistEntry.mUid == BatteryUtils.UID_OTHER_USERS; } void loadLabelAndIcon() { if (mIsLoaded) { return; Loading src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +60 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import android.os.BatteryUsageStats; import android.os.LocaleList; import android.os.UserHandle; import android.os.UserManager; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.ArraySet; Loading @@ -28,6 +29,8 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.Utils; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.overlay.FeatureFactory; import java.lang.annotation.Retention; Loading Loading @@ -265,17 +268,55 @@ public final class ConvertUtils { } } insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); resolveMultiUsersData(context, resultMap); if (purgeLowPercentageAndFakeData) { purgeLowPercentageAndFakeData(context, resultMap); } return resultMap; } @VisibleForTesting static void resolveMultiUsersData( final Context context, final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) { final int currentUserId = context.getUserId(); final UserHandle userHandle = Utils.getManagedProfile(context.getSystemService(UserManager.class)); final int workProfileUserId = userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE; // Loops for all BatteryDiffEntry in the different slots. for (List<BatteryDiffEntry> entryList : indexedUsageMap.values()) { double consumePowerFromOtherUsers = 0f; double consumePercentageFromOtherUsers = 0f; final Iterator<BatteryDiffEntry> iterator = entryList.iterator(); while (iterator.hasNext()) { final BatteryDiffEntry entry = iterator.next(); final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry; if (batteryHistEntry.mConsumerType != CONSUMER_TYPE_UID_BATTERY) { continue; } // Whether the BatteryHistEntry represents the current user data? if (batteryHistEntry.mUserId == currentUserId || batteryHistEntry.mUserId == workProfileUserId) { continue; } // Removes and aggregates non-current users data from the list. iterator.remove(); consumePowerFromOtherUsers += entry.mConsumePower; consumePercentageFromOtherUsers += entry.getPercentOfTotal(); } if (consumePercentageFromOtherUsers != 0) { entryList.add(createOtherUsersEntry(context, consumePowerFromOtherUsers, consumePercentageFromOtherUsers)); } } } private static void insert24HoursData( final int desiredIndex, final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) { final Map<String, BatteryDiffEntry> resultMap = new HashMap<>(); double totalConsumePower = 0.0; double totalConsumePower = 0f; // Loops for all BatteryDiffEntry and aggregate them together. for (List<BatteryDiffEntry> entryList : indexedUsageMap.values()) { for (BatteryDiffEntry entry : entryList) { Loading Loading @@ -361,4 +402,22 @@ public final class ConvertUtils { return locales != null && !locales.isEmpty() ? locales.get(0) : Locale.getDefault(); } private static BatteryDiffEntry createOtherUsersEntry( Context context, double consumePower, double consumePercentage) { final ContentValues values = new ContentValues(); values.put(BatteryHistEntry.KEY_UID, BatteryUtils.UID_OTHER_USERS); values.put(BatteryHistEntry.KEY_USER_ID, BatteryUtils.UID_OTHER_USERS); values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, CONSUMER_TYPE_UID_BATTERY); // We will show the percentage for the "other users" item only, the aggregated // running time information is useless for users to identify individual apps. final BatteryDiffEntry batteryDiffEntry = new BatteryDiffEntry( context, /*foregroundUsageTimeInMs=*/ 0, /*backgroundUsageTimeInMs=*/ 0, consumePower, new BatteryHistEntry(values)); batteryDiffEntry.setTotalConsumePower(100 * consumePower / consumePercentage); return batteryDiffEntry; } } tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +10 −1 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import org.robolectric.annotation.Resetter; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @RunWith(RobolectricTestRunner.class) Loading Loading @@ -343,9 +344,17 @@ public final class BatteryBackupHelperTest { private void verifyBackupData(String expectedResult) throws Exception { final byte[] expectedBytes = expectedResult.getBytes(); final ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class); final Set<String> expectedResultSet = Set.of(expectedResult.split(BatteryBackupHelper.DELIMITER)); verify(mBackupDataOutput).writeEntityHeader( BatteryBackupHelper.KEY_OPTIMIZATION_LIST, expectedBytes.length); verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); verify(mBackupDataOutput).writeEntityData(captor.capture(), eq(expectedBytes.length)); final String actualResult = new String(captor.getValue()); final Set<String> actualResultSet = Set.of(actualResult.split(BatteryBackupHelper.DELIMITER)); assertThat(actualResultSet).isEqualTo(expectedResultSet); } private void createTestingData( Loading Loading
res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -6646,6 +6646,8 @@ <string name="battery_not_usage_24hr">No usage for past 24 hr</string> <!-- Description for no usage time but have battery usage [CHAR LIMIT=120] --> <string name="battery_usage_without_time"></string> <!-- Description for other users aggregated battery usage data [CHAR LIMIT=120] --> <string name="battery_usage_other_users">Other users</string> <!-- Graph subtext displayed to user when enhanced battery estimate is being used [CHAR LIMIT=120] --> <string name="advanced_battery_graph_subtext">Battery left estimate is based on your device usage</string>
src/com/android/settings/fuelgauge/BatteryUtils.java +2 −0 Original line number Diff line number Diff line Loading @@ -69,6 +69,8 @@ public class BatteryUtils { public static final int UID_REMOVED_APPS = -4; /** Special UID value for data usage by tethering. */ public static final int UID_TETHERING = -5; /** Special UID for aggregated other users. */ public static final long UID_OTHER_USERS = Long.MIN_VALUE; @Retention(RetentionPolicy.SOURCE) @IntDef({StatusType.SCREEN_USAGE, Loading
src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java +14 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,9 @@ public class BatteryDiffEntry { /** Gets the app label name for this entry. */ public String getAppLabel() { if (isOtherUsers()) { return mContext.getString(R.string.battery_usage_other_users); } loadLabelAndIcon(); // Returns default applicationn label if we cannot find it. return mAppLabel == null || mAppLabel.length() == 0 Loading @@ -122,6 +125,9 @@ public class BatteryDiffEntry { /** Gets the app icon {@link Drawable} for this entry. */ public Drawable getAppIcon() { if (isOtherUsers()) { return mContext.getDrawable(R.drawable.ic_power_system); } loadLabelAndIcon(); return mAppIcon != null && mAppIcon.getConstantState() != null ? mAppIcon.getConstantState().newDrawable() Loading Loading @@ -156,6 +162,9 @@ public class BatteryDiffEntry { /** Whether the current BatteryDiffEntry is system component or not. */ public boolean isSystemEntry() { if (isOtherUsers()) { return true; } switch (mBatteryHistEntry.mConsumerType) { case ConvertUtils.CONSUMER_TYPE_USER_BATTERY: case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY: Loading @@ -175,6 +184,11 @@ public class BatteryDiffEntry { return false; } private boolean isOtherUsers() { return mBatteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY && mBatteryHistEntry.mUid == BatteryUtils.UID_OTHER_USERS; } void loadLabelAndIcon() { if (mIsLoaded) { return; Loading
src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +60 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import android.os.BatteryUsageStats; import android.os.LocaleList; import android.os.UserHandle; import android.os.UserManager; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.ArraySet; Loading @@ -28,6 +29,8 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.Utils; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.overlay.FeatureFactory; import java.lang.annotation.Retention; Loading Loading @@ -265,17 +268,55 @@ public final class ConvertUtils { } } insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); resolveMultiUsersData(context, resultMap); if (purgeLowPercentageAndFakeData) { purgeLowPercentageAndFakeData(context, resultMap); } return resultMap; } @VisibleForTesting static void resolveMultiUsersData( final Context context, final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) { final int currentUserId = context.getUserId(); final UserHandle userHandle = Utils.getManagedProfile(context.getSystemService(UserManager.class)); final int workProfileUserId = userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE; // Loops for all BatteryDiffEntry in the different slots. for (List<BatteryDiffEntry> entryList : indexedUsageMap.values()) { double consumePowerFromOtherUsers = 0f; double consumePercentageFromOtherUsers = 0f; final Iterator<BatteryDiffEntry> iterator = entryList.iterator(); while (iterator.hasNext()) { final BatteryDiffEntry entry = iterator.next(); final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry; if (batteryHistEntry.mConsumerType != CONSUMER_TYPE_UID_BATTERY) { continue; } // Whether the BatteryHistEntry represents the current user data? if (batteryHistEntry.mUserId == currentUserId || batteryHistEntry.mUserId == workProfileUserId) { continue; } // Removes and aggregates non-current users data from the list. iterator.remove(); consumePowerFromOtherUsers += entry.mConsumePower; consumePercentageFromOtherUsers += entry.getPercentOfTotal(); } if (consumePercentageFromOtherUsers != 0) { entryList.add(createOtherUsersEntry(context, consumePowerFromOtherUsers, consumePercentageFromOtherUsers)); } } } private static void insert24HoursData( final int desiredIndex, final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) { final Map<String, BatteryDiffEntry> resultMap = new HashMap<>(); double totalConsumePower = 0.0; double totalConsumePower = 0f; // Loops for all BatteryDiffEntry and aggregate them together. for (List<BatteryDiffEntry> entryList : indexedUsageMap.values()) { for (BatteryDiffEntry entry : entryList) { Loading Loading @@ -361,4 +402,22 @@ public final class ConvertUtils { return locales != null && !locales.isEmpty() ? locales.get(0) : Locale.getDefault(); } private static BatteryDiffEntry createOtherUsersEntry( Context context, double consumePower, double consumePercentage) { final ContentValues values = new ContentValues(); values.put(BatteryHistEntry.KEY_UID, BatteryUtils.UID_OTHER_USERS); values.put(BatteryHistEntry.KEY_USER_ID, BatteryUtils.UID_OTHER_USERS); values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, CONSUMER_TYPE_UID_BATTERY); // We will show the percentage for the "other users" item only, the aggregated // running time information is useless for users to identify individual apps. final BatteryDiffEntry batteryDiffEntry = new BatteryDiffEntry( context, /*foregroundUsageTimeInMs=*/ 0, /*backgroundUsageTimeInMs=*/ 0, consumePower, new BatteryHistEntry(values)); batteryDiffEntry.setTotalConsumePower(100 * consumePower / consumePercentage); return batteryDiffEntry; } }
tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +10 −1 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import org.robolectric.annotation.Resetter; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @RunWith(RobolectricTestRunner.class) Loading Loading @@ -343,9 +344,17 @@ public final class BatteryBackupHelperTest { private void verifyBackupData(String expectedResult) throws Exception { final byte[] expectedBytes = expectedResult.getBytes(); final ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class); final Set<String> expectedResultSet = Set.of(expectedResult.split(BatteryBackupHelper.DELIMITER)); verify(mBackupDataOutput).writeEntityHeader( BatteryBackupHelper.KEY_OPTIMIZATION_LIST, expectedBytes.length); verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length); verify(mBackupDataOutput).writeEntityData(captor.capture(), eq(expectedBytes.length)); final String actualResult = new String(captor.getValue()); final Set<String> actualResultSet = Set.of(actualResult.split(BatteryBackupHelper.DELIMITER)); assertThat(actualResultSet).isEqualTo(expectedResultSet); } private void createTestingData( Loading