UsageStatsService.java revision f47e51ec605fccf7fed9e50d1adc98fbd4e8b340
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
17package com.android.server.usage;
18
19import android.Manifest;
20import android.app.ActivityManagerNative;
21import android.app.AppGlobals;
22import android.app.AppOpsManager;
23import android.app.admin.DevicePolicyManager;
24import android.app.usage.ConfigurationStats;
25import android.app.usage.IUsageStatsManager;
26import android.app.usage.UsageEvents;
27import android.app.usage.UsageEvents.Event;
28import android.app.usage.UsageStats;
29import android.app.usage.UsageStatsManagerInternal;
30import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
31import android.appwidget.AppWidgetManager;
32import android.content.BroadcastReceiver;
33import android.content.ComponentName;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.pm.PackageInfo;
38import android.content.pm.PackageManager;
39import android.content.pm.ParceledListSlice;
40import android.content.pm.UserInfo;
41import android.content.res.Configuration;
42import android.database.ContentObserver;
43import android.net.Uri;
44import android.os.Binder;
45import android.os.Environment;
46import android.os.Handler;
47import android.os.Looper;
48import android.os.Message;
49import android.os.Process;
50import android.os.RemoteException;
51import android.os.SystemClock;
52import android.os.UserHandle;
53import android.os.UserManager;
54import android.provider.Settings;
55import android.util.ArraySet;
56import android.util.Slog;
57import android.util.SparseArray;
58
59import com.android.internal.os.BackgroundThread;
60import com.android.internal.util.IndentingPrintWriter;
61import com.android.server.SystemConfig;
62import com.android.server.SystemService;
63
64import java.io.File;
65import java.io.FileDescriptor;
66import java.io.PrintWriter;
67import java.util.ArrayList;
68import java.util.Arrays;
69import java.util.List;
70
71/**
72 * A service that collects, aggregates, and persists application usage data.
73 * This data can be queried by apps that have been granted permission by AppOps.
74 */
75public class UsageStatsService extends SystemService implements
76        UserUsageStatsService.StatsUpdatedListener {
77    static final String TAG = "UsageStatsService";
78
79    static final boolean DEBUG = false;
80    private static final long TEN_SECONDS = 10 * 1000;
81    private static final long TWENTY_MINUTES = 20 * 60 * 1000;
82    private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
83    private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
84
85    // Handler message types.
86    static final int MSG_REPORT_EVENT = 0;
87    static final int MSG_FLUSH_TO_DISK = 1;
88    static final int MSG_REMOVE_USER = 2;
89    static final int MSG_INFORM_LISTENERS = 3;
90    static final int MSG_RESET_LAST_TIMESTAMP = 4;
91
92    private final Object mLock = new Object();
93    Handler mHandler;
94    AppOpsManager mAppOps;
95    UserManager mUserManager;
96
97    private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
98    private File mUsageStatsDir;
99    long mRealTimeSnapshot;
100    long mSystemTimeSnapshot;
101
102    private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 1L * 24 * 60 * 60 * 1000; // 1 day
103    private long mAppIdleDurationMillis;
104
105    private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
106            mPackageAccessListeners = new ArrayList<>();
107
108    public UsageStatsService(Context context) {
109        super(context);
110    }
111
112    @Override
113    public void onStart() {
114        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
115        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
116        mHandler = new H(BackgroundThread.get().getLooper());
117
118        File systemDataDir = new File(Environment.getDataDirectory(), "system");
119        mUsageStatsDir = new File(systemDataDir, "usagestats");
120        mUsageStatsDir.mkdirs();
121        if (!mUsageStatsDir.exists()) {
122            throw new IllegalStateException("Usage stats directory does not exist: "
123                    + mUsageStatsDir.getAbsolutePath());
124        }
125
126        getContext().registerReceiver(new UserRemovedReceiver(),
127                new IntentFilter(Intent.ACTION_USER_REMOVED));
128
129        synchronized (mLock) {
130            cleanUpRemovedUsersLocked();
131        }
132
133        mRealTimeSnapshot = SystemClock.elapsedRealtime();
134        mSystemTimeSnapshot = System.currentTimeMillis();
135        // Look at primary user's secure setting for this. TODO: Maybe apply different
136        // thresholds for different users.
137        mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
138                Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
139                UserHandle.USER_OWNER);
140
141        publishLocalService(UsageStatsManagerInternal.class, new LocalService());
142        publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
143    }
144
145    @Override
146    public void onBootPhase(int phase) {
147        if (phase == PHASE_SYSTEM_SERVICES_READY) {
148            // Observe changes to the threshold
149            new SettingsObserver(mHandler).registerObserver();
150        }
151    }
152
153    private class UserRemovedReceiver extends BroadcastReceiver {
154
155        @Override
156        public void onReceive(Context context, Intent intent) {
157            if (intent != null && intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
158                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
159                if (userId >= 0) {
160                    mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
161                }
162            }
163        }
164    }
165
166    @Override
167    public void onStatsUpdated() {
168        mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
169    }
170
171    private void cleanUpRemovedUsersLocked() {
172        final List<UserInfo> users = mUserManager.getUsers(true);
173        if (users == null || users.size() == 0) {
174            throw new IllegalStateException("There can't be no users");
175        }
176
177        ArraySet<String> toDelete = new ArraySet<>();
178        String[] fileNames = mUsageStatsDir.list();
179        if (fileNames == null) {
180            // No users to delete.
181            return;
182        }
183
184        toDelete.addAll(Arrays.asList(fileNames));
185
186        final int userCount = users.size();
187        for (int i = 0; i < userCount; i++) {
188            final UserInfo userInfo = users.get(i);
189            toDelete.remove(Integer.toString(userInfo.id));
190        }
191
192        final int deleteCount = toDelete.size();
193        for (int i = 0; i < deleteCount; i++) {
194            deleteRecursively(new File(mUsageStatsDir, toDelete.valueAt(i)));
195        }
196    }
197
198    private static void deleteRecursively(File f) {
199        File[] files = f.listFiles();
200        if (files != null) {
201            for (File subFile : files) {
202                deleteRecursively(subFile);
203            }
204        }
205
206        if (!f.delete()) {
207            Slog.e(TAG, "Failed to delete " + f);
208        }
209    }
210
211    private UserUsageStatsService getUserDataAndInitializeIfNeededLocked(int userId,
212            long currentTimeMillis) {
213        UserUsageStatsService service = mUserState.get(userId);
214        if (service == null) {
215            service = new UserUsageStatsService(getContext(), userId,
216                    new File(mUsageStatsDir, Integer.toString(userId)), this);
217            service.init(currentTimeMillis);
218            mUserState.put(userId, service);
219        }
220        return service;
221    }
222
223    /**
224     * This should be the only way to get the time from the system.
225     */
226    private long checkAndGetTimeLocked() {
227        final long actualSystemTime = System.currentTimeMillis();
228        final long actualRealtime = SystemClock.elapsedRealtime();
229        final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
230        if (Math.abs(actualSystemTime - expectedSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
231            // The time has changed.
232            final int userCount = mUserState.size();
233            for (int i = 0; i < userCount; i++) {
234                final UserUsageStatsService service = mUserState.valueAt(i);
235                service.onTimeChanged(expectedSystemTime, actualSystemTime);
236            }
237            mRealTimeSnapshot = actualRealtime;
238            mSystemTimeSnapshot = actualSystemTime;
239        }
240        return actualSystemTime;
241    }
242
243    /**
244     * Assuming the event's timestamp is measured in milliseconds since boot,
245     * convert it to a system wall time.
246     */
247    private void convertToSystemTimeLocked(UsageEvents.Event event) {
248        event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
249    }
250
251    /**
252     * Called by the Binder stub
253     */
254    void shutdown() {
255        synchronized (mLock) {
256            mHandler.removeMessages(MSG_REPORT_EVENT);
257            flushToDiskLocked();
258        }
259    }
260
261    /**
262     * Called by the Binder stub.
263     */
264    void reportEvent(UsageEvents.Event event, int userId) {
265        synchronized (mLock) {
266            final long timeNow = checkAndGetTimeLocked();
267            convertToSystemTimeLocked(event);
268
269            final UserUsageStatsService service =
270                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
271            final long lastUsed = service.getLastPackageAccessTime(event.mPackage);
272            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
273            service.reportEvent(event);
274            // Inform listeners if necessary
275            if ((event.mEventType == Event.MOVE_TO_FOREGROUND
276                    || event.mEventType == Event.MOVE_TO_BACKGROUND
277                    || event.mEventType == Event.INTERACTION)) {
278                if (previouslyIdle) {
279                    // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
280                    mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
281                            /* idle = */ 0, event.mPackage));
282                }
283            }
284        }
285    }
286
287    /**
288     * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the
289     * last used timestamp to a point in time thats behind the threshold for idle.
290     */
291    void resetLastTimestamp(String packageName, int userId, boolean idle) {
292        synchronized (mLock) {
293            final long timeNow = checkAndGetTimeLocked();
294            final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0);
295
296            final UserUsageStatsService service =
297                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
298            final long lastUsed = service.getLastPackageAccessTime(packageName);
299            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
300            service.setLastTimestamp(packageName, lastTimestamp);
301            // Inform listeners if necessary
302            if (previouslyIdle != idle) {
303                // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
304                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
305                        /* idle = */ idle ? 1 : 0, packageName));
306            }
307        }
308    }
309
310    /**
311     * Called by the Binder stub.
312     */
313    void flushToDisk() {
314        synchronized (mLock) {
315            flushToDiskLocked();
316        }
317    }
318
319    /**
320     * Called by the Binder stub.
321     */
322    void removeUser(int userId) {
323        synchronized (mLock) {
324            Slog.i(TAG, "Removing user " + userId + " and all data.");
325            mUserState.remove(userId);
326            cleanUpRemovedUsersLocked();
327        }
328    }
329
330    /**
331     * Called by the Binder stub.
332     */
333    List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) {
334        synchronized (mLock) {
335            final long timeNow = checkAndGetTimeLocked();
336            if (!validRange(timeNow, beginTime, endTime)) {
337                return null;
338            }
339
340            final UserUsageStatsService service =
341                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
342            return service.queryUsageStats(bucketType, beginTime, endTime);
343        }
344    }
345
346    /**
347     * Called by the Binder stub.
348     */
349    List<ConfigurationStats> queryConfigurationStats(int userId, int bucketType, long beginTime,
350            long endTime) {
351        synchronized (mLock) {
352            final long timeNow = checkAndGetTimeLocked();
353            if (!validRange(timeNow, beginTime, endTime)) {
354                return null;
355            }
356
357            final UserUsageStatsService service =
358                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
359            return service.queryConfigurationStats(bucketType, beginTime, endTime);
360        }
361    }
362
363    /**
364     * Called by the Binder stub.
365     */
366    UsageEvents queryEvents(int userId, long beginTime, long endTime) {
367        synchronized (mLock) {
368            final long timeNow = checkAndGetTimeLocked();
369            if (!validRange(timeNow, beginTime, endTime)) {
370                return null;
371            }
372
373            final UserUsageStatsService service =
374                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
375            return service.queryEvents(beginTime, endTime);
376        }
377    }
378
379    /**
380     * Called by LocalService stub.
381     */
382    long getLastPackageAccessTime(String packageName, int userId) {
383        synchronized (mLock) {
384            final long timeNow = checkAndGetTimeLocked();
385            // android package is always considered non-idle.
386            // TODO: Add a generic whitelisting mechanism
387            if (packageName.equals("android")) {
388                return timeNow;
389            }
390            final UserUsageStatsService service =
391                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
392            return service.getLastPackageAccessTime(packageName);
393        }
394    }
395
396    void addListener(AppIdleStateChangeListener listener) {
397        synchronized (mLock) {
398            if (!mPackageAccessListeners.contains(listener)) {
399                mPackageAccessListeners.add(listener);
400            }
401        }
402    }
403
404    void removeListener(AppIdleStateChangeListener listener) {
405        synchronized (mLock) {
406            mPackageAccessListeners.remove(listener);
407        }
408    }
409
410    private boolean hasPassedIdleDuration(long lastUsed) {
411        final long now = System.currentTimeMillis();
412        return lastUsed < now - mAppIdleDurationMillis;
413    }
414
415    boolean isAppIdle(String packageName, int userId) {
416        if (packageName == null) return false;
417        if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
418            return false;
419        }
420        if (isActiveDeviceAdmin(packageName, userId)) {
421            return false;
422        }
423
424        final long lastUsed = getLastPackageAccessTime(packageName, userId);
425        return hasPassedIdleDuration(lastUsed);
426    }
427
428    void setAppIdle(String packageName, boolean idle, int userId) {
429        if (packageName == null) return;
430
431        mHandler.obtainMessage(MSG_RESET_LAST_TIMESTAMP, userId, idle ? 1 : 0, packageName)
432                .sendToTarget();
433    }
434
435    private boolean isActiveDeviceAdmin(String packageName, int userId) {
436        DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
437        if (dpm == null) return false;
438        List<ComponentName> components = dpm.getActiveAdminsAsUser(userId);
439        if (components == null) return false;
440        final int size = components.size();
441        for (int i = 0; i < size; i++) {
442            if (components.get(i).getPackageName().equals(packageName)) {
443                return true;
444            }
445        }
446        return false;
447    }
448
449    void informListeners(String packageName, int userId, boolean isIdle) {
450        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
451            listener.onAppIdleStateChanged(packageName, userId, isIdle);
452        }
453    }
454
455    private static boolean validRange(long currentTime, long beginTime, long endTime) {
456        return beginTime <= currentTime && beginTime < endTime;
457    }
458
459    private void flushToDiskLocked() {
460        final int userCount = mUserState.size();
461        for (int i = 0; i < userCount; i++) {
462            UserUsageStatsService service = mUserState.valueAt(i);
463            service.persistActiveStats();
464        }
465
466        mHandler.removeMessages(MSG_FLUSH_TO_DISK);
467    }
468
469    /**
470     * Called by the Binder stub.
471     */
472    void dump(String[] args, PrintWriter pw) {
473        synchronized (mLock) {
474            IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
475            ArraySet<String> argSet = new ArraySet<>();
476            argSet.addAll(Arrays.asList(args));
477
478            final int userCount = mUserState.size();
479            for (int i = 0; i < userCount; i++) {
480                idpw.printPair("user", mUserState.keyAt(i));
481                idpw.println();
482                idpw.increaseIndent();
483                if (argSet.contains("--checkin")) {
484                    mUserState.valueAt(i).checkin(idpw);
485                } else {
486                    mUserState.valueAt(i).dump(idpw);
487                }
488                idpw.decreaseIndent();
489            }
490        }
491    }
492
493    class H extends Handler {
494        public H(Looper looper) {
495            super(looper);
496        }
497
498        @Override
499        public void handleMessage(Message msg) {
500            switch (msg.what) {
501                case MSG_REPORT_EVENT:
502                    reportEvent((UsageEvents.Event) msg.obj, msg.arg1);
503                    break;
504
505                case MSG_FLUSH_TO_DISK:
506                    flushToDisk();
507                    break;
508
509                case MSG_REMOVE_USER:
510                    removeUser(msg.arg1);
511                    break;
512
513                case MSG_INFORM_LISTENERS:
514                    informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
515                    break;
516
517                case MSG_RESET_LAST_TIMESTAMP:
518                    resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
519                    break;
520
521                default:
522                    super.handleMessage(msg);
523                    break;
524            }
525        }
526    }
527
528    /**
529     * Observe settings changes for Settings.Secure.APP_IDLE_DURATION.
530     */
531    private class SettingsObserver extends ContentObserver {
532
533        SettingsObserver(Handler handler) {
534            super(handler);
535        }
536
537        void registerObserver() {
538            getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
539                    Settings.Secure.APP_IDLE_DURATION), false, this, UserHandle.USER_OWNER);
540        }
541
542        @Override
543        public void onChange(boolean selfChange, Uri uri, int userId) {
544            mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
545                    Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
546                    UserHandle.USER_OWNER);
547            // TODO: Check if we need to update idle states of all the apps
548        }
549    }
550
551    private class BinderService extends IUsageStatsManager.Stub {
552
553        private boolean hasPermission(String callingPackage) {
554            final int callingUid = Binder.getCallingUid();
555            if (callingUid == Process.SYSTEM_UID) {
556                return true;
557            }
558            final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
559                    callingUid, callingPackage);
560            if (mode == AppOpsManager.MODE_DEFAULT) {
561                // The default behavior here is to check if PackageManager has given the app
562                // permission.
563                return getContext().checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
564                        == PackageManager.PERMISSION_GRANTED;
565            }
566            return mode == AppOpsManager.MODE_ALLOWED;
567        }
568
569        @Override
570        public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
571                long endTime, String callingPackage) {
572            if (!hasPermission(callingPackage)) {
573                return null;
574            }
575
576            final int userId = UserHandle.getCallingUserId();
577            final long token = Binder.clearCallingIdentity();
578            try {
579                final List<UsageStats> results = UsageStatsService.this.queryUsageStats(
580                        userId, bucketType, beginTime, endTime);
581                if (results != null) {
582                    return new ParceledListSlice<>(results);
583                }
584            } finally {
585                Binder.restoreCallingIdentity(token);
586            }
587            return null;
588        }
589
590        @Override
591        public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
592                long beginTime, long endTime, String callingPackage) throws RemoteException {
593            if (!hasPermission(callingPackage)) {
594                return null;
595            }
596
597            final int userId = UserHandle.getCallingUserId();
598            final long token = Binder.clearCallingIdentity();
599            try {
600                final List<ConfigurationStats> results =
601                        UsageStatsService.this.queryConfigurationStats(userId, bucketType,
602                                beginTime, endTime);
603                if (results != null) {
604                    return new ParceledListSlice<>(results);
605                }
606            } finally {
607                Binder.restoreCallingIdentity(token);
608            }
609            return null;
610        }
611
612        @Override
613        public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
614            if (!hasPermission(callingPackage)) {
615                return null;
616            }
617
618            final int userId = UserHandle.getCallingUserId();
619            final long token = Binder.clearCallingIdentity();
620            try {
621                return UsageStatsService.this.queryEvents(userId, beginTime, endTime);
622            } finally {
623                Binder.restoreCallingIdentity(token);
624            }
625        }
626
627        @Override
628        public boolean isAppIdle(String packageName, int userId) {
629            try {
630                userId = ActivityManagerNative.getDefault().handleIncomingUser(Binder.getCallingPid(),
631                        Binder.getCallingUid(), userId, false, true, "isAppIdle", null);
632            } catch (RemoteException re) {
633                return false;
634            }
635            final long token = Binder.clearCallingIdentity();
636            try {
637                return UsageStatsService.this.isAppIdle(packageName, userId);
638            } finally {
639                Binder.restoreCallingIdentity(token);
640            }
641        }
642
643        @Override
644        public void setAppIdle(String packageName, boolean idle, int userId) {
645            final int callingUid = Binder.getCallingUid();
646            try {
647                userId = ActivityManagerNative.getDefault().handleIncomingUser(
648                        Binder.getCallingPid(), callingUid, userId, false, true,
649                        "setAppIdle", null);
650            } catch (RemoteException re) {
651                return;
652            }
653            getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
654                    "No permission to change app idle state");
655            final long token = Binder.clearCallingIdentity();
656            try {
657                PackageInfo pi = AppGlobals.getPackageManager()
658                        .getPackageInfo(packageName, 0, userId);
659                if (pi == null) return;
660                UsageStatsService.this.setAppIdle(packageName, idle, userId);
661            } catch (RemoteException re) {
662            } finally {
663                Binder.restoreCallingIdentity(token);
664            }
665        }
666
667        @Override
668        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
669            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
670                    != PackageManager.PERMISSION_GRANTED) {
671                pw.println("Permission Denial: can't dump UsageStats from pid="
672                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
673                        + " without permission " + android.Manifest.permission.DUMP);
674                return;
675            }
676            UsageStatsService.this.dump(args, pw);
677        }
678    }
679
680    /**
681     * This local service implementation is primarily used by ActivityManagerService.
682     * ActivityManagerService will call these methods holding the 'am' lock, which means we
683     * shouldn't be doing any IO work or other long running tasks in these methods.
684     */
685    private class LocalService extends UsageStatsManagerInternal {
686
687        @Override
688        public void reportEvent(ComponentName component, int userId, int eventType) {
689            if (component == null) {
690                Slog.w(TAG, "Event reported without a component name");
691                return;
692            }
693
694            UsageEvents.Event event = new UsageEvents.Event();
695            event.mPackage = component.getPackageName();
696            event.mClass = component.getClassName();
697
698            // This will later be converted to system time.
699            event.mTimeStamp = SystemClock.elapsedRealtime();
700
701            event.mEventType = eventType;
702            mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
703        }
704
705        @Override
706        public void reportEvent(String packageName, int userId, int eventType) {
707            if (packageName == null) {
708                Slog.w(TAG, "Event reported without a package name");
709                return;
710            }
711
712            UsageEvents.Event event = new UsageEvents.Event();
713            event.mPackage = packageName;
714
715            // This will later be converted to system time.
716            event.mTimeStamp = SystemClock.elapsedRealtime();
717
718            event.mEventType = eventType;
719            mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
720        }
721
722        @Override
723        public void reportConfigurationChange(Configuration config, int userId) {
724            if (config == null) {
725                Slog.w(TAG, "Configuration event reported with a null config");
726                return;
727            }
728
729            UsageEvents.Event event = new UsageEvents.Event();
730            event.mPackage = "android";
731
732            // This will later be converted to system time.
733            event.mTimeStamp = SystemClock.elapsedRealtime();
734
735            event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
736            event.mConfiguration = new Configuration(config);
737            mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
738        }
739
740        @Override
741        public boolean isAppIdle(String packageName, int userId) {
742            return UsageStatsService.this.isAppIdle(packageName, userId);
743        }
744
745        @Override
746        public long getLastPackageAccessTime(String packageName, int userId) {
747            return UsageStatsService.this.getLastPackageAccessTime(packageName, userId);
748        }
749
750        @Override
751        public void prepareShutdown() {
752            // This method *WILL* do IO work, but we must block until it is finished or else
753            // we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
754            // we are shutting down.
755            shutdown();
756        }
757
758        @Override
759        public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
760            UsageStatsService.this.addListener(listener);
761        }
762
763        @Override
764        public void removeAppIdleStateChangeListener(
765                AppIdleStateChangeListener listener) {
766            UsageStatsService.this.removeListener(listener);
767        }
768    }
769}
770