Home | History | Annotate | Download | only in usage
      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