Loading Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ android_library { "guava", "jsr305", "net-utils-framework-common", "app-usage-event-protos-lite", "settings-contextual-card-protos-lite", "settings-log-bridge-protos-lite", "settings-telephony-protos-lite", Loading src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +7 −2 Original line number Diff line number Diff line Loading @@ -141,12 +141,17 @@ public interface PowerUsageFeatureProvider { Intent getResumeChargeIntent(boolean isDockDefender); /** * Returns {@link Set} for hidding applications background usage time. * Returns {@link Set} for hiding applications background usage time. */ Set<CharSequence> getHideBackgroundUsageTimeSet(Context context); /** * Returns package names for hidding application in the usage screen. * Returns package names for hiding application in the usage screen. */ CharSequence[] getHideApplicationEntries(Context context); /** * Returns {@link Set} for ignoring task root class names for screen on time. */ Set<CharSequence> getIgnoreScreenOnTimeTaskRootSet(Context context); } src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -165,4 +165,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider public CharSequence[] getHideApplicationEntries(Context context) { return new CharSequence[0]; } @Override public Set<CharSequence> getIgnoreScreenOnTimeTaskRootSet(Context context) { return new ArraySet<>(); } } src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoader.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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; import android.app.usage.UsageEvents; import android.content.Context; import android.os.AsyncTask; import android.util.Log; import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.Map; import java.util.function.Supplier; /** Load app usage events data in the background. */ public final class AppUsageDataLoader { private static final String TAG = "AppUsageDataLoader"; // For testing only. @VisibleForTesting static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier; @VisibleForTesting static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier; private AppUsageDataLoader() {} static void enqueueWork(final Context context) { AsyncTask.execute(() -> { Log.d(TAG, "loadAppUsageDataSafely() in the AsyncTask"); loadAppUsageDataSafely(context.getApplicationContext()); }); } @VisibleForTesting static void loadAppUsageData(final Context context) { final long start = System.currentTimeMillis(); final Map<Long, UsageEvents> appUsageEvents = sFakeAppUsageEventsSupplier != null ? sFakeAppUsageEventsSupplier.get() : DataProcessor.getAppUsageEvents(context); if (appUsageEvents == null) { Log.w(TAG, "loadAppUsageData() returns null"); return; } final List<AppUsageEvent> appUsageEventList = sFakeUsageEventsListSupplier != null ? sFakeUsageEventsListSupplier.get() : DataProcessor.generateAppUsageEventListFromUsageEvents( context, appUsageEvents); if (appUsageEventList == null || appUsageEventList.isEmpty()) { Log.w(TAG, "loadAppUsageData() returns null or empty content"); return; } final long elapsedTime = System.currentTimeMillis() - start; Log.d(TAG, String.format("loadAppUsageData() size=%d in %d/ms", appUsageEventList.size(), elapsedTime)); // Uploads the AppUsageEvent data into database. DatabaseUtils.sendAppUsageEventData(context, appUsageEventList); } private static void loadAppUsageDataSafely(final Context context) { try { loadAppUsageData(context); } catch (RuntimeException e) { Log.e(TAG, "loadAppUsageData:" + e); } } } src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java +63 −11 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity; import com.android.settings.fuelgauge.batteryusage.db.BatteryState; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; Loading @@ -43,11 +45,11 @@ public class BatteryUsageContentProvider extends ContentProvider { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public static final Duration QUERY_DURATION_HOURS = Duration.ofDays(6); @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public static final String QUERY_KEY_TIMESTAMP = "timestamp"; /** Codes */ private static final int BATTERY_STATE_CODE = 1; private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2; private static final int APP_USAGE_EVENT_CODE = 3; private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { Loading @@ -55,10 +57,19 @@ public class BatteryUsageContentProvider extends ContentProvider { DatabaseUtils.AUTHORITY, /*path=*/ DatabaseUtils.BATTERY_STATE_TABLE, /*code=*/ BATTERY_STATE_CODE); sUriMatcher.addURI( DatabaseUtils.AUTHORITY, /*path=*/ DatabaseUtils.APP_USAGE_LATEST_TIMESTAMP_PATH, /*code=*/ APP_USAGE_LATEST_TIMESTAMP_CODE); sUriMatcher.addURI( DatabaseUtils.AUTHORITY, /*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE, /*code=*/ APP_USAGE_EVENT_CODE); } private Clock mClock; private BatteryStateDao mBatteryStateDao; private AppUsageEventDao mAppUsageEventDao; @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public void setClock(Clock clock) { Loading @@ -73,6 +84,7 @@ public class BatteryUsageContentProvider extends ContentProvider { } mClock = Clock.systemUTC(); mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao(); mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao(); Log.w(TAG, "create content provider from " + getCallingPackage()); return true; } Loading @@ -88,6 +100,8 @@ public class BatteryUsageContentProvider extends ContentProvider { switch (sUriMatcher.match(uri)) { case BATTERY_STATE_CODE: return getBatteryStates(uri); case APP_USAGE_LATEST_TIMESTAMP_CODE: return getAppUsageLatestTimestamp(uri); default: throw new IllegalArgumentException("unknown URI: " + uri); } Loading @@ -111,6 +125,14 @@ public class BatteryUsageContentProvider extends ContentProvider { Log.e(TAG, "insert() from:" + uri + " error:" + e); return null; } case APP_USAGE_EVENT_CODE: try { mAppUsageEventDao.insert(AppUsageEventEntity.create(contentValues)); return uri; } catch (RuntimeException e) { Log.e(TAG, "insert() from:" + uri + " error:" + e); return null; } default: throw new IllegalArgumentException("unknown URI: " + uri); } Loading Loading @@ -145,24 +167,54 @@ public class BatteryUsageContentProvider extends ContentProvider { Log.e(TAG, "query() from:" + uri + " error:" + e); } AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext())); Log.w(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms"); Log.d(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms"); return cursor; } private Cursor getAppUsageLatestTimestamp(Uri uri) { final long queryUserId = getQueryUserId(uri); if (queryUserId == DatabaseUtils.INVALID_USER_ID) { return null; } final long timestamp = mClock.millis(); Cursor cursor = null; try { cursor = mAppUsageEventDao.getLatestTimestampOfUser(queryUserId); } catch (RuntimeException e) { Log.e(TAG, "query() from:" + uri + " error:" + e); } Log.d(TAG, String.format("query app usage latest timestamp %d for user %d in %d/ms", timestamp, queryUserId, (mClock.millis() - timestamp))); return cursor; } // If URI contains query parameter QUERY_KEY_USERID, use the value directly. // Otherwise, return INVALID_USER_ID. private long getQueryUserId(Uri uri) { Log.d(TAG, "getQueryUserId from uri: " + uri); return getQueryValueFromUri( uri, DatabaseUtils.QUERY_KEY_USERID, DatabaseUtils.INVALID_USER_ID); } // If URI contains query parameter QUERY_KEY_TIMESTAMP, use the value directly. // Otherwise, load the data for QUERY_DURATION_HOURS by default. private long getQueryTimestamp(Uri uri, long defaultTimestamp) { final String firstTimestampString = uri.getQueryParameter(QUERY_KEY_TIMESTAMP); if (TextUtils.isEmpty(firstTimestampString)) { Log.w(TAG, "empty query timestamp"); return defaultTimestamp; Log.d(TAG, "getQueryTimestamp from uri: " + uri); return getQueryValueFromUri(uri, DatabaseUtils.QUERY_KEY_TIMESTAMP, defaultTimestamp); } private long getQueryValueFromUri(Uri uri, String key, long defaultValue) { final String value = uri.getQueryParameter(key); if (TextUtils.isEmpty(value)) { Log.w(TAG, "empty query value"); return defaultValue; } try { return Long.parseLong(firstTimestampString); return Long.parseLong(value); } catch (NumberFormatException e) { Log.e(TAG, "invalid query timestamp: " + firstTimestampString, e); return defaultTimestamp; Log.e(TAG, "invalid query value: " + value, e); return defaultValue; } } } Loading
Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ android_library { "guava", "jsr305", "net-utils-framework-common", "app-usage-event-protos-lite", "settings-contextual-card-protos-lite", "settings-log-bridge-protos-lite", "settings-telephony-protos-lite", Loading
src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +7 −2 Original line number Diff line number Diff line Loading @@ -141,12 +141,17 @@ public interface PowerUsageFeatureProvider { Intent getResumeChargeIntent(boolean isDockDefender); /** * Returns {@link Set} for hidding applications background usage time. * Returns {@link Set} for hiding applications background usage time. */ Set<CharSequence> getHideBackgroundUsageTimeSet(Context context); /** * Returns package names for hidding application in the usage screen. * Returns package names for hiding application in the usage screen. */ CharSequence[] getHideApplicationEntries(Context context); /** * Returns {@link Set} for ignoring task root class names for screen on time. */ Set<CharSequence> getIgnoreScreenOnTimeTaskRootSet(Context context); }
src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +5 −0 Original line number Diff line number Diff line Loading @@ -165,4 +165,9 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider public CharSequence[] getHideApplicationEntries(Context context) { return new CharSequence[0]; } @Override public Set<CharSequence> getIgnoreScreenOnTimeTaskRootSet(Context context) { return new ArraySet<>(); } }
src/com/android/settings/fuelgauge/batteryusage/AppUsageDataLoader.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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; import android.app.usage.UsageEvents; import android.content.Context; import android.os.AsyncTask; import android.util.Log; import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.Map; import java.util.function.Supplier; /** Load app usage events data in the background. */ public final class AppUsageDataLoader { private static final String TAG = "AppUsageDataLoader"; // For testing only. @VisibleForTesting static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier; @VisibleForTesting static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier; private AppUsageDataLoader() {} static void enqueueWork(final Context context) { AsyncTask.execute(() -> { Log.d(TAG, "loadAppUsageDataSafely() in the AsyncTask"); loadAppUsageDataSafely(context.getApplicationContext()); }); } @VisibleForTesting static void loadAppUsageData(final Context context) { final long start = System.currentTimeMillis(); final Map<Long, UsageEvents> appUsageEvents = sFakeAppUsageEventsSupplier != null ? sFakeAppUsageEventsSupplier.get() : DataProcessor.getAppUsageEvents(context); if (appUsageEvents == null) { Log.w(TAG, "loadAppUsageData() returns null"); return; } final List<AppUsageEvent> appUsageEventList = sFakeUsageEventsListSupplier != null ? sFakeUsageEventsListSupplier.get() : DataProcessor.generateAppUsageEventListFromUsageEvents( context, appUsageEvents); if (appUsageEventList == null || appUsageEventList.isEmpty()) { Log.w(TAG, "loadAppUsageData() returns null or empty content"); return; } final long elapsedTime = System.currentTimeMillis() - start; Log.d(TAG, String.format("loadAppUsageData() size=%d in %d/ms", appUsageEventList.size(), elapsedTime)); // Uploads the AppUsageEvent data into database. DatabaseUtils.sendAppUsageEventData(context, appUsageEventList); } private static void loadAppUsageDataSafely(final Context context) { try { loadAppUsageData(context); } catch (RuntimeException e) { Log.e(TAG, "loadAppUsageData:" + e); } } }
src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java +63 −11 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity; import com.android.settings.fuelgauge.batteryusage.db.BatteryState; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; Loading @@ -43,11 +45,11 @@ public class BatteryUsageContentProvider extends ContentProvider { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public static final Duration QUERY_DURATION_HOURS = Duration.ofDays(6); @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public static final String QUERY_KEY_TIMESTAMP = "timestamp"; /** Codes */ private static final int BATTERY_STATE_CODE = 1; private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2; private static final int APP_USAGE_EVENT_CODE = 3; private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { Loading @@ -55,10 +57,19 @@ public class BatteryUsageContentProvider extends ContentProvider { DatabaseUtils.AUTHORITY, /*path=*/ DatabaseUtils.BATTERY_STATE_TABLE, /*code=*/ BATTERY_STATE_CODE); sUriMatcher.addURI( DatabaseUtils.AUTHORITY, /*path=*/ DatabaseUtils.APP_USAGE_LATEST_TIMESTAMP_PATH, /*code=*/ APP_USAGE_LATEST_TIMESTAMP_CODE); sUriMatcher.addURI( DatabaseUtils.AUTHORITY, /*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE, /*code=*/ APP_USAGE_EVENT_CODE); } private Clock mClock; private BatteryStateDao mBatteryStateDao; private AppUsageEventDao mAppUsageEventDao; @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public void setClock(Clock clock) { Loading @@ -73,6 +84,7 @@ public class BatteryUsageContentProvider extends ContentProvider { } mClock = Clock.systemUTC(); mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao(); mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao(); Log.w(TAG, "create content provider from " + getCallingPackage()); return true; } Loading @@ -88,6 +100,8 @@ public class BatteryUsageContentProvider extends ContentProvider { switch (sUriMatcher.match(uri)) { case BATTERY_STATE_CODE: return getBatteryStates(uri); case APP_USAGE_LATEST_TIMESTAMP_CODE: return getAppUsageLatestTimestamp(uri); default: throw new IllegalArgumentException("unknown URI: " + uri); } Loading @@ -111,6 +125,14 @@ public class BatteryUsageContentProvider extends ContentProvider { Log.e(TAG, "insert() from:" + uri + " error:" + e); return null; } case APP_USAGE_EVENT_CODE: try { mAppUsageEventDao.insert(AppUsageEventEntity.create(contentValues)); return uri; } catch (RuntimeException e) { Log.e(TAG, "insert() from:" + uri + " error:" + e); return null; } default: throw new IllegalArgumentException("unknown URI: " + uri); } Loading Loading @@ -145,24 +167,54 @@ public class BatteryUsageContentProvider extends ContentProvider { Log.e(TAG, "query() from:" + uri + " error:" + e); } AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext())); Log.w(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms"); Log.d(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms"); return cursor; } private Cursor getAppUsageLatestTimestamp(Uri uri) { final long queryUserId = getQueryUserId(uri); if (queryUserId == DatabaseUtils.INVALID_USER_ID) { return null; } final long timestamp = mClock.millis(); Cursor cursor = null; try { cursor = mAppUsageEventDao.getLatestTimestampOfUser(queryUserId); } catch (RuntimeException e) { Log.e(TAG, "query() from:" + uri + " error:" + e); } Log.d(TAG, String.format("query app usage latest timestamp %d for user %d in %d/ms", timestamp, queryUserId, (mClock.millis() - timestamp))); return cursor; } // If URI contains query parameter QUERY_KEY_USERID, use the value directly. // Otherwise, return INVALID_USER_ID. private long getQueryUserId(Uri uri) { Log.d(TAG, "getQueryUserId from uri: " + uri); return getQueryValueFromUri( uri, DatabaseUtils.QUERY_KEY_USERID, DatabaseUtils.INVALID_USER_ID); } // If URI contains query parameter QUERY_KEY_TIMESTAMP, use the value directly. // Otherwise, load the data for QUERY_DURATION_HOURS by default. private long getQueryTimestamp(Uri uri, long defaultTimestamp) { final String firstTimestampString = uri.getQueryParameter(QUERY_KEY_TIMESTAMP); if (TextUtils.isEmpty(firstTimestampString)) { Log.w(TAG, "empty query timestamp"); return defaultTimestamp; Log.d(TAG, "getQueryTimestamp from uri: " + uri); return getQueryValueFromUri(uri, DatabaseUtils.QUERY_KEY_TIMESTAMP, defaultTimestamp); } private long getQueryValueFromUri(Uri uri, String key, long defaultValue) { final String value = uri.getQueryParameter(key); if (TextUtils.isEmpty(value)) { Log.w(TAG, "empty query value"); return defaultValue; } try { return Long.parseLong(firstTimestampString); return Long.parseLong(value); } catch (NumberFormatException e) { Log.e(TAG, "invalid query timestamp: " + firstTimestampString, e); return defaultTimestamp; Log.e(TAG, "invalid query value: " + value, e); return defaultValue; } } }