diff options
8 files changed, 106 insertions, 39 deletions
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index ab71e73fd58c..0f999ad68a62 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -41,6 +41,43 @@ public final class UsageEvents implements Parcelable { /** @hide */ public static final String INSTANT_APP_CLASS_NAME = "android.instant_class"; + /** @hide */ + public static final String OBFUSCATED_NOTIFICATION_CHANNEL_ID = "unknown_channel_id"; + + /** + * Flag: indicates to not obfuscate or hide any usage event data when being queried. + * @hide + */ + public static final int SHOW_ALL_EVENT_DATA = 0x00000000; + + /** + * Flag: indicates to obfuscate package and class names for instant apps when querying usage + * events. + * @hide + */ + public static final int OBFUSCATE_INSTANT_APPS = 0x00000001; + + /** + * Flag: indicates to hide all {@link Event#SHORTCUT_INVOCATION} events when querying usage + * events. + * @hide + */ + public static final int HIDE_SHORTCUT_EVENTS = 0x00000002; + + /** + * Flag: indicates to obfuscate the notification channel id for all notification events, + * such as {@link Event#NOTIFICATION_SEEN} and {@link Event#NOTIFICATION_INTERRUPTION} events, + * when querying usage events. + * @hide + */ + public static final int OBFUSCATE_NOTIFICATION_EVENTS = 0x00000004; + + /** + * Flag: indicates to hide all {@link Event#LOCUS_ID_SET} events when querying usage events. + * @hide + */ + public static final int HIDE_LOCUS_EVENTS = 0x00000008; + /** * An event representing a state change for a component. */ @@ -627,6 +664,13 @@ public final class UsageEvents implements Parcelable { return ret; } + /** @hide */ + public Event getObfuscatedNotificationEvent() { + final Event ret = new Event(this); + ret.mNotificationChannelId = OBFUSCATED_NOTIFICATION_CHANNEL_ID; + return ret; + } + /** * Returns the locusId for this event if the event is of type {@link #LOCUS_ID_SET}, * otherwise it returns null. diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index af115b1e80c1..4b95e4d7fa6c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -381,6 +381,8 @@ applications that come with the platform <permission name="android.permission.REBOOT"/> <!-- Permission required for access VIBRATOR_STATE. --> <permission name="android.permission.ACCESS_VIBRATOR_STATE"/> + <!-- Permission required for UsageStatsTest CTS test. --> + <permission name="android.permission.MANAGE_NOTIFICATIONS"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index cc2c92b4c7eb..d821050ac5b4 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -253,7 +253,8 @@ <!-- Permission required for CTS test - ShortcutManagerUsageTest --> <uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/> - <!-- Permission required for CTS test - UsageStatsTest --> + <!-- Permissions required for CTS test - UsageStatsTest --> + <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS"/> <uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"/> <!-- Permissions required to test ambient display. --> diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index 442c9e543b01..f688759e9f63 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -216,17 +216,11 @@ public abstract class UsageStatsManagerInternal { /** * Returns the events for the user in the given time period. * - * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the - * result. - * @param hideShortcutInvocationEvents whether the {@link UsageEvents.Event#SHORTCUT_INVOCATION} - * events need to be excluded from the result. - * @param hideLocusIdEvents whether the {@link UsageEvents.Event#LOCUS_ID_SET} - * events need to be excluded from the result. - * + * @param flags defines the visibility of certain usage events - see flags defined in + * {@link UsageEvents}. */ public abstract UsageEvents queryEventsForUser(@UserIdInt int userId, long beginTime, - long endTime, boolean obfuscateInstantApps, boolean hideShortcutInvocationEvents, - boolean hideLocusIdEvents); + long endTime, int flags); /** * Used to persist the last time a job was run for this app, in order to make decisions later diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java index 644c155b4158..fba4f4e6a89c 100644 --- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java @@ -59,7 +59,7 @@ class UsageStatsQueryHelper { */ boolean querySince(long sinceTime) { UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser( - mUserId, sinceTime, System.currentTimeMillis(), false, false, false); + mUserId, sinceTime, System.currentTimeMillis(), UsageEvents.SHOW_ALL_EVENT_DATA); if (usageEvents == null) { return false; } diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index 01d9dc00cf47..c8543c4d5383 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -19,9 +19,9 @@ package com.android.server.people.data; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -189,7 +189,7 @@ public final class UsageStatsQueryHelperTest { private void addUsageEvents(UsageEvents.Event... events) { UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{}); when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(), - anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(usageEvents); + eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents); } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 420695dc51e4..df5b311bbab1 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -500,6 +500,19 @@ public class UsageStatsService extends SystemService implements == PackageManager.PERMISSION_GRANTED); } + /** + * Obfuscate both {@link UsageEvents.Event#NOTIFICATION_SEEN} and + * {@link UsageEvents.Event#NOTIFICATION_INTERRUPTION} events if the provided calling uid does + * not hold the {@link android.Manifest.permission.MANAGE_NOTIFICATIONS} permission. + */ + private boolean shouldObfuscateNotificationEvents(int callingPid, int callingUid) { + if (callingUid == Process.SYSTEM_UID) { + return false; + } + return !(getContext().checkPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS, + callingPid, callingUid) == PackageManager.PERMISSION_GRANTED); + } + private static void deleteRecursively(File f) { File[] files = f.listFiles(); if (files != null) { @@ -1038,9 +1051,7 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - UsageEvents queryEvents(int userId, long beginTime, long endTime, - boolean shouldObfuscateInstantApps, boolean shouldHideShortcutInvocationEvents, - boolean shouldHideLocusIdEvents) { + UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) { synchronized (mLock) { if (!mUserUnlockedStates.get(userId)) { Slog.w(TAG, "Failed to query events for locked user " + userId); @@ -1051,8 +1062,7 @@ public class UsageStatsService extends SystemService implements if (service == null) { return null; // user was stopped or removed } - return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps, - shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents); + return service.queryEvents(beginTime, endTime, flags); } } @@ -1475,10 +1485,15 @@ public class UsageStatsService extends SystemService implements try { final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( userId, callingPackage, callingPid, callingUid); - boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); - return UsageStatsService.this.queryEvents(userId, beginTime, endTime, - obfuscateInstantApps, hideShortcutInvocationEvents, - shouldHideLocusIdEvents); + final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); + final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents( + callingPid, callingUid); + int flags = UsageEvents.SHOW_ALL_EVENT_DATA; + if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS; + if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS; + if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS; + if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS; + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags); } finally { Binder.restoreCallingIdentity(token); } @@ -1525,10 +1540,15 @@ public class UsageStatsService extends SystemService implements try { final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( userId, callingPackage, callingPid, callingUid); - boolean shouldHideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); - return UsageStatsService.this.queryEvents(userId, beginTime, endTime, - obfuscateInstantApps, hideShortcutInvocationEvents, - shouldHideLocusIdEvents); + final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents( + callingPid, callingUid); + boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid); + int flags = UsageEvents.SHOW_ALL_EVENT_DATA; + if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS; + if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS; + if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS; + if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS; + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags); } finally { Binder.restoreCallingIdentity(token); } @@ -2144,12 +2164,8 @@ public class UsageStatsService extends SystemService implements } @Override - public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime, - boolean obfuscateInstantApps, boolean shouldHideShortcutInvocationEvents, - boolean shouldHideLocusIdEvents) { - return UsageStatsService.this.queryEvents( - userId, beginTime, endTime, obfuscateInstantApps, - shouldHideShortcutInvocationEvents, shouldHideLocusIdEvents); + public UsageEvents queryEventsForUser(int userId, long beginTime, long endTime, int flags) { + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags); } @Override diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index d9317ace6e24..db26d88dbfbb 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -18,6 +18,10 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.DEVICE_STARTUP; +import static android.app.usage.UsageEvents.HIDE_LOCUS_EVENTS; +import static android.app.usage.UsageEvents.HIDE_SHORTCUT_EVENTS; +import static android.app.usage.UsageEvents.OBFUSCATE_INSTANT_APPS; +import static android.app.usage.UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS; import static android.app.usage.UsageStatsManager.INTERVAL_BEST; import static android.app.usage.UsageStatsManager.INTERVAL_COUNT; import static android.app.usage.UsageStatsManager.INTERVAL_DAILY; @@ -481,8 +485,7 @@ class UserUsageStatsService { return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner); } - UsageEvents queryEvents(final long beginTime, final long endTime, boolean obfuscateInstantApps, - boolean hideShortcutInvocationEvents, boolean hideLocusIdEvents) { + UsageEvents queryEvents(final long beginTime, final long endTime, int flags) { if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) { return null; } @@ -500,15 +503,22 @@ class UserUsageStatsService { } Event event = stats.events.get(i); - if (hideShortcutInvocationEvents - && event.mEventType == Event.SHORTCUT_INVOCATION) { + final int eventType = event.mEventType; + if (eventType == Event.SHORTCUT_INVOCATION + && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) { continue; } - if (hideLocusIdEvents - && event.mEventType == Event.LOCUS_ID_SET) { + if (eventType == Event.LOCUS_ID_SET + && (flags & HIDE_LOCUS_EVENTS) == HIDE_LOCUS_EVENTS) { continue; } - if (obfuscateInstantApps) { + if ((eventType == Event.NOTIFICATION_SEEN + || eventType == Event.NOTIFICATION_INTERRUPTION) + && (flags & OBFUSCATE_NOTIFICATION_EVENTS) + == OBFUSCATE_NOTIFICATION_EVENTS) { + event = event.getObfuscatedNotificationEvent(); + } + if ((flags & OBFUSCATE_INSTANT_APPS) == OBFUSCATE_INSTANT_APPS) { event = event.getObfuscatedIfInstantApp(); } if (event.mPackage != null) { |