Update UsageStats#checkAndGetTimeLocked behavior.

The time change event was happening for every user even if they were not
unlocked, causing UsageStatsService to crash. Since the data is now
located in CE, the #checkAndGetTimeLocked function has been moved to
UserUsageStatsService, tying time change events to each user instead of
UsageStatsService. This gives the guarantee that no usage stats data
will be accessed or modified if the user service is not existent while
keeping the same behavior as earlier.

References to #checkAndGetTimeLocked in UsageStatsService have been
updated to call System.currentTimeMillis() directly.

Bug: 140529704
Test: atest UsageStatsTest
Test: atest UsageStatsDatabaseTest
Change-Id: I746790f3918b927ac8ac76ee860c94cac603e5da
This commit is contained in:
Varun Shah
2019-09-05 11:50:18 -07:00
parent 3d8dd77fe5
commit 5e1613ef64
2 changed files with 70 additions and 79 deletions

View File

@@ -121,7 +121,7 @@ public class UsageStatsService extends SystemService implements
private static final long TEN_SECONDS = 10 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
private static final boolean ENABLE_KERNEL_UPDATES = true;
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
@@ -156,8 +156,6 @@ public class UsageStatsService extends SystemService implements
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
private final SparseBooleanArray mUserUnlockedStates = new SparseBooleanArray();
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
int mUsageSource;
/** Manages the standby state of apps. */
@@ -253,9 +251,6 @@ 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());
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
}
@@ -345,7 +340,7 @@ public class UsageStatsService extends SystemService implements
mUserUnlockedStates.put(userId, true);
final UserUsageStatsService userService = getUserDataAndInitializeIfNeededLocked(
userId, System.currentTimeMillis());
userService.userUnlocked(checkAndGetTimeLocked());
userService.userUnlocked(System.currentTimeMillis());
// Process all the pending reported events
while (pendingEvents.peek() != null) {
reportEvent(pendingEvents.poll(), userId);
@@ -568,37 +563,6 @@ public class UsageStatsService extends SystemService implements
}
}
/**
* This should be the only way to get the time from the system.
*/
private long checkAndGetTimeLocked() {
final long actualSystemTime = System.currentTimeMillis();
final long actualRealtime = SystemClock.elapsedRealtime();
final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
final long diffSystemTime = actualSystemTime - expectedSystemTime;
if (Math.abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS
&& ENABLE_TIME_CHANGE_CORRECTION) {
// The time has changed.
Slog.i(TAG, "Time changed in UsageStats by " + (diffSystemTime / 1000) + " seconds");
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
final UserUsageStatsService service = mUserState.valueAt(i);
service.onTimeChanged(expectedSystemTime, actualSystemTime);
}
mRealTimeSnapshot = actualRealtime;
mSystemTimeSnapshot = actualSystemTime;
}
return actualSystemTime;
}
/**
* Assuming the event's timestamp is measured in milliseconds since boot,
* convert it to a system wall time.
*/
private void convertToSystemTimeLocked(Event event) {
event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
}
/**
* Called by the Binder stub
*/
@@ -718,9 +682,8 @@ public class UsageStatsService extends SystemService implements
return;
}
final long timeNow = checkAndGetTimeLocked();
final long timeNow = System.currentTimeMillis();
final long elapsedRealtime = SystemClock.elapsedRealtime();
convertToSystemTimeLocked(event);
if (event.mPackage != null
&& mPackageManagerInternal.isPackageEphemeral(userId, event.mPackage)) {
@@ -876,13 +839,8 @@ public class UsageStatsService extends SystemService implements
return null;
}
final long timeNow = checkAndGetTimeLocked();
if (!validRange(timeNow, beginTime, endTime)) {
return null;
}
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
List<UsageStats> list = service.queryUsageStats(bucketType, beginTime, endTime);
if (list == null) {
return null;
@@ -913,13 +871,8 @@ public class UsageStatsService extends SystemService implements
return null;
}
final long timeNow = checkAndGetTimeLocked();
if (!validRange(timeNow, beginTime, endTime)) {
return null;
}
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
return service.queryConfigurationStats(bucketType, beginTime, endTime);
}
}
@@ -935,13 +888,8 @@ public class UsageStatsService extends SystemService implements
return null;
}
final long timeNow = checkAndGetTimeLocked();
if (!validRange(timeNow, beginTime, endTime)) {
return null;
}
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
return service.queryEventStats(bucketType, beginTime, endTime);
}
}
@@ -957,13 +905,8 @@ public class UsageStatsService extends SystemService implements
return null;
}
final long timeNow = checkAndGetTimeLocked();
if (!validRange(timeNow, beginTime, endTime)) {
return null;
}
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps);
}
}
@@ -979,21 +922,12 @@ public class UsageStatsService extends SystemService implements
return null;
}
final long timeNow = checkAndGetTimeLocked();
if (!validRange(timeNow, beginTime, endTime)) {
return null;
}
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
return service.queryEventsForPackage(beginTime, endTime, packageName, includeTaskRoot);
}
}
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
private String buildFullToken(String packageName, String token) {
final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
sb.append(packageName);
@@ -2050,8 +1984,8 @@ public class UsageStatsService extends SystemService implements
// Check to ensure that only user 0's data is b/r for now
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
user, System.currentTimeMillis());
return userStats.getBackupPayload(key);
} else {
return null;
@@ -2068,8 +2002,8 @@ public class UsageStatsService extends SystemService implements
}
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
user, System.currentTimeMillis());
userStats.applyRestoredPayload(key, payload);
}
}

