diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2d9c6687c0cdc..99d8d07d71d6d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1847,6 +1847,7 @@ package android.app.usage { 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 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 30ea5c4761912..2a50e0de21457 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -67,4 +67,5 @@ interface IUsageStatsManager { 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 1db7e9da0bcb7..067c212532d31 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -1250,4 +1250,33 @@ public final class UsageStatsManager { } 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. + *

Note that this usage stats is user-agnostic.

+ *

+ * 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}. + *

+ * + * @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 d786a5dac83ae..24c58f49bed61 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 @@ public class UserUsageStatsServiceTest { @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 0510b9cb54e12..52f9fe52a5fa5 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.SystemProperties; 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.Arrays; 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 @@ public class UsageStatsService extends SystemService implements 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 mLastTimeComponentUsedGlobal = new ArrayMap<>(); + /** Manages the standby state of apps. */ AppStandbyInternal mAppStandby; @@ -279,6 +287,9 @@ public class UsageStatsService extends SystemService implements 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(); @@ -803,6 +814,28 @@ public class UsageStatsService extends SystemService implements } } + /** + * 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. */ @@ -940,6 +973,12 @@ public class UsageStatsService extends SystemService implements 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 class UsageStatsService extends SystemService implements 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 22b4f4ef57e5b..36d8c857ca21a 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 @@ class UserUsageStatsService { + 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