diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java index 2e977fd143d..c1b7818db34 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java @@ -17,7 +17,9 @@ package com.android.settings.fuelgauge.batteryusage; import android.annotation.IntDef; import android.annotation.Nullable; +import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents.Event; +import android.app.usage.UsageStatsManager; import android.content.ContentValues; import android.content.Context; import android.content.pm.PackageManager; @@ -25,7 +27,9 @@ import android.database.Cursor; import android.os.BatteryUsageStats; import android.os.Build; import android.os.LocaleList; +import android.os.RemoteException; import android.os.UserHandle; +import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Base64; import android.util.Log; @@ -167,8 +171,10 @@ public final class ConvertUtils { /** Converts to {@link AppUsageEvent} from {@link Event} */ @Nullable public static AppUsageEvent convertToAppUsageEvent( - Context context, final Event event, final long userId) { - if (event.getPackageName() == null) { + Context context, final IUsageStatsManager usageStatsManager, final Event event, + final long userId) { + final String packageName = event.getPackageName(); + if (packageName == null) { // See b/190609174: Event package names should never be null, but sometimes they are. // Note that system events like device shutting down should still come with the android // package name. @@ -182,13 +188,20 @@ public final class ConvertUtils { appUsageEventBuilder .setTimestamp(event.getTimeStamp()) .setType(getAppUsageEventType(event.getEventType())) - .setPackageName(event.getPackageName()) + .setPackageName(packageName) .setUserId(userId); + final String taskRootPackageName = getTaskRootPackageName(event); + if (taskRootPackageName != null) { + appUsageEventBuilder.setTaskRootPackageName(taskRootPackageName); + } + + final String effectivePackageName = + getEffectivePackageName(usageStatsManager, packageName, taskRootPackageName); try { final long uid = context .getPackageManager() - .getPackageUidAsUser(event.getPackageName(), (int) userId); + .getPackageUidAsUser(effectivePackageName, (int) userId); appUsageEventBuilder.setUid(uid); } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, String.format( @@ -201,10 +214,6 @@ public final class ConvertUtils { } catch (NoClassDefFoundError | NoSuchMethodError e) { Log.w(TAG, "UsageEvent instance ID API error"); } - String taskRootPackageName = getTaskRootPackageName(event); - if (taskRootPackageName != null) { - appUsageEventBuilder.setTaskRootPackageName(taskRootPackageName); - } return appUsageEventBuilder.build(); } @@ -267,6 +276,35 @@ public final class ConvertUtils { : Locale.getDefault(); } + /** + * Returns the package name the app usage should be attributed to. + * + * + */ + @VisibleForTesting + static String getEffectivePackageName( + final IUsageStatsManager usageStatsManager, final String packageName, + final String taskRootPackageName) { + int usageSource = getUsageSource(usageStatsManager); + switch (usageSource) { + case UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY: + return !TextUtils.isEmpty(taskRootPackageName) + ? taskRootPackageName + : packageName; + case UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY: + return packageName; + default: + Log.e(TAG, "Unexpected usage source: " + usageSource); + return packageName; + } + } + /** * Returns the package name of the task root when this event was reported when {@code event} is * one of: @@ -298,6 +336,20 @@ public final class ConvertUtils { } } + /** + * Returns what App Usage Observers will consider the source of usage for an activity. + * + * @see UsageStatsManager#getUsageSource() + */ + private static int getUsageSource(final IUsageStatsManager usageStatsManager) { + try { + return usageStatsManager.getUsageSource(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to getUsageSource", e); + return UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY; + } + } + private static AppUsageEventType getAppUsageEventType(final int eventType) { switch (eventType) { case Event.ACTIVITY_RESUMED: diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java index 3b3a13582da..c33e0a3ffb0 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java @@ -256,7 +256,8 @@ public final class DataProcessor { break; } final AppUsageEvent appUsageEvent = - ConvertUtils.convertToAppUsageEvent(context, event, userId); + ConvertUtils.convertToAppUsageEvent( + context, sUsageStatsManager, event, userId); if (appUsageEvent != null) { numEventsFetched++; appUsageEventList.add(appUsageEvent); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java index aee8398198c..fe1bff6ce00 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java @@ -15,6 +15,9 @@ */ package com.android.settings.fuelgauge.batteryusage; +import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY; +import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -22,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; import android.content.ContentValues; @@ -31,6 +35,7 @@ import android.database.MatrixCursor; import android.os.BatteryManager; import android.os.BatteryUsageStats; import android.os.LocaleList; +import android.os.RemoteException; import android.os.UserHandle; import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity; @@ -56,6 +61,8 @@ public final class ConvertUtilsTest { @Mock private BatteryUsageStats mBatteryUsageStats; @Mock + private IUsageStatsManager mUsageStatsManager; + @Mock private BatteryEntry mMockBatteryEntry; @Before @@ -278,7 +285,7 @@ public final class ConvertUtilsTest { final long userId = 2; final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent( - mContext, event, userId); + mContext, mUsageStatsManager, event, userId); assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L); assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.ACTIVITY_RESUMED); assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1"); @@ -299,7 +306,7 @@ public final class ConvertUtilsTest { final long userId = 1; final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent( - mContext, event, userId); + mContext, mUsageStatsManager, event, userId); assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L); assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN); assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1"); @@ -315,7 +322,7 @@ public final class ConvertUtilsTest { event.mPackage = null; final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent( - mContext, event, /*userId=*/ 0); + mContext, mUsageStatsManager, event, /*userId=*/ 0); assertThat(appUsageEvent).isNull(); } @@ -331,7 +338,7 @@ public final class ConvertUtilsTest { final long userId = 1; final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent( - mContext, event, userId); + mContext, mUsageStatsManager, event, userId); assertThat(appUsageEvent).isNull(); } @@ -415,4 +422,53 @@ public final class ConvertUtilsTest { mContext.getResources().getConfiguration().setLocales(new LocaleList()); assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); } + + @Test + public void getEffectivePackageName_currentActivity_returnPackageName() throws RemoteException { + when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_CURRENT_ACTIVITY); + final String packageName = "com.android.settings1"; + final String taskRootPackageName = "com.android.settings2"; + + assertThat(ConvertUtils.getEffectivePackageName( + mUsageStatsManager, packageName, taskRootPackageName)) + .isEqualTo(packageName); + } + + @Test + public void getEffectivePackageName_usageSourceThrowException_returnPackageName() + throws RemoteException { + when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException()); + final String packageName = "com.android.settings1"; + final String taskRootPackageName = "com.android.settings2"; + + assertThat(ConvertUtils.getEffectivePackageName( + mUsageStatsManager, packageName, taskRootPackageName)) + .isEqualTo(packageName); + } + + @Test + public void getEffectivePackageName_rootActivity_returnTaskRootPackageName() + throws RemoteException { + when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY); + final String packageName = "com.android.settings1"; + final String taskRootPackageName = "com.android.settings2"; + + assertThat(ConvertUtils.getEffectivePackageName( + mUsageStatsManager, packageName, taskRootPackageName)) + .isEqualTo(taskRootPackageName); + } + + @Test + public void getEffectivePackageName_nullOrEmptyTaskRoot_returnPackageName() + throws RemoteException { + when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY); + final String packageName = "com.android.settings1"; + + assertThat(ConvertUtils.getEffectivePackageName( + mUsageStatsManager, packageName, /*taskRootPackageName=*/ null)) + .isEqualTo(packageName); + assertThat(ConvertUtils.getEffectivePackageName( + mUsageStatsManager, packageName, /*taskRootPackageName=*/ "")) + .isEqualTo(packageName); + } }