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

Commit a51b1bae authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Support multi-user privacy for battery usage chart" into tm-qpr-dev am: fedab69c

parents fade53b0 fedab69c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -6648,6 +6648,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