Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 764d513d authored by ykhung's avatar ykhung
Browse files

Support multi-user privacy for battery usage chart

When there are multiple accounts in the devices, the battery usage list
is shared in the current design. We will aggregate other users usage
data into a single item to support multi-user privacy requirements

Screenshot: https://screenshot.googleplex.com/AkFTUtNvnoxcuGR

Bug: 202119550
Test: make RunSettingsRoboTests
Change-Id: I6cb55f0d50a4caca83212a0a54410530a032c089
parent 9447ec6d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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>
+2 −0
Original line number Diff line number Diff line
@@ -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,
+14 −0
Original line number Diff line number Diff line
@@ -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
@@ -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()
@@ -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:
@@ -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;
+60 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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) {
@@ -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;
    }
}
+10 −1
Original line number Diff line number Diff line
@@ -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)
@@ -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