summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Suprabh Shukla <suprabh@google.com> 2018-03-03 01:32:21 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-03-03 01:32:21 +0000
commitc0fe0626f6b9676ef1fde7c2f9a42f3922683b5d (patch)
treef7c0f24ceea01c4736be6292c8593526ab73d850
parentb93a014cc8ba9c87ad84bafcb4013d2bd83eedc5 (diff)
parent217ccda8ac8f38137882a0f9fefeac8e53dc4ed6 (diff)
Merge "An api to query usage events for the caller"
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl1
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java38
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java37
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java41
5 files changed, 114 insertions, 4 deletions
diff --git a/api/current.txt b/api/current.txt
index 50d601d52105..df2e5bc5bcf7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7445,6 +7445,7 @@ package android.app.usage {
method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
+ 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
field public static final int INTERVAL_DAILY = 0; // 0x0
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index e72b84d24802..fff1a00c585e 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -32,6 +32,7 @@ interface IUsageStatsManager {
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
+ UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage);
void setAppInactive(String packageName, boolean inactive, int userId);
boolean isAppInactive(String packageName, int userId);
void whitelistAppTemporarily(String packageName, long duration, int userId);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5a57b067f2c4..5f9fa43203da 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -52,10 +52,13 @@ import java.util.Map;
* </pre>
* A request for data in the middle of a time interval will include that interval.
* <p/>
- * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS.
- * However, declaring the permission implies intention to use the API and the user of the device
- * still needs to grant permission through the Settings application.
- * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS}
+ * <b>NOTE:</b> Most methods on this API require the permission
+ * android.permission.PACKAGE_USAGE_STATS. However, declaring the permission implies intention to
+ * use the API and the user of the device still needs to grant permission through the Settings
+ * application.
+ * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS}.
+ * Methods which only return the information for the calling package do not require this permission.
+ * E.g. {@link #getAppStandbyBucket()} and {@link #queryEventsForSelf(long, long)}.
*/
@SystemService(Context.USAGE_STATS_SERVICE)
public final class UsageStatsManager {
@@ -206,6 +209,8 @@ public final class UsageStatsManager {
* 2014 - com.example.charlie
* </pre>
*
+ * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
+ *
* @param intervalType The time interval by which the stats are aggregated.
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
@@ -235,6 +240,7 @@ public final class UsageStatsManager {
* Gets the hardware configurations the device was in for the given time range, aggregated by
* the specified interval. The results are ordered as in
* {@link #queryUsageStats(int, long, long)}.
+ * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
*
* @param intervalType The time interval by which the stats are aggregated.
* @param beginTime The inclusive beginning of the range of stats to include in the results.
@@ -259,6 +265,7 @@ public final class UsageStatsManager {
/**
* Query for events in the given time range. Events are only kept by the system for a few
* days.
+ * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
*
* @param beginTime The inclusive beginning of the range of events to include in the results.
* @param endTime The exclusive end of the range of events to include in the results.
@@ -278,9 +285,32 @@ public final class UsageStatsManager {
}
/**
+ * Like {@link #queryEvents(long, long)}, but only returns events for the calling package.
+ *
+ * @param beginTime The inclusive beginning of the range of events to include in the results.
+ * @param endTime The exclusive end of the range of events to include in the results.
+ * @return A {@link UsageEvents} object.
+ *
+ * @see #queryEvents(long, long)
+ */
+ public UsageEvents queryEventsForSelf(long beginTime, long endTime) {
+ try {
+ final UsageEvents events = mService.queryEventsForPackage(beginTime, endTime,
+ mContext.getOpPackageName());
+ if (events != null) {
+ return events;
+ }
+ } catch (RemoteException e) {
+ // fallthrough
+ }
+ return sEmptyResults;
+ }
+
+ /**
* A convenience method that queries for all stats in the given range (using the best interval
* for that range), merges the resulting data, and keys it by package name.
* See {@link #queryUsageStats(int, long, long)}.
+ * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
*
* @param beginTime The inclusive beginning of the range of stats to include in the results.
* @param endTime The exclusive end of the range of stats to include in the results.
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 69b2c63efc18..a30257890c3c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -465,6 +465,23 @@ public class UsageStatsService extends SystemService implements
}
}
+ /**
+ * Called by the Binder stub.
+ */
+ UsageEvents queryEventsForPackage(int userId, long beginTime, long endTime,
+ String packageName) {
+ synchronized (mLock) {
+ final long timeNow = checkAndGetTimeLocked();
+ if (!validRange(timeNow, beginTime, endTime)) {
+ return null;
+ }
+
+ final UserUsageStatsService service =
+ getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ return service.queryEventsForPackage(beginTime, endTime, packageName);
+ }
+ }
+
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
@@ -661,6 +678,26 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public UsageEvents queryEventsForPackage(long beginTime, long endTime,
+ String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ if (mPackageManagerInternal.getPackageUid(callingPackage, PackageManager.MATCH_ANY_USER,
+ callingUserId) != callingUid) {
+ throw new SecurityException("Calling uid " + callingPackage + " cannot query events"
+ + "for package " + callingPackage);
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return UsageStatsService.this.queryEventsForPackage(callingUserId, beginTime,
+ endTime, callingPackage);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public boolean isAppInactive(String packageName, int userId) {
try {
userId = ActivityManager.getService().handleIncomingUser(Binder.getCallingPid(),
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 7f060b3093e9..29c5ee862aaf 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -355,6 +355,47 @@ class UserUsageStatsService {
return new UsageEvents(results, table);
}
+ UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
+ final String packageName) {
+ final ArraySet<String> names = new ArraySet<>();
+ names.add(packageName);
+ final List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+ beginTime, endTime, (stats, mutable, accumulatedResult) -> {
+ if (stats.events == null) {
+ return;
+ }
+
+ final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
+ if (startIndex < 0) {
+ return;
+ }
+
+ final int size = stats.events.size();
+ for (int i = startIndex; i < size; i++) {
+ if (stats.events.keyAt(i) >= endTime) {
+ return;
+ }
+
+ final UsageEvents.Event event = stats.events.valueAt(i);
+ if (!packageName.equals(event.mPackage)) {
+ continue;
+ }
+ if (event.mClass != null) {
+ names.add(event.mClass);
+ }
+ accumulatedResult.add(event);
+ }
+ });
+
+ if (results == null || results.isEmpty()) {
+ return null;
+ }
+
+ final String[] table = names.toArray(new String[names.size()]);
+ Arrays.sort(table);
+ return new UsageEvents(results, table);
+ }
+
void persistActiveStats() {
if (mStatsChanged) {
Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");