Create package-level last time usage stats for app hibernation
Create a user-agnostic package-level last time used usage stats for app
hibernation. Usage is updated when USER_INTERACTION or
APP_COMPONENT_USED event reported. Also moved checkAndGetTimeLocked and
convertToSystemTimeLocked methods up to UsageStatsService class and
refactored usages.
Bug: 183142974
Test: atest CtsUsageStatsTestCases:UsageStatsTest
Test: atest UsageStatsServiceTest
Test: atest UserUsageStatsServiceTest
Change-Id: I93d8653bb92b98af8f6b57156eb1139fbd7e562a
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 92c21f3..86d9255 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1835,6 +1835,7 @@
public final class UsageStatsManager {
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
+ method @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long getLastTimeAnyComponentUsed(@NonNull String);
method public int getUsageSource();
method @RequiresPermission(android.Manifest.permission.BIND_CARRIER_SERVICES) public void onCarrierPrivilegedAppsChanged();
method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 30ea5c4..2a50e0d 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -67,4 +67,5 @@
void reportUsageStop(in IBinder activity, String token, String callingPackage);
int getUsageSource();
void forceUsageSourceSettingRead();
+ long getLastTimeAnyComponentUsed(String packageName);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 1db7e9d..067c212 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1250,4 +1250,33 @@
} catch (RemoteException re) {
}
}
+
+ /**
+ * Get the last time a package is used by any users including explicit user interaction and
+ * component usage, measured in milliseconds since the epoch and truncated to the boundary of
+ * last day before the exact time. For packages that are never used, the time will be the epoch.
+ * <p> Note that this usage stats is user-agnostic. </p>
+ * <p>
+ * Also note that component usage is only reported for component bindings (e.g. broadcast
+ * receiver, service, content provider) and only when such a binding would cause an app to leave
+ * the stopped state.
+ * See {@link UsageEvents.Event.USER_INTERACTION}, {@link UsageEvents.Event.APP_COMPONENT_USED}.
+ * </p>
+ *
+ * @param packageName The name of the package to be queried.
+ * @return last time the queried package is used since the epoch.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.PACKAGE_USAGE_STATS})
+ public long getLastTimeAnyComponentUsed(@NonNull String packageName) {
+ // TODO(b/183462940): This usage data is not persisted to disk yet.
+ try {
+ return mService.getLastTimeAnyComponentUsed(packageName);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
index d786a5d..24c58f4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
@@ -106,7 +106,9 @@
@Test
public void testReportEvent_packageUsedEventNotTracked() {
- Event event = new Event(APP_COMPONENT_USED, SystemClock.elapsedRealtime());
+ // For APP_COMPONENT_USED event, the time stamp should have been converted to current time
+ // before reported here.
+ Event event = new Event(APP_COMPONENT_USED, System.currentTimeMillis());
event.mPackage = TEST_PACKAGE_NAME;
mService.reportEvent(event);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0510b9c..52f9fe5 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -75,6 +75,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
@@ -109,8 +110,10 @@
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.TimeUnit;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -166,6 +169,11 @@
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
int mUsageSource;
+ private long mRealTimeSnapshot;
+ private long mSystemTimeSnapshot;
+ // A map storing last time global usage of packages, measured in milliseconds since the epoch.
+ private final Map<String, Long> mLastTimeComponentUsedGlobal = new ArrayMap<>();
+
/** Manages the standby state of apps. */
AppStandbyInternal mAppStandby;
@@ -279,6 +287,9 @@
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
null, mHandler);
+ mRealTimeSnapshot = SystemClock.elapsedRealtime();
+ mSystemTimeSnapshot = System.currentTimeMillis();
+
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
publishLocalService(AppStandbyInternal.class, mAppStandby);
publishBinderServices();
@@ -804,6 +815,28 @@
}
/**
+ * Assuming the event's timestamp is measured in milliseconds since boot,
+ * convert it to a system wall time. System and real time snapshots are updated before
+ * conversion.
+ */
+ private void convertToSystemTimeLocked(Event event) {
+ final long actualSystemTime = System.currentTimeMillis();
+ if (ENABLE_TIME_CHANGE_CORRECTION) {
+ final long actualRealtime = SystemClock.elapsedRealtime();
+ final long expectedSystemTime =
+ (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
+ final long diffSystemTime = actualSystemTime - expectedSystemTime;
+ if (Math.abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
+ // The time has changed.
+ Slog.i(TAG, "Time changed in by " + (diffSystemTime / 1000) + " seconds");
+ mRealTimeSnapshot = actualRealtime;
+ mSystemTimeSnapshot = actualSystemTime;
+ }
+ }
+ event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
+ }
+
+ /**
* Called by the Binder stub.
*/
void reportEvent(Event event, int userId) {
@@ -940,6 +973,12 @@
Slog.w(TAG, "Failed to note usage stop", iae);
}
break;
+ case Event.USER_INTERACTION:
+ // Fall through
+ case Event.APP_COMPONENT_USED:
+ convertToSystemTimeLocked(event);
+ mLastTimeComponentUsedGlobal.put(event.mPackage, event.mTimeStamp);
+ break;
}
final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
@@ -2094,6 +2133,16 @@
public void forceUsageSourceSettingRead() {
readUsageSourceSetting();
}
+
+ @Override
+ public long getLastTimeAnyComponentUsed(String packageName) {
+ synchronized (mLock) {
+ // Truncate the returned milliseconds to the boundary of the last day before exact
+ // time for privacy reasons.
+ return mLastTimeComponentUsedGlobal.getOrDefault(packageName, 0L)
+ / TimeUnit.DAYS.toMillis(1) * TimeUnit.DAYS.toMillis(1);
+ }
+ }
}
void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 22b4f4e..36d8c85 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -277,8 +277,11 @@
+ eventToString(event.mEventType));
}
- checkAndGetTimeLocked();
- convertToSystemTimeLocked(event);
+ if (event.mEventType != Event.USER_INTERACTION
+ && event.mEventType != Event.APP_COMPONENT_USED) {
+ checkAndGetTimeLocked();
+ convertToSystemTimeLocked(event);
+ }
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover