1 /** 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.server.usage; 18 19 import android.Manifest; 20 import android.app.AppOpsManager; 21 import android.app.usage.ConfigurationStats; 22 import android.app.usage.IUsageStatsManager; 23 import android.app.usage.UsageEvents; 24 import android.app.usage.UsageStats; 25 import android.app.usage.UsageStatsManagerInternal; 26 import android.content.BroadcastReceiver; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ParceledListSlice; 33 import android.content.pm.UserInfo; 34 import android.content.res.Configuration; 35 import android.os.Binder; 36 import android.os.Environment; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.Process; 41 import android.os.RemoteException; 42 import android.os.SystemClock; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.util.ArraySet; 46 import android.util.Slog; 47 import android.util.SparseArray; 48 49 import com.android.internal.os.BackgroundThread; 50 import com.android.internal.util.IndentingPrintWriter; 51 import com.android.server.SystemService; 52 53 import java.io.File; 54 import java.io.FileDescriptor; 55 import java.io.PrintWriter; 56 import java.util.Arrays; 57 import java.util.List; 58 59 /** 60 * A service that collects, aggregates, and persists application usage data. 61 * This data can be queried by apps that have been granted permission by AppOps. 62 */ 63 public class UsageStatsService extends SystemService implements 64 UserUsageStatsService.StatsUpdatedListener { 65 static final String TAG = "UsageStatsService"; 66 67 static final boolean DEBUG = false; 68 private static final long TEN_SECONDS = 10 * 1000; 69 private static final long TWENTY_MINUTES = 20 * 60 * 1000; 70 private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES; 71 private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds. 72 73 // Handler message types. 74 static final int MSG_REPORT_EVENT = 0; 75 static final int MSG_FLUSH_TO_DISK = 1; 76 static final int MSG_REMOVE_USER = 2; 77 78 private final Object mLock = new Object(); 79 Handler mHandler; 80 AppOpsManager mAppOps; 81 UserManager mUserManager; 82 83 private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>(); 84 private File mUsageStatsDir; 85 long mRealTimeSnapshot; 86 long mSystemTimeSnapshot; 87 88 public UsageStatsService(Context context) { 89 super(context); 90 } 91 92 @Override 93 public void onStart() { 94 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); 95 mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 96 mHandler = new H(BackgroundThread.get().getLooper()); 97 98 File systemDataDir = new File(Environment.getDataDirectory(), "system"); 99 mUsageStatsDir = new File(systemDataDir, "usagestats"); 100 mUsageStatsDir.mkdirs(); 101 if (!mUsageStatsDir.exists()) { 102 throw new IllegalStateException("Usage stats directory does not exist: " 103 + mUsageStatsDir.getAbsolutePath()); 104 } 105 106 getContext().registerReceiver(new UserRemovedReceiver(), 107 new IntentFilter(Intent.ACTION_USER_REMOVED)); 108 109 synchronized (mLock) { 110 cleanUpRemovedUsersLocked(); 111 } 112 113 mRealTimeSnapshot = SystemClock.elapsedRealtime(); 114 mSystemTimeSnapshot = System.currentTimeMillis(); 115 116 publishLocalService(UsageStatsManagerInternal.class, new LocalService()); 117 publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService()); 118 } 119 120 private class UserRemovedReceiver extends BroadcastReceiver { 121 122 @Override 123 public void onReceive(Context context, Intent intent) { 124 if (intent != null && intent.getAction().equals(Intent.ACTION_USER_REMOVED)) { 125 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 126 if (userId >= 0) { 127 mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget(); 128 } 129 } 130 } 131 } 132 133 @Override 134 public void onStatsUpdated() { 135 mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL); 136 } 137 138 private void cleanUpRemovedUsersLocked() { 139 final List<UserInfo> users = mUserManager.getUsers(true); 140 if (users == null || users.size() == 0) { 141 throw new IllegalStateException("There can't be no users"); 142 } 143 144 ArraySet<String> toDelete = new ArraySet<>(); 145 String[] fileNames = mUsageStatsDir.list(); 146 if (fileNames == null) { 147 // No users to delete. 148 return; 149 } 150 151 toDelete.addAll(Arrays.asList(fileNames)); 152 153 final int userCount = users.size(); 154 for (int i = 0; i < userCount; i++) { 155 final UserInfo userInfo = users.get(i); 156 toDelete.remove(Integer.toString(userInfo.id)); 157 } 158 159 final int deleteCount = toDelete.size(); 160 for (int i = 0; i < deleteCount; i++) { 161 deleteRecursively(new File(mUsageStatsDir, toDelete.valueAt(i))); 162 } 163 } 164 165 private static void deleteRecursively(File f) { 166 File[] files = f.listFiles(); 167 if (files != null) { 168 for (File subFile : files) { 169 deleteRecursively(subFile); 170 } 171 } 172 173 if (!f.delete()) { 174 Slog.e(TAG, "Failed to delete " + f); 175 } 176 } 177 178 private UserUsageStatsService getUserDataAndInitializeIfNeededLocked(int userId, 179 long currentTimeMillis) { 180 UserUsageStatsService service = mUserState.get(userId); 181 if (service == null) { 182 service = new UserUsageStatsService(getContext(), userId, 183 new File(mUsageStatsDir, Integer.toString(userId)), this); 184 service.init(currentTimeMillis); 185 mUserState.put(userId, service); 186 } 187 return service; 188 } 189 190 /** 191 * This should be the only way to get the time from the system. 192 */ 193 private long checkAndGetTimeLocked() { 194 final long actualSystemTime = System.currentTimeMillis(); 195 final long actualRealtime = SystemClock.elapsedRealtime(); 196 final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot; 197 if (Math.abs(actualSystemTime - expectedSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) { 198 // The time has changed. 199 final int userCount = mUserState.size(); 200 for (int i = 0; i < userCount; i++) { 201 final UserUsageStatsService service = mUserState.valueAt(i); 202 service.onTimeChanged(expectedSystemTime, actualSystemTime); 203 } 204 mRealTimeSnapshot = actualRealtime; 205 mSystemTimeSnapshot = actualSystemTime; 206 } 207 return actualSystemTime; 208 } 209 210 /** 211 * Assuming the event's timestamp is measured in milliseconds since boot, 212 * convert it to a system wall time. 213 */ 214 private void convertToSystemTimeLocked(UsageEvents.Event event) { 215 event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot; 216 } 217 218 /** 219 * Called by the Binder stub 220 */ 221 void shutdown() { 222 synchronized (mLock) { 223 mHandler.removeMessages(MSG_REPORT_EVENT); 224 flushToDiskLocked(); 225 } 226 } 227 228 /** 229 * Called by the Binder stub. 230 */ 231 void reportEvent(UsageEvents.Event event, int userId) { 232 synchronized (mLock) { 233 final long timeNow = checkAndGetTimeLocked(); 234 convertToSystemTimeLocked(event); 235 236 final UserUsageStatsService service = 237 getUserDataAndInitializeIfNeededLocked(userId, timeNow); 238 service.reportEvent(event); 239 } 240 } 241 242 /** 243 * Called by the Binder stub. 244 */ 245 void flushToDisk() { 246 synchronized (mLock) { 247 flushToDiskLocked(); 248 } 249 } 250 251 /** 252 * Called by the Binder stub. 253 */ 254 void removeUser(int userId) { 255 synchronized (mLock) { 256 Slog.i(TAG, "Removing user " + userId + " and all data."); 257 mUserState.remove(userId); 258 cleanUpRemovedUsersLocked(); 259 } 260 } 261 262 /** 263 * Called by the Binder stub. 264 */ 265 List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) { 266 synchronized (mLock) { 267 final long timeNow = checkAndGetTimeLocked(); 268 if (!validRange(timeNow, beginTime, endTime)) { 269 return null; 270 } 271 272 final UserUsageStatsService service = 273 getUserDataAndInitializeIfNeededLocked(userId, timeNow); 274 return service.queryUsageStats(bucketType, beginTime, endTime); 275 } 276 } 277 278 /** 279 * Called by the Binder stub. 280 */ 281 List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime, 282 long endTime) { 283 synchronized (mLock) { 284 final long timeNow = checkAndGetTimeLocked(); 285 if (!validRange(timeNow, beginTime, endTime)) { 286 return null; 287 } 288 289 final UserUsageStatsService service = 290 getUserDataAndInitializeIfNeededLocked(userId, timeNow); 291 return service.queryConfigurationStats(bucketType, beginTime, endTime); 292 } 293 } 294 295 /** 296 * Called by the Binder stub. 297 */ 298 UsageEvents queryEvents(int userId, long beginTime, long endTime) { 299 synchronized (mLock) { 300 final long timeNow = checkAndGetTimeLocked(); 301 if (!validRange(timeNow, beginTime, endTime)) { 302 return null; 303 } 304 305 final UserUsageStatsService service = 306 getUserDataAndInitializeIfNeededLocked(userId, timeNow); 307 return service.queryEvents(beginTime, endTime); 308 } 309 } 310 311 private static boolean validRange(long currentTime, long beginTime, long endTime) { 312 return beginTime <= currentTime && beginTime < endTime; 313 } 314 315 private void flushToDiskLocked() { 316 final int userCount = mUserState.size(); 317 for (int i = 0; i < userCount; i++) { 318 UserUsageStatsService service = mUserState.valueAt(i); 319 service.persistActiveStats(); 320 } 321 322 mHandler.removeMessages(MSG_FLUSH_TO_DISK); 323 } 324 325 /** 326 * Called by the Binder stub. 327 */ 328 void dump(String[] args, PrintWriter pw) { 329 synchronized (mLock) { 330 IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); 331 ArraySet<String> argSet = new ArraySet<>(); 332 argSet.addAll(Arrays.asList(args)); 333 334 final int userCount = mUserState.size(); 335 for (int i = 0; i < userCount; i++) { 336 idpw.printPair("user", mUserState.keyAt(i)); 337 idpw.println(); 338 idpw.increaseIndent(); 339 if (argSet.contains("--checkin")) { 340 mUserState.valueAt(i).checkin(idpw); 341 } else { 342 mUserState.valueAt(i).dump(idpw); 343 } 344 idpw.decreaseIndent(); 345 } 346 } 347 } 348 349 class H extends Handler { 350 public H(Looper looper) { 351 super(looper); 352 } 353 354 @Override 355 public void handleMessage(Message msg) { 356 switch (msg.what) { 357 case MSG_REPORT_EVENT: 358 reportEvent((UsageEvents.Event) msg.obj, msg.arg1); 359 break; 360 361 case MSG_FLUSH_TO_DISK: 362 flushToDisk(); 363 break; 364 365 case MSG_REMOVE_USER: 366 removeUser(msg.arg1); 367 break; 368 369 default: 370 super.handleMessage(msg); 371 break; 372 } 373 } 374 } 375 376 private class BinderService extends IUsageStatsManager.Stub { 377 378 private boolean hasPermission(String callingPackage) { 379 final int callingUid = Binder.getCallingUid(); 380 if (callingUid == Process.SYSTEM_UID) { 381 return true; 382 } 383 final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, 384 callingUid, callingPackage); 385 if (mode == AppOpsManager.MODE_DEFAULT) { 386 // The default behavior here is to check if PackageManager has given the app 387 // permission. 388 return getContext().checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS) 389 == PackageManager.PERMISSION_GRANTED; 390 } 391 return mode == AppOpsManager.MODE_ALLOWED; 392 } 393 394 @Override 395 public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime, 396 long endTime, String callingPackage) { 397 if (!hasPermission(callingPackage)) { 398 return null; 399 } 400 401 final int userId = UserHandle.getCallingUserId(); 402 final long token = Binder.clearCallingIdentity(); 403 try { 404 final List<UsageStats> results = UsageStatsService.this.queryUsageStats( 405 userId, bucketType, beginTime, endTime); 406 if (results != null) { 407 return new ParceledListSlice<>(results); 408 } 409 } finally { 410 Binder.restoreCallingIdentity(token); 411 } 412 return null; 413 } 414 415 @Override 416 public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType, 417 long beginTime, long endTime, String callingPackage) throws RemoteException { 418 if (!hasPermission(callingPackage)) { 419 return null; 420 } 421 422 final int userId = UserHandle.getCallingUserId(); 423 final long token = Binder.clearCallingIdentity(); 424 try { 425 final List<ConfigurationStats> results = 426 UsageStatsService.this.queryConfigurationStats(userId, bucketType, 427 beginTime, endTime); 428 if (results != null) { 429 return new ParceledListSlice<>(results); 430 } 431 } finally { 432 Binder.restoreCallingIdentity(token); 433 } 434 return null; 435 } 436 437 @Override 438 public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) { 439 if (!hasPermission(callingPackage)) { 440 return null; 441 } 442 443 final int userId = UserHandle.getCallingUserId(); 444 final long token = Binder.clearCallingIdentity(); 445 try { 446 return UsageStatsService.this.queryEvents(userId, beginTime, endTime); 447 } finally { 448 Binder.restoreCallingIdentity(token); 449 } 450 } 451 452 @Override 453 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 454 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 455 != PackageManager.PERMISSION_GRANTED) { 456 pw.println("Permission Denial: can't dump UsageStats from pid=" 457 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 458 + " without permission " + android.Manifest.permission.DUMP); 459 return; 460 } 461 UsageStatsService.this.dump(args, pw); 462 } 463 } 464 465 /** 466 * This local service implementation is primarily used by ActivityManagerService. 467 * ActivityManagerService will call these methods holding the 'am' lock, which means we 468 * shouldn't be doing any IO work or other long running tasks in these methods. 469 */ 470 private class LocalService extends UsageStatsManagerInternal { 471 472 @Override 473 public void reportEvent(ComponentName component, int userId, int eventType) { 474 if (component == null) { 475 Slog.w(TAG, "Event reported without a component name"); 476 return; 477 } 478 479 UsageEvents.Event event = new UsageEvents.Event(); 480 event.mPackage = component.getPackageName(); 481 event.mClass = component.getClassName(); 482 483 // This will later be converted to system time. 484 event.mTimeStamp = SystemClock.elapsedRealtime(); 485 486 event.mEventType = eventType; 487 mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); 488 } 489 490 @Override 491 public void reportConfigurationChange(Configuration config, int userId) { 492 if (config == null) { 493 Slog.w(TAG, "Configuration event reported with a null config"); 494 return; 495 } 496 497 UsageEvents.Event event = new UsageEvents.Event(); 498 event.mPackage = "android"; 499 500 // This will later be converted to system time. 501 event.mTimeStamp = SystemClock.elapsedRealtime(); 502 503 event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE; 504 event.mConfiguration = new Configuration(config); 505 mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); 506 } 507 508 @Override 509 public void prepareShutdown() { 510 // This method *WILL* do IO work, but we must block until it is finished or else 511 // we might not shutdown cleanly. This is ok to do with the 'am' lock held, because 512 // we are shutting down. 513 shutdown(); 514 } 515 } 516 } 517