View File

@@ -76,6 +76,8 @@ class UserUsageStatsService {
private final String mLogPrefix;
private String mLastBackgroundedPackage;
private final int mUserId;
private long mRealTimeSnapshot;
private long mSystemTimeSnapshot;
private static final long[] INTERVAL_LENGTH = new long[] {
UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
@@ -101,6 +103,8 @@ class UserUsageStatsService {
mListener = listener;
mLogPrefix = "User[" + Integer.toString(userId) + "] ";
mUserId = userId;
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
}
void init(final long currentTimeMillis) {
@@ -165,12 +169,41 @@ class UserUsageStatsService {
persistActiveStats();
}
void onTimeChanged(long oldTime, long newTime) {
private void onTimeChanged(long oldTime, long newTime) {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
loadActiveStats(newTime);
}
/**
* This should be the only way to get the time from the system.
*/
private long checkAndGetTimeLocked() {
final long actualSystemTime = System.currentTimeMillis();
if (!UsageStatsService.ENABLE_TIME_CHANGE_CORRECTION) {
return actualSystemTime;
}
final long actualRealtime = SystemClock.elapsedRealtime();
final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
final long diffSystemTime = actualSystemTime - expectedSystemTime;
if (Math.abs(diffSystemTime) > UsageStatsService.TIME_CHANGE_THRESHOLD_MILLIS) {
// The time has changed.
Slog.i(TAG, mLogPrefix + "Time changed in by " + (diffSystemTime / 1000) + " seconds");
onTimeChanged(expectedSystemTime, actualSystemTime);
mRealTimeSnapshot = actualRealtime;
mSystemTimeSnapshot = actualSystemTime;
}
return actualSystemTime;
}
/**
* Assuming the event's timestamp is measured in milliseconds since boot,
* convert it to a system wall time.
*/
private void convertToSystemTimeLocked(Event event) {
event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
}
void reportEvent(Event event) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
@@ -178,6 +211,9 @@ class UserUsageStatsService {
+ eventToString(event.mEventType));
}
checkAndGetTimeLocked();
convertToSystemTimeLocked(event);
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats(event.mTimeStamp);
@@ -298,6 +334,10 @@ class UserUsageStatsService {
}
};
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
/**
* Generic query method that selects the appropriate IntervalStats for the specified time range
* and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
@@ -370,19 +410,31 @@ class UserUsageStatsService {
}
List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
}
List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
}
List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
}
UsageEvents queryEvents(final long beginTime, final long endTime,
boolean obfuscateInstantApps) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
final ArraySet<String> names = new ArraySet<>();
List<Event> results = queryStats(INTERVAL_DAILY,
beginTime, endTime, new StatCombiner<Event>() {
@@ -428,6 +480,9 @@ class UserUsageStatsService {
UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
final String packageName, boolean includeTaskRoot) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
final ArraySet<String> names = new ArraySet<>();
names.add(packageName);
final List<Event> results = queryStats(INTERVAL_DAILY,
@@ -1030,10 +1085,12 @@ class UserUsageStatsService {
}
byte[] getBackupPayload(String key){
checkAndGetTimeLocked();
return mDatabase.getBackupPayload(key);
}
void applyRestoredPayload(String key, byte[] payload){
checkAndGetTimeLocked();
mDatabase.applyRestoredPayload(key, payload);
}
}