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

Commit 068adaf1 authored by Zaiyue Xue's avatar Zaiyue Xue
Browse files

Enable i18n for battery tips card and fix b/297036263.

Bug: 291689623
Bug: 297036263
Fix: 297036263
Test: manual
Change-Id: I41aff99c73ace995ef9dfa8f1dc28024cd12d236
parent d9360ec3
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1415,12 +1415,12 @@
        <item>Reduce screen timeout to extend battery life</item>
    </string-array>

    <string-array name="power_anomaly_main_btn_strings">
    <string-array name="power_anomaly_main_btn_strings" translatable="false">
        <item>@string/battery_tips_card_action_button</item>
        <item>@string/battery_tips_card_action_button</item>
    </string-array>

    <string-array name="power_anomaly_dismiss_btn_strings">
    <string-array name="power_anomaly_dismiss_btn_strings" translatable="false">
        <item>@string/battery_tips_card_dismiss_button</item>
        <item>@string/battery_tips_card_dismiss_button</item>
    </string-array>
+5 −5
Original line number Diff line number Diff line
@@ -9758,14 +9758,14 @@
    <!-- Preference summary for battery usage list page[CHAR_LIMIT=50]-->
    <string name="app_battery_usage_summary">Set battery usage for apps</string>
    <!-- Label of action button in battery tips card [CHAR LIMIT=NONE] -->
    <string name="battery_tips_card_action_button" translatable="false">View Settings</string>
    <!-- Label of action button in battery tips card [CHAR LIMIT=50] -->
    <string name="battery_tips_card_action_button">View Settings</string>
    <!-- Label of dismiss button in battery tips card [CHAR LIMIT=NONE] -->
    <string name="battery_tips_card_dismiss_button" translatable="false">Got it</string>
    <!-- Label of dismiss button in battery tips card [CHAR LIMIT=50] -->
    <string name="battery_tips_card_dismiss_button">Got it</string>
    <!-- Feedback card message in battery tips card [CHAR LIMIT=NONE] -->
    <string name="battery_tips_card_feedback_info" translatable="false">Is this message helpful?</string>
    <string name="battery_tips_card_feedback_info">Is this message helpful?</string>
    <!-- Filter title for battery unrestricted[CHAR_LIMIT=50]-->
    <string name="filter_battery_unrestricted_title">Unrestricted</string>
+59 −158
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

