diff options
| author | 2023-11-03 13:47:22 +0000 | |
|---|---|---|
| committer | 2023-11-03 13:47:22 +0000 | |
| commit | 06ab26fee3325433d7d1e9c99b17ab483e884026 (patch) | |
| tree | b47d09ef50905ecd0e3c9c87e5c7aa1a7302a2a8 | |
| parent | 410820bb8b1980b2f478e908c3e4407e34915a14 (diff) | |
| parent | fef1c6a6c75a8f1e8b05a4c053c00ef275a6de61 (diff) | |
Merge "Reland "UsageStats: New event query API with specific event types."" into main
| -rw-r--r-- | core/api/current.txt | 16 | ||||
| -rw-r--r-- | core/java/android/app/usage/IUsageStatsManager.aidl | 5 | ||||
| -rw-r--r-- | core/java/android/app/usage/UsageEvents.java | 41 | ||||
| -rw-r--r-- | core/java/android/app/usage/UsageEventsQuery.aidl | 19 | ||||
| -rw-r--r-- | core/java/android/app/usage/UsageEventsQuery.java | 173 | ||||
| -rw-r--r-- | core/java/android/app/usage/UsageStatsManager.java | 24 | ||||
| -rw-r--r-- | core/java/android/app/usage/flags.aconfig | 7 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java | 134 | ||||
| -rw-r--r-- | services/usage/java/com/android/server/usage/UsageStatsService.java | 122 | ||||
| -rw-r--r-- | services/usage/java/com/android/server/usage/UserUsageStatsService.java | 22 |
10 files changed, 504 insertions, 59 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index e02803d27783..fec797e64f8f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9321,6 +9321,21 @@ package android.app.usage { field public static final int USER_INTERACTION = 7; // 0x7 } + @FlaggedApi("android.app.usage.filter_based_event_query_api") public final class UsageEventsQuery implements android.os.Parcelable { + method public int describeContents(); + method public long getBeginTimeMillis(); + method public long getEndTimeMillis(); + method @NonNull public java.util.Set<java.lang.Integer> getEventTypes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.UsageEventsQuery> CREATOR; + } + + public static final class UsageEventsQuery.Builder { + ctor public UsageEventsQuery.Builder(long, long); + method @NonNull public android.app.usage.UsageEventsQuery.Builder addEventTypes(@NonNull int...); + method @NonNull public android.app.usage.UsageEventsQuery build(); + } + public final class UsageStats implements android.os.Parcelable { ctor public UsageStats(android.app.usage.UsageStats); method public void add(android.app.usage.UsageStats); @@ -9345,6 +9360,7 @@ package android.app.usage { method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long); method public java.util.List<android.app.usage.EventStats> queryEventStats(int, long, long); method public android.app.usage.UsageEvents queryEvents(long, long); + method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery); method public android.app.usage.UsageEvents queryEventsForSelf(long, long); method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long); field public static final int INTERVAL_BEST = 4; // 0x4 diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 49543a1e3d99..ebd5d649fc2b 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -20,10 +20,9 @@ import android.app.PendingIntent; import android.app.usage.BroadcastResponseStats; import android.app.usage.BroadcastResponseStatsList; import android.app.usage.UsageEvents; +import android.app.usage.UsageEventsQuery; import android.content.pm.ParceledListSlice; -import java.util.Map; - /** * System private API for talking with the UsageStatsManagerService. * @@ -42,6 +41,8 @@ interface IUsageStatsManager { UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage); UsageEvents queryEventsForUser(long beginTime, long endTime, int userId, String callingPackage); UsageEvents queryEventsForPackageForUser(long beginTime, long endTime, int userId, String pkg, String callingPackage); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)") + UsageEvents queryEventsWithFilter(in UsageEventsQuery query, String callingPackage); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void setAppInactive(String packageName, boolean inactive, int userId); boolean isAppStandbyEnabled(); diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index c188686d2d5b..1eb452cfd085 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -349,6 +349,47 @@ public final class UsageEvents implements Parcelable { */ public static final int MAX_EVENT_TYPE = 31; + /** + * Keep in sync with the event types defined above. + * @hide + */ + @IntDef(flag = false, value = { + NONE, + ACTIVITY_RESUMED, + ACTIVITY_PAUSED, + END_OF_DAY, + CONTINUE_PREVIOUS_DAY, + CONFIGURATION_CHANGE, + SYSTEM_INTERACTION, + USER_INTERACTION, + SHORTCUT_INVOCATION, + CHOOSER_ACTION, + NOTIFICATION_SEEN, + STANDBY_BUCKET_CHANGED, + NOTIFICATION_INTERRUPTION, + SLICE_PINNED_PRIV, + SLICE_PINNED, + SCREEN_INTERACTIVE, + SCREEN_NON_INTERACTIVE, + KEYGUARD_SHOWN, + KEYGUARD_HIDDEN, + FOREGROUND_SERVICE_START, + FOREGROUND_SERVICE_STOP, + CONTINUING_FOREGROUND_SERVICE, + ROLLOVER_FOREGROUND_SERVICE, + ACTIVITY_STOPPED, + ACTIVITY_DESTROYED, + FLUSH_TO_DISK, + DEVICE_SHUTDOWN, + DEVICE_STARTUP, + USER_UNLOCKED, + USER_STOPPED, + LOCUS_ID_SET, + APP_COMPONENT_USED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EventType {} + /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/core/java/android/app/usage/UsageEventsQuery.aidl b/core/java/android/app/usage/UsageEventsQuery.aidl new file mode 100644 index 000000000000..5ed370ddfa86 --- /dev/null +++ b/core/java/android/app/usage/UsageEventsQuery.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2023 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 android.app.usage; + +parcelable UsageEventsQuery;
\ No newline at end of file diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java new file mode 100644 index 000000000000..8c63d1857865 --- /dev/null +++ b/core/java/android/app/usage/UsageEventsQuery.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2023 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 android.app.usage; + +import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.app.usage.UsageEvents.Event; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArraySet; + +import com.android.internal.util.ArrayUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * An Object-Oriented representation for a {@link UsageEvents} query. + * Used by {@link UsageStatsManager#queryEvents(UsageEventsQuery)} call. + */ +@FlaggedApi(Flags.FLAG_FILTER_BASED_EVENT_QUERY_API) +public final class UsageEventsQuery implements Parcelable { + private final @CurrentTimeMillisLong long mBeginTimeMillis; + private final @CurrentTimeMillisLong long mEndTimeMillis; + private final @Event.EventType int[] mEventTypes; + + private UsageEventsQuery(@NonNull Builder builder) { + mBeginTimeMillis = builder.mBeginTimeMillis; + mEndTimeMillis = builder.mEndTimeMillis; + mEventTypes = ArrayUtils.convertToIntArray(builder.mEventTypes); + } + + private UsageEventsQuery(Parcel in) { + mBeginTimeMillis = in.readLong(); + mEndTimeMillis = in.readLong(); + int eventTypesLength = in.readInt(); + mEventTypes = new int[eventTypesLength]; + in.readIntArray(mEventTypes); + } + + /** + * Returns the inclusive timestamp to indicate the beginning of the range of events. + * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}. + */ + public @CurrentTimeMillisLong long getBeginTimeMillis() { + return mBeginTimeMillis; + } + + /** + * Returns the exclusive timpstamp to indicate the end of the range of events. + * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}. + */ + public @CurrentTimeMillisLong long getEndTimeMillis() { + return mEndTimeMillis; + } + + /** + * Returns the set of usage event types for the query. + * <em>Note: An empty set indicates query for all usage events. </em> + */ + public @NonNull Set<Integer> getEventTypes() { + if (ArrayUtils.isEmpty(mEventTypes)) { + return Collections.emptySet(); + } + + HashSet<Integer> eventTypeSet = new HashSet<>(); + for (int eventType : mEventTypes) { + eventTypeSet.add(eventType); + } + return eventTypeSet; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mBeginTimeMillis); + dest.writeLong(mEndTimeMillis); + dest.writeInt(mEventTypes.length); + dest.writeIntArray(mEventTypes); + } + + @NonNull + public static final Creator<UsageEventsQuery> CREATOR = + new Creator<UsageEventsQuery>() { + @Override + public UsageEventsQuery createFromParcel(Parcel in) { + return new UsageEventsQuery(in); + } + + @Override + public UsageEventsQuery[] newArray(int size) { + return new UsageEventsQuery[size]; + } + }; + + /** @hide */ + public int[] getEventTypeFilter() { + return Arrays.copyOf(mEventTypes, mEventTypes.length); + } + + /** + * Builder for UsageEventsQuery. + */ + public static final class Builder { + private final @CurrentTimeMillisLong long mBeginTimeMillis; + private final @CurrentTimeMillisLong long mEndTimeMillis; + private final ArraySet<Integer> mEventTypes = new ArraySet<>(); + + /** + * Constructor that specifies the period for which to return events. + * @param beginTimeMillis Inclusive beginning timestamp, as per + * {@link java.lang.System#currentTimeMillis()} + * @param endTimeMillis Exclusive ending timestamp, as per + * {@link java.lang.System#currentTimeMillis()} + * + * @throws IllegalArgumentException if {@code beginTimeMillis} < + * {@code endTimeMillis} + */ + public Builder(@CurrentTimeMillisLong long beginTimeMillis, + @CurrentTimeMillisLong long endTimeMillis) { + if (beginTimeMillis < 0 || endTimeMillis < beginTimeMillis) { + throw new IllegalArgumentException("Invalid period"); + } + mBeginTimeMillis = beginTimeMillis; + mEndTimeMillis = endTimeMillis; + } + + /** + * Builds a read-only UsageEventsQuery object. + */ + public @NonNull UsageEventsQuery build() { + return new UsageEventsQuery(this); + } + + /** + * Specifies the list of usage event types to be included in the query. + * @param eventTypes List of the usage event types. See {@link UsageEvents.Event} + * + * @throws llegalArgumentException if the event type is not valid. + */ + public @NonNull Builder addEventTypes(@NonNull @Event.EventType int... eventTypes) { + for (int i = 0; i < eventTypes.length; i++) { + final int eventType = eventTypes[i]; + if (eventType < Event.NONE || eventType > Event.MAX_EVENT_TYPE) { + throw new IllegalArgumentException("Invalid usage event type: " + eventType); + } + mEventTypes.add(eventType); + } + return this; + } + } +} diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 2a10ed181576..4f1c993bfa1a 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -18,6 +18,7 @@ package android.app.usage; import android.Manifest; import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -581,6 +582,29 @@ public final class UsageStatsManager { } /** + * Query for events with specific UsageEventsQuery object. + * <em>Note: if the user's device is not in an unlocked state (as defined by + * {@link UserManager#isUserUnlocked()}), then {@code null} will be returned.</em> + * + * @param query The query object used to specify the query parameters. + * @return A {@link UsageEvents}. + */ + @FlaggedApi(Flags.FLAG_FILTER_BASED_EVENT_QUERY_API) + @NonNull + @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) + public UsageEvents queryEvents(@NonNull UsageEventsQuery query) { + try { + UsageEvents iter = mService.queryEventsWithFilter(query, mContext.getOpPackageName()); + if (iter != null) { + return iter; + } + } catch (RemoteException e) { + // fallthrough and return empty result. + } + return sEmptyResults; + } + + /** * Like {@link #queryEvents(long, long)}, but only returns events for the calling package. * <em>Note: Starting from {@link android.os.Build.VERSION_CODES#R Android R}, if the user's * device is not in an unlocked state (as defined by {@link UserManager#isUserUnlocked()}), diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig index 0b8e29f954a5..a611255c3817 100644 --- a/core/java/android/app/usage/flags.aconfig +++ b/core/java/android/app/usage/flags.aconfig @@ -28,3 +28,10 @@ flag { description: "Flag for parcelable usage event list" bug: "301254110" } + +flag { + name: "filter_based_event_query_api" + namespace: "backstage_power" + description: " Feature flag to support filter based event query API" + bug: "194321117" +} diff --git a/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java new file mode 100644 index 000000000000..839b645cf352 --- /dev/null +++ b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2023 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 android.app.usage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.app.usage.UsageEvents.Event; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Random; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class UsageEventsQueryTest { + @Test + public void testQueryDuration() { + // Test with negative beginTimeMillis. + long beginTimeMillis = -100; + long endTimeMillis = 100; + try { + UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis) + .build(); + fail("beginTimeMillis should be a non-negative timestamp measured as the number of" + + " milliseconds since 1970-01-01T00:00:00Z."); + } catch (IllegalArgumentException e) { + // Expected, fall through; + } + + // Test with negative endTimeMillis. + beginTimeMillis = 1001; + endTimeMillis = -1; + try { + UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis) + .build(); + fail("endTimeMillis should be a non-negative timestamp measured as the number of" + + " milliseconds since 1970-01-01T00:00:00Z."); + } catch (IllegalArgumentException e) { + // Expected, fall through; + } + + // Test with beginTimeMillis < endTimeMillis; + beginTimeMillis = 2001; + endTimeMillis = 1000; + try { + UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis) + .build(); + fail("beginTimeMillis should be smaller than endTimeMillis"); + } catch (IllegalArgumentException e) { + // Expected, fall through; + } + + // Test with beginTimeMillis == endTimeMillis, valid. + beginTimeMillis = 1001; + endTimeMillis = 1001; + try { + UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis) + .build(); + assertEquals(query.getBeginTimeMillis(), query.getEndTimeMillis()); + } catch (IllegalArgumentException e) { + // Not expected for valid duration. + fail("Valid duration for beginTimeMillis=" + beginTimeMillis + + ", endTimeMillis=" + endTimeMillis); + } + + beginTimeMillis = 2001; + endTimeMillis = 3001; + try { + UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis) + .build(); + assertEquals(query.getBeginTimeMillis(), 2001); + assertEquals(query.getEndTimeMillis(), 3001); + } catch (IllegalArgumentException e) { + // Not expected for valid duration. + fail("Valid duration for beginTimeMillis=" + beginTimeMillis + + ", endTimeMillis=" + endTimeMillis); + } + } + + @Test + public void testQueryEventTypes() { + Random rnd = new Random(); + UsageEventsQuery.Builder queryBuilder = new UsageEventsQuery.Builder(1000, 2000); + + // Test with invalid event type. + int eventType = Event.NONE - 1; + try { + queryBuilder.addEventTypes(eventType); + fail("Invalid event type: " + eventType); + } catch (IllegalArgumentException e) { + // Expected, fall through. + } + + eventType = Event.MAX_EVENT_TYPE + 1; + try { + queryBuilder.addEventTypes(eventType); + fail("Invalid event type: " + eventType); + } catch (IllegalArgumentException e) { + // Expected, fall through. + } + + // Test with valid and duplicate event types. + eventType = rnd.nextInt(Event.MAX_EVENT_TYPE + 1); + try { + UsageEventsQuery query = queryBuilder.addEventTypes(eventType, eventType, eventType) + .build(); + Set<Integer> eventTypeSet = query.getEventTypes(); + assertEquals(eventTypeSet.size(), 1); + int type = eventTypeSet.iterator().next(); + assertEquals(type, eventType); + } catch (IllegalArgumentException e) { + fail("Valid event type: " + eventType); + } + } +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index e413663160b5..f64ab22628d9 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -57,6 +57,7 @@ import android.app.usage.Flags; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; +import android.app.usage.UsageEventsQuery; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManager.StandbyBuckets; @@ -113,6 +114,8 @@ import com.android.server.SystemService; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import com.android.server.utils.AlarmQueue; +import libcore.util.EmptyArray; + import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -1478,6 +1481,14 @@ public class UsageStatsService extends SystemService implements * Called by the Binder stub. */ UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) { + return queryEventsWithTypes(userId, beginTime, endTime, flags, EmptyArray.INT); + } + + /** + * Called by the Binder stub. + */ + UsageEvents queryEventsWithTypes(int userId, long beginTime, long endTime, int flags, + int[] eventTypeFilter) { synchronized (mLock) { if (!mUserUnlockedStates.contains(userId)) { Slog.w(TAG, "Failed to query events for locked user " + userId); @@ -1488,7 +1499,7 @@ public class UsageStatsService extends SystemService implements if (service == null) { return null; // user was stopped or removed } - return service.queryEvents(beginTime, endTime, flags); + return service.queryEvents(beginTime, endTime, flags, eventTypeFilter); } } @@ -2123,7 +2134,7 @@ public class UsageStatsService extends SystemService implements private final class BinderService extends IUsageStatsManager.Stub { - private boolean hasPermission(String callingPackage) { + private boolean hasQueryPermission(String callingPackage) { final int callingUid = Binder.getCallingUid(); if (callingUid == Process.SYSTEM_UID) { return true; @@ -2203,10 +2214,37 @@ public class UsageStatsService extends SystemService implements return uid == Process.SYSTEM_UID; } + private UsageEvents queryEventsHelper(int userId, long beginTime, long endTime, + String callingPackage, int[] eventTypeFilter) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( + callingUid, userId); + + final long token = Binder.clearCallingIdentity(); + try { + final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( + userId, callingPackage, callingPid, callingUid); + 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.queryEventsWithTypes(userId, beginTime, endTime, + flags, eventTypeFilter); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime, String callingPackage, int userId) { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { return null; } @@ -2234,7 +2272,7 @@ public class UsageStatsService extends SystemService implements @Override public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime, String callingPackage) throws RemoteException { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { return null; } @@ -2256,7 +2294,7 @@ public class UsageStatsService extends SystemService implements @Override public ParceledListSlice<EventStats> queryEventStats(int bucketType, long beginTime, long endTime, String callingPackage) throws RemoteException { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { return null; } @@ -2277,32 +2315,25 @@ public class UsageStatsService extends SystemService implements @Override public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { return null; } - final int userId = UserHandle.getCallingUserId(); - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( - callingUid, userId); + return queryEventsHelper(UserHandle.getCallingUserId(), beginTime, endTime, + callingPackage, /* eventTypeFilter= */ EmptyArray.INT); + } - final long token = Binder.clearCallingIdentity(); - try { - final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( - userId, callingPackage, callingPid, callingUid); - 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); + @Override + public UsageEvents queryEventsWithFilter(@NonNull UsageEventsQuery query, + @NonNull String callingPackage) { + Objects.requireNonNull(query); + Objects.requireNonNull(callingPackage); + + if (!hasQueryPermission(callingPackage)) { + return null; } + return queryEventsHelper(UserHandle.getCallingUserId(), query.getBeginTimeMillis(), + query.getEndTimeMillis(), callingPackage, query.getEventTypeFilter()); } @Override @@ -2312,7 +2343,7 @@ public class UsageStatsService extends SystemService implements final int callingUserId = UserHandle.getUserId(callingUid); checkCallerIsSameApp(callingPackage); - final boolean includeTaskRoot = hasPermission(callingPackage); + final boolean includeTaskRoot = hasQueryPermission(callingPackage); final long token = Binder.clearCallingIdentity(); try { @@ -2326,7 +2357,7 @@ public class UsageStatsService extends SystemService implements @Override public UsageEvents queryEventsForUser(long beginTime, long endTime, int userId, String callingPackage) { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { return null; } @@ -2337,33 +2368,14 @@ public class UsageStatsService extends SystemService implements "No permission to query usage stats for this user"); } - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( - callingUid, callingUserId); - - final long token = Binder.clearCallingIdentity(); - try { - final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents( - userId, callingPackage, callingPid, callingUid); - 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); - } + return queryEventsHelper(userId, beginTime, endTime, callingPackage, + /* eventTypeFilter= */ EmptyArray.INT); } @Override public UsageEvents queryEventsForPackageForUser(long beginTime, long endTime, int userId, String pkg, String callingPackage) { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { return null; } if (userId != UserHandle.getCallingUserId()) { @@ -2404,7 +2416,7 @@ public class UsageStatsService extends SystemService implements if (actualCallingUid != callingUid) { return false; } - } else if (!hasPermission(callingPackage)) { + } else if (!hasQueryPermission(callingPackage)) { return false; } final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( @@ -2454,7 +2466,7 @@ public class UsageStatsService extends SystemService implements final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); // If the calling app is asking about itself, continue, else check for permission. final boolean sameApp = packageUid == callingUid; - if (!sameApp && !hasPermission(callingPackage)) { + if (!sameApp && !hasQueryPermission(callingPackage)) { throw new SecurityException("Don't have permission to query app standby bucket"); } @@ -2502,7 +2514,7 @@ public class UsageStatsService extends SystemService implements } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } - if (!hasPermission(callingPackageName)) { + if (!hasQueryPermission(callingPackageName)) { throw new SecurityException( "Don't have permission to query app standby bucket"); } @@ -2556,7 +2568,7 @@ public class UsageStatsService extends SystemService implements final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); // If the calling app is asking about itself, continue, else check for permission. if (packageUid != callingUid) { - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { throw new SecurityException( "Don't have permission to query min app standby bucket"); } @@ -2900,7 +2912,7 @@ public class UsageStatsService extends SystemService implements if (!hasPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS)) { throw new SecurityException("Caller doesn't have INTERACT_ACROSS_USERS permission"); } - if (!hasPermission(callingPackage)) { + if (!hasQueryPermission(callingPackage)) { throw new SecurityException("Don't have permission to query usage stats"); } synchronized (mLock) { diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index ddb27969dd0a..9b67ab6d99c1 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -70,7 +70,7 @@ import java.util.Set; * in UsageStatsService. */ class UserUsageStatsService { - private static final String TAG = "UsageStatsService"; + private static final String TAG = UsageStatsService.TAG; private static final boolean DEBUG = UsageStatsService.DEBUG; private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final int sDateFormatFlags = @@ -535,10 +535,23 @@ class UserUsageStatsService { return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner, true); } - UsageEvents queryEvents(final long beginTime, final long endTime, int flags) { + UsageEvents queryEvents(final long beginTime, final long endTime, int flags, + int[] eventTypeFilter) { if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) { return null; } + + // Ensure valid event type filter. + final boolean isQueryForAllEvents = ArrayUtils.isEmpty(eventTypeFilter); + final boolean[] queryEventFilter = new boolean[Event.MAX_EVENT_TYPE + 1]; + if (!isQueryForAllEvents) { + for (int eventType : eventTypeFilter) { + if (eventType < Event.NONE || eventType > Event.MAX_EVENT_TYPE) { + throw new IllegalArgumentException("invalid event type: " + eventType); + } + queryEventFilter[eventType] = true; + } + } final ArraySet<String> names = new ArraySet<>(); List<Event> results = queryStats(INTERVAL_DAILY, beginTime, endTime, new StatCombiner<Event>() { @@ -547,6 +560,7 @@ class UserUsageStatsService { List<Event> accumulatedResult) { final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); + for (int i = startIndex; i < size; i++) { Event event = stats.events.get(i); if (event.mTimeStamp >= endTime) { @@ -554,6 +568,10 @@ class UserUsageStatsService { } final int eventType = event.mEventType; + if (!isQueryForAllEvents && !queryEventFilter[eventType]) { + continue; + } + if (eventType == Event.SHORTCUT_INVOCATION && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) { continue; |