StatsCompanionService.java revision adaf8b344e312853530e276ceff05783133ecf17
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.stats;
17
18import android.app.AlarmManager;
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.pm.PackageInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.UserInfo;
27import android.net.NetworkStats;
28import android.os.BatteryStatsInternal;
29import android.os.Binder;
30import android.os.Bundle;
31import android.os.IBinder;
32import android.os.IStatsCompanionService;
33import android.os.IStatsManager;
34import android.os.Process;
35import android.os.RemoteException;
36import android.os.ServiceManager;
37import android.os.StatsLogEventWrapper;
38import android.os.UserHandle;
39import android.os.UserManager;
40import android.util.Slog;
41import android.util.StatsLog;
42
43import com.android.internal.annotations.GuardedBy;
44import com.android.internal.net.NetworkStatsFactory;
45import com.android.internal.os.KernelWakelockReader;
46import com.android.internal.os.KernelWakelockStats;
47import com.android.internal.os.KernelCpuSpeedReader;
48import com.android.internal.os.PowerProfile;
49import com.android.server.LocalServices;
50import com.android.server.SystemService;
51
52import java.util.ArrayList;
53import java.util.List;
54import java.util.Map;
55
56/**
57 * Helper service for statsd (the native stats management service in cmds/statsd/).
58 * Used for registering and receiving alarms on behalf of statsd.
59 *
60 * @hide
61 */
62public class StatsCompanionService extends IStatsCompanionService.Stub {
63    static final String TAG = "StatsCompanionService";
64    static final boolean DEBUG = true;
65    public static final String ACTION_TRIGGER_COLLECTION =
66        "com.android.server.stats.action.TRIGGER_COLLECTION";
67
68    private final Context mContext;
69    private final AlarmManager mAlarmManager;
70    @GuardedBy("sStatsdLock")
71    private static IStatsManager sStatsd;
72    private static final Object sStatsdLock = new Object();
73
74    private final PendingIntent mAnomalyAlarmIntent;
75    private final PendingIntent mPullingAlarmIntent;
76    private final BroadcastReceiver mAppUpdateReceiver;
77    private final BroadcastReceiver mUserUpdateReceiver;
78    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
79    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
80    private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
81
82    public StatsCompanionService(Context context) {
83        super();
84        mContext = context;
85        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
86
87        mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
88                new Intent(mContext, AnomalyAlarmReceiver.class), 0);
89        mPullingAlarmIntent = PendingIntent.getBroadcast(
90            mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
91        mAppUpdateReceiver = new AppUpdateReceiver();
92        mUserUpdateReceiver = new BroadcastReceiver() {
93            @Override
94            public void onReceive(Context context, Intent intent) {
95                synchronized (sStatsdLock) {
96                    sStatsd = fetchStatsdService();
97                    if (sStatsd == null) {
98                        Slog.w(TAG, "Could not access statsd");
99                        return;
100                    }
101                    try {
102                        // Pull the latest state of UID->app name, version mapping.
103                        // Needed since the new user basically has a version of every app.
104                        informAllUidsLocked(context);
105                    } catch (RemoteException e) {
106                        Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
107                        forgetEverything();
108                    }
109                }
110            }
111        };
112        Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
113        PowerProfile powerProfile = new PowerProfile(context);
114        final int numClusters = powerProfile.getNumCpuClusters();
115        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
116        int firstCpuOfCluster = 0;
117        for (int i = 0; i < numClusters; i++) {
118            final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
119            mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
120                            numSpeedSteps);
121            firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
122        }
123    }
124
125    @Override
126    public void sendBroadcast(String pkg, String cls) {
127        mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
128                UserHandle.SYSTEM);
129    }
130
131    private final static int[] toIntArray(List<Integer> list) {
132        int[] ret = new int[list.size()];
133        for (int i = 0; i < ret.length; i++) {
134            ret[i] = list.get(i);
135        }
136        return ret;
137    }
138
139    // Assumes that sStatsdLock is held.
140    private final void informAllUidsLocked(Context context) throws RemoteException {
141        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
142        PackageManager pm = context.getPackageManager();
143        final List<UserInfo> users = um.getUsers(true);
144        if (DEBUG) {
145            Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
146        }
147
148        List<Integer> uids = new ArrayList();
149        List<Integer> versions = new ArrayList();
150        List<String> apps = new ArrayList();
151
152        // Add in all the apps for every user/profile.
153        for (UserInfo profile : users) {
154            List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
155            for (int j = 0; j < pi.size(); j++) {
156                if (pi.get(j).applicationInfo != null) {
157                    uids.add(pi.get(j).applicationInfo.uid);
158                    versions.add(pi.get(j).versionCode);
159                    apps.add(pi.get(j).packageName);
160                }
161            }
162        }
163        sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
164                String[apps.size()]));
165        if (DEBUG) {
166            Slog.w(TAG, "Sent data for " + uids.size() + " apps");
167        }
168    }
169
170    public final static class AppUpdateReceiver extends BroadcastReceiver {
171        @Override
172        public void onReceive(Context context, Intent intent) {
173            /**
174             * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
175             * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
176             * If we can't find the value for EXTRA_REPLACING, we default to false.
177             */
178            if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
179                    && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
180                return; // Keep only replacing or normal add and remove.
181            }
182            Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
183            synchronized (sStatsdLock) {
184                if (sStatsd == null) {
185                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
186                    return;
187                }
188                try {
189                    if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
190                        Bundle b = intent.getExtras();
191                        int uid = b.getInt(Intent.EXTRA_UID);
192                        boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
193                        if (!replacing) {
194                            // Don't bother sending an update if we're right about to get another
195                            // intent for the new version that's added.
196                            PackageManager pm = context.getPackageManager();
197                            String app = intent.getData().getSchemeSpecificPart();
198                            sStatsd.informOnePackageRemoved(app, uid);
199                        }
200                    } else {
201                        PackageManager pm = context.getPackageManager();
202                        Bundle b = intent.getExtras();
203                        int uid = b.getInt(Intent.EXTRA_UID);
204                        String app = intent.getData().getSchemeSpecificPart();
205                        PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
206                        sStatsd.informOnePackage(app, uid, pi.versionCode);
207                    }
208                } catch (Exception e) {
209                    Slog.w(TAG, "Failed to inform statsd of an app update", e);
210                }
211            }
212        }
213    }
214
215    public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
216        @Override
217        public void onReceive(Context context, Intent intent) {
218            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
219            synchronized (sStatsdLock) {
220                if (sStatsd == null) {
221                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
222                    return;
223                }
224                try {
225                    // Two-way call to statsd to retain AlarmManager wakelock
226                    sStatsd.informAnomalyAlarmFired();
227                } catch (RemoteException e) {
228                    Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
229                }
230            }
231            // AlarmManager releases its own wakelock here.
232        }
233    }
234
235    public final static class PullingAlarmReceiver extends BroadcastReceiver {
236      @Override
237      public void onReceive(Context context, Intent intent) {
238        if (DEBUG)
239          Slog.d(TAG, "Time to poll something.");
240        synchronized (sStatsdLock) {
241          if (sStatsd == null) {
242            Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing");
243            return;
244          }
245          try {
246            // Two-way call to statsd to retain AlarmManager wakelock
247            sStatsd.informPollAlarmFired();
248          } catch (RemoteException e) {
249            Slog.w(TAG, "Failed to inform statsd of pulling alarm firing", e);
250          }
251        }
252        // AlarmManager releases its own wakelock here.
253      }
254    }
255
256    @Override // Binder call
257    public void setAnomalyAlarm(long timestampMs) {
258        enforceCallingPermission();
259        if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
260        final long callingToken = Binder.clearCallingIdentity();
261        try {
262            // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
263            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
264            // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
265            mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
266        } finally {
267            Binder.restoreCallingIdentity(callingToken);
268        }
269    }
270
271    @Override // Binder call
272    public void cancelAnomalyAlarm() {
273        enforceCallingPermission();
274        if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
275        final long callingToken = Binder.clearCallingIdentity();
276        try {
277            mAlarmManager.cancel(mAnomalyAlarmIntent);
278        } finally {
279            Binder.restoreCallingIdentity(callingToken);
280        }
281    }
282
283    @Override // Binder call
284    public void setPullingAlarms(long timestampMs, long intervalMs) {
285      enforceCallingPermission();
286      if (DEBUG)
287        Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
288      final long callingToken = Binder.clearCallingIdentity();
289      try {
290        // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
291        // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
292        // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
293        mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
294      } finally {
295        Binder.restoreCallingIdentity(callingToken);
296      }
297    }
298
299    @Override // Binder call
300    public void cancelPullingAlarms() {
301      enforceCallingPermission();
302      if (DEBUG)
303        Slog.d(TAG, "Cancelling pulling alarm");
304      final long callingToken = Binder.clearCallingIdentity();
305      try {
306        mAlarmManager.cancel(mPullingAlarmIntent);
307      } finally {
308        Binder.restoreCallingIdentity(callingToken);
309      }
310    }
311
312    private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
313        List<StatsLogEventWrapper> ret = new ArrayList<>();
314        int size = stats.size();
315        NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
316        for (int j = 0; j < size; j++) {
317            stats.getValues(j, entry);
318            StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
319            e.writeInt(entry.uid);
320            if (withFGBG) {
321                e.writeInt(entry.set);
322            }
323            e.writeLong(entry.rxBytes);
324            e.writeLong(entry.rxPackets);
325            e.writeLong(entry.txBytes);
326            e.writeLong(entry.txPackets);
327            ret.add(e);
328        }
329        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
330    }
331
332    /**
333     * Allows rollups per UID but keeping the set (foreground/background) slicing.
334     * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
335     */
336    private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
337        final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
338
339        final NetworkStats.Entry entry = new NetworkStats.Entry();
340        entry.iface = NetworkStats.IFACE_ALL;
341        entry.tag = NetworkStats.TAG_NONE;
342        entry.metered = NetworkStats.METERED_ALL;
343        entry.roaming = NetworkStats.ROAMING_ALL;
344
345        int size = stats.size();
346        NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
347        for (int i = 0; i < size; i++) {
348            stats.getValues(i, recycle);
349
350            // Skip specific tags, since already counted in TAG_NONE
351            if (recycle.tag != NetworkStats.TAG_NONE) continue;
352
353            entry.set = recycle.set; // Allows slicing by background/foreground
354            entry.uid = recycle.uid;
355            entry.rxBytes = recycle.rxBytes;
356            entry.rxPackets = recycle.rxPackets;
357            entry.txBytes = recycle.txBytes;
358            entry.txPackets = recycle.txPackets;
359            // Operations purposefully omitted since we don't use them for statsd.
360            ret.combineValues(entry);
361        }
362        return ret;
363    }
364
365    @Override // Binder call
366    public StatsLogEventWrapper[] pullData(int tagId) {
367        enforceCallingPermission();
368        if (DEBUG)
369            Slog.d(TAG, "Pulling " + tagId);
370
371        switch (tagId) {
372            case StatsLog.WIFI_BYTES_TRANSFERRED: {
373                long token = Binder.clearCallingIdentity();
374                try {
375                    // TODO: Consider caching the following call to get BatteryStatsInternal.
376                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
377                    String[] ifaces = bs.getWifiIfaces();
378                    if (ifaces.length == 0) {
379                        return null;
380                    }
381                    NetworkStatsFactory nsf = new NetworkStatsFactory();
382                    // Combine all the metrics per Uid into one record.
383                    NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
384                            NetworkStats.TAG_NONE, null).groupedByUid();
385                    return addNetworkStats(tagId, stats, false);
386                } catch (java.io.IOException e) {
387                    Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
388                } finally {
389                    Binder.restoreCallingIdentity(token);
390                }
391                break;
392            }
393            case StatsLog.MOBILE_BYTES_TRANSFERRED: {
394                long token = Binder.clearCallingIdentity();
395                try {
396                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
397                    String[] ifaces = bs.getMobileIfaces();
398                    if (ifaces.length == 0) {
399                        return null;
400                    }
401                    NetworkStatsFactory nsf = new NetworkStatsFactory();
402                    // Combine all the metrics per Uid into one record.
403                    NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
404                        NetworkStats.TAG_NONE, null).groupedByUid();
405                    return addNetworkStats(tagId, stats, false);
406                } catch (java.io.IOException e) {
407                    Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
408                } finally {
409                    Binder.restoreCallingIdentity(token);
410                }
411                break;
412            }
413            case StatsLog.WIFI_BYTES_TRANSFERRED_BY_FG_BG: {
414                long token = Binder.clearCallingIdentity();
415                try {
416                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
417                    String[] ifaces = bs.getWifiIfaces();
418                    if (ifaces.length == 0) {
419                        return null;
420                    }
421                    NetworkStatsFactory nsf = new NetworkStatsFactory();
422                    NetworkStats stats = rollupNetworkStatsByFGBG(
423                            nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
424                            NetworkStats.TAG_NONE, null));
425                    return addNetworkStats(tagId, stats, true);
426                } catch (java.io.IOException e) {
427                    Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
428                } finally {
429                    Binder.restoreCallingIdentity(token);
430                }
431                break;
432            }
433            case StatsLog.MOBILE_BYTES_TRANSFERRED_BY_FG_BG: {
434                long token = Binder.clearCallingIdentity();
435                try {
436                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
437                    String[] ifaces = bs.getMobileIfaces();
438                    if (ifaces.length == 0) {
439                        return null;
440                    }
441                    NetworkStatsFactory nsf = new NetworkStatsFactory();
442                    NetworkStats stats = rollupNetworkStatsByFGBG(
443                            nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
444                            NetworkStats.TAG_NONE, null));
445                    return addNetworkStats(tagId, stats, true);
446                } catch (java.io.IOException e) {
447                    Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
448                } finally {
449                    Binder.restoreCallingIdentity(token);
450                }
451                break;
452            }
453            case StatsLog.KERNEL_WAKELOCK_PULLED: {
454                final KernelWakelockStats wakelockStats =
455                        mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
456                List<StatsLogEventWrapper> ret = new ArrayList();
457                for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
458                    String name = ent.getKey();
459                    KernelWakelockStats.Entry kws = ent.getValue();
460                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
461                    e.writeString(name);
462                    e.writeInt(kws.mCount);
463                    e.writeInt(kws.mVersion);
464                    e.writeLong(kws.mTotalTime);
465                    ret.add(e);
466                }
467                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
468            }
469            case StatsLog.CPU_TIME_PER_FREQ_PULLED: {
470                List<StatsLogEventWrapper> ret = new ArrayList();
471                for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
472                    long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readDelta();
473                    if (clusterTimeMs != null) {
474                        for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
475                            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
476                            e.writeInt(tagId);
477                            e.writeInt(speed);
478                            e.writeLong(clusterTimeMs[speed]);
479                            ret.add(e);
480                        }
481                    }
482                }
483                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
484            }
485            default:
486                Slog.w(TAG, "No such tagId data as " + tagId);
487                return null;
488        }
489        return null;
490    }
491
492    @Override // Binder call
493    public void statsdReady() {
494        enforceCallingPermission();
495        if (DEBUG) Slog.d(TAG, "learned that statsdReady");
496        sayHiToStatsd(); // tell statsd that we're ready too and link to it
497    }
498
499    private void enforceCallingPermission() {
500        if (Binder.getCallingPid() == Process.myPid()) {
501            return;
502        }
503        mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
504    }
505
506    // Lifecycle and related code
507
508    /**
509     * Fetches the statsd IBinder service
510     */
511    private static IStatsManager fetchStatsdService() {
512        return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
513    }
514
515    public static final class Lifecycle extends SystemService {
516        private StatsCompanionService mStatsCompanionService;
517
518        public Lifecycle(Context context) {
519            super(context);
520        }
521
522        @Override
523        public void onStart() {
524            mStatsCompanionService = new StatsCompanionService(getContext());
525            try {
526                publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
527                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
528            } catch (Exception e) {
529                Slog.e(TAG, "Failed to publishBinderService", e);
530            }
531        }
532
533        @Override
534        public void onBootPhase(int phase) {
535            super.onBootPhase(phase);
536            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
537                mStatsCompanionService.systemReady();
538            }
539        }
540    }
541
542    /**
543     * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
544     */
545    private void systemReady() {
546        if (DEBUG) Slog.d(TAG, "Learned that systemReady");
547        sayHiToStatsd();
548    }
549
550    /**
551     * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
552     */
553    private void sayHiToStatsd() {
554        synchronized (sStatsdLock) {
555            if (sStatsd != null) {
556                Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
557                        new IllegalStateException("sStatsd is not null when being fetched"));
558                return;
559            }
560            sStatsd = fetchStatsdService();
561            if (sStatsd == null) {
562                Slog.w(TAG, "Could not access statsd");
563                return;
564            }
565            if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
566            try {
567                sStatsd.statsCompanionReady();
568                // If the statsCompanionReady two-way binder call returns, link to statsd.
569                try {
570                    sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
571                } catch (RemoteException e) {
572                    Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
573                    forgetEverything();
574                }
575                // Setup broadcast receiver for updates
576                IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
577                filter.addAction(Intent.ACTION_PACKAGE_ADDED);
578                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
579                filter.addDataScheme("package");
580                mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
581                        null);
582
583                // Setup receiver for user initialize (which happens once for a new user) and
584                // if a user is removed.
585                filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
586                filter.addAction(Intent.ACTION_USER_REMOVED);
587                mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
588                        filter, null, null);
589
590                // Pull the latest state of UID->app name, version mapping when statsd starts.
591                informAllUidsLocked(mContext);
592            } catch (RemoteException e) {
593                Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
594                forgetEverything();
595            }
596        }
597    }
598
599    private class StatsdDeathRecipient implements IBinder.DeathRecipient {
600        @Override
601        public void binderDied() {
602            Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
603            forgetEverything();
604        }
605    }
606
607    private void forgetEverything() {
608        synchronized (sStatsdLock) {
609            sStatsd = null;
610            mContext.unregisterReceiver(mAppUpdateReceiver);
611            mContext.unregisterReceiver(mUserUpdateReceiver);
612            cancelAnomalyAlarm();
613            cancelPullingAlarms();
614        }
615    }
616
617}
618