@@ -51,10 +52,10 @@ import java.time.Clock;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@@ -66,6 +67,7 @@ public final class DatabaseUtils {
    /** Clear memory threshold for device booting phase. **/
    private static final long CLEAR_MEMORY_THRESHOLD_MS = Duration.ofMinutes(5).toMillis();
    private static final long CLEAR_MEMORY_DELAYED_MS = Duration.ofSeconds(2).toMillis();
    private static final long INVALID_TIMESTAMP = 0L;

    static final int DATA_RETENTION_INTERVAL_DAY = 9;
    static final String KEY_LAST_LOAD_FULL_CHARGE_TIME = "last_load_full_charge_time";
@@ -158,8 +160,8 @@ public final class DatabaseUtils {
                        .appendQueryParameter(
                                QUERY_KEY_USERID, Long.toString(userId))
                        .build();
        final long latestTimestamp =
                loadAppUsageLatestTimestampFromContentProvider(context, appUsageLatestTimestampUri);
        final long latestTimestamp = loadLongFromContentProvider(
                context, appUsageLatestTimestampUri, /*defaultValue=*/ INVALID_TIMESTAMP);
        final String latestTimestampString = utcToLocalTimeForLogging(latestTimestamp);
        Log.d(TAG, String.format(
                "getAppUsageStartTimestampOfUser() userId=%d latestTimestamp=%s in %d/ms",
@@ -196,8 +198,8 @@ public final class DatabaseUtils {
                        .appendQueryParameter(QUERY_KEY_USERID, queryUserIdString)
                        .build();

        final List<AppUsageEvent> appUsageEventList =
                loadAppUsageEventsFromContentProvider(context, appUsageEventUri);
        final List<AppUsageEvent> appUsageEventList = loadListFromContentProvider(
                context, appUsageEventUri, ConvertUtils::convertToAppUsageEvent);
        Log.d(TAG, String.format("getAppUsageEventForUser userId=%s size=%d in %d/ms",
                queryUserIdString, appUsageEventList.size(),
                (System.currentTimeMillis() - startTime)));
@@ -229,8 +231,8 @@ public final class DatabaseUtils {
                                QUERY_BATTERY_EVENT_TYPE, queryBatteryEventTypesString)
                        .build();

        final List<BatteryEvent> batteryEventList =
                loadBatteryEventsFromContentProvider(context, batteryEventUri);
        final List<BatteryEvent> batteryEventList = loadListFromContentProvider(
                context, batteryEventUri, ConvertUtils::convertToBatteryEvent);
        Log.d(TAG, String.format("getBatteryEvents size=%d in %d/ms", batteryEventList.size(),
                (System.currentTimeMillis() - startTime)));
        return batteryEventList;
@@ -257,8 +259,8 @@ public final class DatabaseUtils {
                                QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
                        .build();

        final List<BatteryUsageSlot> batteryUsageSlotList =
                loadBatteryUsageSlotsFromContentProvider(context, batteryUsageSlotUri);
        final List<BatteryUsageSlot> batteryUsageSlotList = loadListFromContentProvider(
                context, batteryUsageSlotUri, ConvertUtils::convertToBatteryUsageSlot);
        Log.d(TAG, String.format("getBatteryUsageSlots size=%d in %d/ms",
                batteryUsageSlotList.size(), (System.currentTimeMillis() - startTime)));
        return batteryUsageSlotList;
@@ -274,8 +276,8 @@ public final class DatabaseUtils {
                        .authority(AUTHORITY)
                        .appendPath(LAST_FULL_CHARGE_TIMESTAMP_PATH)
                        .build();
        final long lastFullChargeTime = loadLastFullChargeTimeFromContentProvider(
                context, lastFullChargeTimeUri);
        final long lastFullChargeTime = loadLongFromContentProvider(
                context, lastFullChargeTimeUri, /*defaultValue=*/ INVALID_TIMESTAMP);
        final String lastFullChargeTimeString = utcToLocalTimeForLogging(lastFullChargeTime);
        Log.d(TAG, String.format(
                "getLastFullChargeTime() lastFullChargeTime=%s in %d/ms",
@@ -297,8 +299,8 @@ public final class DatabaseUtils {
                        .appendQueryParameter(
                                QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
                        .build();
        final long batteryStateLatestTimestamp = loadBatteryStateLatestTimestampFromContentProvider(
                context, batteryStateLatestTimestampUri);
        final long batteryStateLatestTimestamp = loadLongFromContentProvider(
                context, batteryStateLatestTimestampUri, /*defaultValue=*/ INVALID_TIMESTAMP);
        final String batteryStateLatestTimestampString =
                utcToLocalTimeForLogging(batteryStateLatestTimestamp);
        Log.d(TAG, String.format(
@@ -322,8 +324,21 @@ public final class DatabaseUtils {
                                QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
                        .build();

        final Map<Long, Map<String, BatteryHistEntry>> resultMap =
                loadHistoryMapFromContentProvider(context, batteryStateUri);
        final List<BatteryHistEntry> batteryHistEntryList = loadListFromContentProvider(
                context, batteryStateUri, cursor -> new BatteryHistEntry(cursor));
        final Map<Long, Map<String, BatteryHistEntry>> resultMap = new ArrayMap();
        for (final BatteryHistEntry entry : batteryHistEntryList) {
            final long timestamp = entry.mTimestamp;
            final String key = entry.getKey();
            Map batteryHistEntryMap = resultMap.get(timestamp);
            // Creates new one if there is no corresponding map.
            if (batteryHistEntryMap == null) {
                batteryHistEntryMap = new ArrayMap();
                resultMap.put(timestamp, batteryHistEntryMap);
            }
            batteryHistEntryMap.put(key, entry);
        }

        if (resultMap == null || resultMap.isEmpty()) {
            Log.d(TAG, "getBatteryHistoryMap() returns empty or null");
        } else {
@@ -713,160 +728,46 @@ public final class DatabaseUtils {
        }
    }

    private static void writeString(
            Context context, PrintWriter writer, String prefix, String key) {
        final SharedPreferences sharedPreferences = getSharedPreferences(context);
        if (sharedPreferences != null) {
            final String content = sharedPreferences.getString(key, "");
            writer.println(String.format("\t\t%s: %s", prefix, content));
        }
    }

    private static long loadAppUsageLatestTimestampFromContentProvider(
            Context context, final Uri appUsageLatestTimestampUri) {
        // 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(
                        appUsageLatestTimestampUri, null, null, null)) {
            if (cursor == null || cursor.getCount() == 0) {
                return INVALID_USER_ID;
            }
            cursor.moveToFirst();
            // There is only one column returned so use the index 0 directly.
            final long latestTimestamp = cursor.getLong(/*columnIndex=*/ 0);
            // If there is no data for this user, 0 will be returned from the database.
            return latestTimestamp == 0 ? INVALID_USER_ID : latestTimestamp;
        }
    }

    private static List<AppUsageEvent> loadAppUsageEventsFromContentProvider(
            Context context, Uri appUsageEventUri) {
        final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
    @VisibleForTesting
    static <T> T loadFromContentProvider(
            Context context, Uri uri, T defaultValue, Function<Cursor, T> cursorReader) {
        // Transfer work profile to user profile. Please see b/297036263.
        context = getParentContext(context);
        if (context == null) {
            return appUsageEventList;
        }
        try (Cursor cursor = sFakeSupplier != null
                ? sFakeSupplier.get()
                : context.getContentResolver().query(appUsageEventUri, null, null, null)) {
            if (cursor == null || cursor.getCount() == 0) {
                return appUsageEventList;
            return defaultValue;
        }
            // Loads and converts all AppUsageEvent data from cursor.
            while (cursor.moveToNext()) {
                appUsageEventList.add(ConvertUtils.convertToAppUsageEvent(cursor));
        try (Cursor cursor = sFakeSupplier != null ? sFakeSupplier.get() :
                context.getContentResolver().query(uri, null, null, null)) {
            return (cursor == null || cursor.getCount() == 0)
                    ? defaultValue : cursorReader.apply(cursor);
        }
    }
        return appUsageEventList;
    }

    private static List<BatteryEvent> loadBatteryEventsFromContentProvider(
            Context context, Uri batteryEventUri) {
        final List<BatteryEvent> batteryEventList = new ArrayList<>();
        context = getParentContext(context);
        if (context == null) {
            return batteryEventList;
        }
        try (Cursor cursor = sFakeSupplier != null
                ? sFakeSupplier.get()
                : context.getContentResolver().query(batteryEventUri, null, null, null)) {
            if (cursor == null || cursor.getCount() == 0) {
                return batteryEventList;
            }
            // Loads and converts all AppUsageEvent data from cursor.
            while (cursor.moveToNext()) {
                batteryEventList.add(ConvertUtils.convertToBatteryEvent(cursor));
            }
        }
        return batteryEventList;
    private static long loadLongFromContentProvider(
            Context context, Uri uri, final long defaultValue) {
        return loadFromContentProvider(context, uri, defaultValue,
                cursor -> cursor.moveToFirst() ? cursor.getLong(/*columnIndex=*/ 0) : defaultValue);
    }

    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.
    private static <E> List<E> loadListFromContentProvider(
            Context context, Uri uri, Function<Cursor, E> converter) {
        return loadFromContentProvider(context, uri, new ArrayList<>(),
                cursor -> {
                    final List<E> list = new ArrayList<>();
                    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;
                        list.add(converter.apply(cursor));
                    }
                    return list;
                });
    }

    private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
            Context context, Uri batteryStateUri) {
        context = getParentContext(context);
        if (context == null) {
            return null;
        }
        final Map<Long, Map<String, BatteryHistEntry>> resultMap = new HashMap();
        try (Cursor cursor = sFakeSupplier != null ? sFakeSupplier.get() :
                     context.getContentResolver().query(batteryStateUri, null, null, null)) {
            if (cursor == null || cursor.getCount() == 0) {
                return resultMap;
            }
            // Loads and converts all BatteryHistEntry data from cursor.
            while (cursor.moveToNext()) {
                final BatteryHistEntry entry = new BatteryHistEntry(cursor);
                final long timestamp = entry.mTimestamp;
                final String key = entry.getKey();
                Map batteryHistEntryMap = resultMap.get(timestamp);
                // Creates new one if there is no corresponding map.
                if (batteryHistEntryMap == null) {
                    batteryHistEntryMap = new HashMap<>();
                    resultMap.put(timestamp, batteryHistEntryMap);
                }
                batteryHistEntryMap.put(key, entry);
            }
    private static void writeString(
            Context context, PrintWriter writer, String prefix, String key) {
        final SharedPreferences sharedPreferences = getSharedPreferences(context);
        if (sharedPreferences != null) {
            final String content = sharedPreferences.getString(key, "");
            writer.println(String.format("\t\t%s: %s", prefix, content));
        }
        return resultMap;
    }

    private static void clearMemory() {
+16 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVIT
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -36,6 +38,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryUsageStats;
import android.os.RemoteException;
@@ -530,6 +533,19 @@ public final class DatabaseUtilsTest {
        assertThat(dumpContent.contains("LastUploadFullChargeTime")).isTrue();
    }

    @Test
    public void loadFromContentProvider_workProfile_transferToUserProfile() throws Exception {
        // Test to verify b/297036263
        doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
        doReturn(true).when(mUserManager).isManagedProfile();
        doReturn(UserHandle.CURRENT).when(mContext).getUser();
        doReturn(UserHandle.SYSTEM).when(mUserManager).getProfileParent(UserHandle.CURRENT);

        DatabaseUtils.loadFromContentProvider(mContext, Uri.EMPTY, null, cursor -> 1);

        verify(mContext).createPackageContextAsUser(anyString(), anyInt(), any());
    }

    private static void verifyBatteryEntryContentValues(
            double consumedPower, ContentValues values) {
        final BatteryInformation batteryInformation =