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  */
16 package com.android.server.stats;
17 
18 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
19 
20 import android.app.AlarmManager;
21 import android.app.AlarmManager.OnAlarmListener;
22 import android.app.StatsManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.FileUtils;
34 import android.os.Handler;
35 import android.os.HandlerThread;
36 import android.os.IBinder;
37 import android.os.IStatsCompanionService;
38 import android.os.IStatsd;
39 import android.os.Looper;
40 import android.os.ParcelFileDescriptor;
41 import android.os.PowerManager;
42 import android.os.RemoteException;
43 import android.os.StatsFrameworkInitializer;
44 import android.os.SystemClock;
45 import android.os.UserHandle;
46 import android.os.UserManager;
47 import android.util.Log;
48 import android.util.proto.ProtoOutputStream;
49 
50 import com.android.internal.annotations.GuardedBy;
51 
52 import java.io.File;
53 import java.io.FileDescriptor;
54 import java.io.FileOutputStream;
55 import java.io.IOException;
56 import java.io.PrintWriter;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.List;
60 import java.util.concurrent.TimeUnit;
61 import java.util.concurrent.atomic.AtomicBoolean;
62 
63 /**
64  * Helper service for statsd (the native stats management service in cmds/statsd/).
65  * Used for registering and receiving alarms on behalf of statsd.
66  *
67  * @hide
68  */
69 public class StatsCompanionService extends IStatsCompanionService.Stub {
70 
71     private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
72 
73     public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
74     public static final String CONFIG_DIR = "/data/misc/stats-service";
75 
76     static final String TAG = "StatsCompanionService";
77     static final boolean DEBUG = false;
78     /**
79      * Hard coded field ids of frameworks/base/cmds/statsd/src/uid_data.proto
80      * to be used in ProtoOutputStream.
81      */
82     private static final int APPLICATION_INFO_FIELD_ID = 1;
83     private static final int UID_FIELD_ID = 1;
84     private static final int VERSION_FIELD_ID = 2;
85     private static final int VERSION_STRING_FIELD_ID = 3;
86     private static final int PACKAGE_NAME_FIELD_ID = 4;
87     private static final int INSTALLER_FIELD_ID = 5;
88 
89     public static final int DEATH_THRESHOLD = 10;
90 
91     static final class CompanionHandler extends Handler {
CompanionHandler(Looper looper)92         CompanionHandler(Looper looper) {
93             super(looper);
94         }
95     }
96 
97     private final Context mContext;
98     private final AlarmManager mAlarmManager;
99     @GuardedBy("sStatsdLock")
100     private static IStatsd sStatsd;
101     private static final Object sStatsdLock = new Object();
102 
103     private final OnAlarmListener mAnomalyAlarmListener;
104     private final OnAlarmListener mPullingAlarmListener;
105     private final OnAlarmListener mPeriodicAlarmListener;
106 
107     private StatsManagerService mStatsManagerService;
108 
109     @GuardedBy("sStatsdLock")
110     private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
111     @GuardedBy("sStatsdLock")
112     private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
113     private final CompanionHandler mHandler;
114 
115     // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle.
116     private AtomicBoolean mBootCompleted = new AtomicBoolean(false);
117 
StatsCompanionService(Context context)118     public StatsCompanionService(Context context) {
119         super();
120         mContext = context;
121         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
122         if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
123         HandlerThread handlerThread = new HandlerThread(TAG);
124         handlerThread.start();
125         mHandler = new CompanionHandler(handlerThread.getLooper());
126 
127         mAnomalyAlarmListener = new AnomalyAlarmListener(context);
128         mPullingAlarmListener = new PullingAlarmListener(context);
129         mPeriodicAlarmListener = new PeriodicAlarmListener(context);
130     }
131 
toIntArray(List<Integer> list)132     private final static int[] toIntArray(List<Integer> list) {
133         int[] ret = new int[list.size()];
134         for (int i = 0; i < ret.length; i++) {
135             ret[i] = list.get(i);
136         }
137         return ret;
138     }
139 
toLongArray(List<Long> list)140     private final static long[] toLongArray(List<Long> list) {
141         long[] ret = new long[list.size()];
142         for (int i = 0; i < ret.length; i++) {
143             ret[i] = list.get(i);
144         }
145         return ret;
146     }
147 
148     /**
149      * Non-blocking call to retrieve a reference to statsd
150      *
151      * @return IStatsd object if statsd is ready, null otherwise.
152      */
getStatsdNonblocking()153     private static IStatsd getStatsdNonblocking() {
154         synchronized (sStatsdLock) {
155             return sStatsd;
156         }
157     }
158 
informAllUids(Context context)159     private static void informAllUids(Context context) {
160         ParcelFileDescriptor[] fds;
161         try {
162             fds = ParcelFileDescriptor.createPipe();
163         } catch (IOException e) {
164             Log.e(TAG, "Failed to create a pipe to send uid map data.", e);
165             return;
166         }
167         HandlerThread backgroundThread = new HandlerThread(
168                 "statsCompanionService.bg", THREAD_PRIORITY_BACKGROUND);
169         backgroundThread.start();
170         Handler handler = new Handler(backgroundThread.getLooper());
171         handler.post(() -> {
172             UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
173             PackageManager pm = context.getPackageManager();
174             final List<UserHandle> users = um.getUserHandles(true);
175             if (DEBUG) {
176                 Log.d(TAG, "Iterating over " + users.size() + " userHandles.");
177             }
178             IStatsd statsd = getStatsdNonblocking();
179             if (statsd == null) {
180                 return;
181             }
182             try {
183                 statsd.informAllUidData(fds[0]);
184             } catch (RemoteException e) {
185                 Log.e(TAG, "Failed to send uid map to statsd");
186             }
187             try {
188                 fds[0].close();
189             } catch (IOException e) {
190                 Log.e(TAG, "Failed to close the read side of the pipe.", e);
191             }
192             final ParcelFileDescriptor writeFd = fds[1];
193             FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
194             try {
195                 ProtoOutputStream output = new ProtoOutputStream(fout);
196                 int numRecords = 0;
197                 // Add in all the apps for every user/profile.
198                 for (UserHandle userHandle : users) {
199                     List<PackageInfo> pi =
200                             pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
201                                             | PackageManager.MATCH_ANY_USER
202                                             | PackageManager.MATCH_APEX,
203                                     userHandle.getIdentifier());
204                     for (int j = 0; j < pi.size(); j++) {
205                         if (pi.get(j).applicationInfo != null) {
206                             String installer;
207                             try {
208                                 installer = pm.getInstallerPackageName(pi.get(j).packageName);
209                             } catch (IllegalArgumentException e) {
210                                 installer = "";
211                             }
212                             long applicationInfoToken =
213                                     output.start(ProtoOutputStream.FIELD_TYPE_MESSAGE
214                                             | ProtoOutputStream.FIELD_COUNT_REPEATED
215                                                     | APPLICATION_INFO_FIELD_ID);
216                             output.write(ProtoOutputStream.FIELD_TYPE_INT32
217                                     | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
218                                             pi.get(j).applicationInfo.uid);
219                             output.write(ProtoOutputStream.FIELD_TYPE_INT64
220                                     | ProtoOutputStream.FIELD_COUNT_SINGLE
221                                             | VERSION_FIELD_ID, pi.get(j).getLongVersionCode());
222                             output.write(ProtoOutputStream.FIELD_TYPE_STRING
223                                     | ProtoOutputStream.FIELD_COUNT_SINGLE
224                                     | VERSION_STRING_FIELD_ID,
225                                             pi.get(j).versionName);
226                             output.write(ProtoOutputStream.FIELD_TYPE_STRING
227                                     | ProtoOutputStream.FIELD_COUNT_SINGLE
228                                             | PACKAGE_NAME_FIELD_ID, pi.get(j).packageName);
229                             output.write(ProtoOutputStream.FIELD_TYPE_STRING
230                                     | ProtoOutputStream.FIELD_COUNT_SINGLE
231                                             | INSTALLER_FIELD_ID,
232                                                     installer == null ? "" : installer);
233                             numRecords++;
234                             output.end(applicationInfoToken);
235                         }
236                     }
237                 }
238                 output.flush();
239                 if (DEBUG) {
240                     Log.d(TAG, "Sent data for " + numRecords + " apps");
241                 }
242             } finally {
243                 FileUtils.closeQuietly(fout);
244                 backgroundThread.quit();
245                 backgroundThread.interrupt();
246             }
247         });
248     }
249 
250     private static class WakelockThread extends Thread {
251         private final PowerManager.WakeLock mWl;
252         private final Runnable mRunnable;
253 
WakelockThread(Context context, String wakelockName, Runnable runnable)254         WakelockThread(Context context, String wakelockName, Runnable runnable) {
255             PowerManager powerManager = (PowerManager)
256                     context.getSystemService(Context.POWER_SERVICE);
257             mWl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakelockName);
258             mRunnable = runnable;
259         }
260         @Override
run()261         public void run() {
262             try {
263                 mRunnable.run();
264             } finally {
265                 mWl.release();
266             }
267         }
268         @Override
start()269         public void start() {
270             mWl.acquire();
271             super.start();
272         }
273     }
274 
275     private final static class AppUpdateReceiver extends BroadcastReceiver {
276         @Override
onReceive(Context context, Intent intent)277         public void onReceive(Context context, Intent intent) {
278             /**
279              * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
280              * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
281              * If we can't find the value for EXTRA_REPLACING, we default to false.
282              */
283             if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
284                     && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
285                 return; // Keep only replacing or normal add and remove.
286             }
287             if (DEBUG) Log.d(TAG, "StatsCompanionService noticed an app was updated.");
288             synchronized (sStatsdLock) {
289                 if (sStatsd == null) {
290                     Log.w(TAG, "Could not access statsd to inform it of an app update");
291                     return;
292                 }
293                 try {
294                     if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
295                         Bundle b = intent.getExtras();
296                         int uid = b.getInt(Intent.EXTRA_UID);
297                         boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
298                         if (!replacing) {
299                             // Don't bother sending an update if we're right about to get another
300                             // intent for the new version that's added.
301                             String app = intent.getData().getSchemeSpecificPart();
302                             sStatsd.informOnePackageRemoved(app, uid);
303                         }
304                     } else {
305                         PackageManager pm = context.getPackageManager();
306                         Bundle b = intent.getExtras();
307                         int uid = b.getInt(Intent.EXTRA_UID);
308                         String app = intent.getData().getSchemeSpecificPart();
309                         PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
310                         String installer;
311                         try {
312                             installer = pm.getInstallerPackageName(app);
313                         } catch (IllegalArgumentException e) {
314                             installer = "";
315                         }
316                         sStatsd.informOnePackage(
317                                 app,
318                                 uid,
319                                 pi.getLongVersionCode(),
320                                 pi.versionName == null ? "" : pi.versionName,
321                                 installer == null ? "" : installer);
322                     }
323                 } catch (Exception e) {
324                     Log.w(TAG, "Failed to inform statsd of an app update", e);
325                 }
326             }
327         }
328     }
329 
330     private static final class UserUpdateReceiver extends BroadcastReceiver {
331         @Override
onReceive(Context context, Intent intent)332         public void onReceive(Context context, Intent intent) {
333             // Pull the latest state of UID->app name, version mapping.
334             // Needed since the new user basically has a version of every app.
335             informAllUids(context);
336         }
337     }
338 
339     public static final class AnomalyAlarmListener implements OnAlarmListener {
340         private final Context mContext;
341 
AnomalyAlarmListener(Context context)342         AnomalyAlarmListener(Context context) {
343             mContext = context;
344         }
345 
346         @Override
onAlarm()347         public void onAlarm() {
348             if (DEBUG) {
349                 Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
350                         + System.currentTimeMillis() + "ms.");
351             }
352             IStatsd statsd = getStatsdNonblocking();
353             if (statsd == null) {
354                 Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
355                 return;
356             }
357 
358             // Wakelock needs to be retained while calling statsd.
359             Thread thread = new WakelockThread(mContext,
360                     AnomalyAlarmListener.class.getCanonicalName(), new Runnable() {
361                         @Override
362                         public void run() {
363                             try {
364                                 statsd.informAnomalyAlarmFired();
365                             } catch (RemoteException e) {
366                                 Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
367                             }
368                         }
369                     });
370             thread.start();
371         }
372     }
373 
374     public final static class PullingAlarmListener implements OnAlarmListener {
375         private final Context mContext;
376 
PullingAlarmListener(Context context)377         PullingAlarmListener(Context context) {
378             mContext = context;
379         }
380 
381         @Override
onAlarm()382         public void onAlarm() {
383             if (DEBUG) {
384                 Log.d(TAG, "Time to poll something.");
385             }
386             IStatsd statsd = getStatsdNonblocking();
387             if (statsd == null) {
388                 Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
389                 return;
390             }
391 
392             // Wakelock needs to be retained while calling statsd.
393             Thread thread = new WakelockThread(mContext,
394                     PullingAlarmListener.class.getCanonicalName(), new Runnable() {
395                         @Override
396                         public void run() {
397                             try {
398                                 statsd.informPollAlarmFired();
399                             } catch (RemoteException e) {
400                                 Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
401                             }
402                         }
403                     });
404             thread.start();
405         }
406     }
407 
408     public final static class PeriodicAlarmListener implements OnAlarmListener {
409         private final Context mContext;
410 
PeriodicAlarmListener(Context context)411         PeriodicAlarmListener(Context context) {
412             mContext = context;
413         }
414 
415         @Override
onAlarm()416         public void onAlarm() {
417             if (DEBUG) {
418                 Log.d(TAG, "Time to trigger periodic alarm.");
419             }
420             IStatsd statsd = getStatsdNonblocking();
421             if (statsd == null) {
422                 Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
423                 return;
424             }
425 
426             // Wakelock needs to be retained while calling statsd.
427             Thread thread = new WakelockThread(mContext,
428                     PeriodicAlarmListener.class.getCanonicalName(), new Runnable() {
429                         @Override
430                         public void run() {
431                             try {
432                                 statsd.informAlarmForSubscriberTriggeringFired();
433                             } catch (RemoteException e) {
434                                 Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
435                             }
436                         }
437                     });
438             thread.start();
439         }
440     }
441 
442     public final static class ShutdownEventReceiver extends BroadcastReceiver {
443         @Override
onReceive(Context context, Intent intent)444         public void onReceive(Context context, Intent intent) {
445             /**
446              * Skip immediately if intent is not relevant to device shutdown.
447              */
448             if (!intent.getAction().equals(Intent.ACTION_REBOOT)
449                     && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
450                     && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
451                 return;
452             }
453 
454             if (DEBUG) {
455                 Log.i(TAG, "StatsCompanionService noticed a shutdown.");
456             }
457             IStatsd statsd = getStatsdNonblocking();
458             if (statsd == null) {
459                 Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
460                 return;
461             }
462             try {
463                 // two way binder call
464                 statsd.informDeviceShutdown();
465             } catch (Exception e) {
466                 Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
467             }
468         }
469     }
470 
471     @Override // Binder call
setAnomalyAlarm(long timestampMs)472     public void setAnomalyAlarm(long timestampMs) {
473         StatsCompanion.enforceStatsdCallingUid();
474         if (DEBUG) Log.d(TAG, "Setting anomaly alarm for " + timestampMs);
475         final long callingToken = Binder.clearCallingIdentity();
476         try {
477             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
478             // only fire when it awakens.
479             // AlarmManager will automatically cancel any previous mAnomalyAlarmListener alarm.
480             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".anomaly",
481                     mAnomalyAlarmListener, mHandler);
482         } finally {
483             Binder.restoreCallingIdentity(callingToken);
484         }
485     }
486 
487     @Override // Binder call
cancelAnomalyAlarm()488     public void cancelAnomalyAlarm() {
489         StatsCompanion.enforceStatsdCallingUid();
490         if (DEBUG) Log.d(TAG, "Cancelling anomaly alarm");
491         final long callingToken = Binder.clearCallingIdentity();
492         try {
493             mAlarmManager.cancel(mAnomalyAlarmListener);
494         } finally {
495             Binder.restoreCallingIdentity(callingToken);
496         }
497     }
498 
499     @Override // Binder call
setAlarmForSubscriberTriggering(long timestampMs)500     public void setAlarmForSubscriberTriggering(long timestampMs) {
501         StatsCompanion.enforceStatsdCallingUid();
502         if (DEBUG) {
503             Log.d(TAG,
504                     "Setting periodic alarm in about " + (timestampMs
505                             - SystemClock.elapsedRealtime()));
506         }
507         final long callingToken = Binder.clearCallingIdentity();
508         try {
509             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
510             // only fire when it awakens.
511             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
512                     mPeriodicAlarmListener, mHandler);
513         } finally {
514             Binder.restoreCallingIdentity(callingToken);
515         }
516     }
517 
518     @Override // Binder call
cancelAlarmForSubscriberTriggering()519     public void cancelAlarmForSubscriberTriggering() {
520         StatsCompanion.enforceStatsdCallingUid();
521         if (DEBUG) {
522             Log.d(TAG, "Cancelling periodic alarm");
523         }
524         final long callingToken = Binder.clearCallingIdentity();
525         try {
526             mAlarmManager.cancel(mPeriodicAlarmListener);
527         } finally {
528             Binder.restoreCallingIdentity(callingToken);
529         }
530     }
531 
532     @Override // Binder call
setPullingAlarm(long nextPullTimeMs)533     public void setPullingAlarm(long nextPullTimeMs) {
534         StatsCompanion.enforceStatsdCallingUid();
535         if (DEBUG) {
536             Log.d(TAG, "Setting pulling alarm in about "
537                     + (nextPullTimeMs - SystemClock.elapsedRealtime()));
538         }
539         final long callingToken = Binder.clearCallingIdentity();
540         try {
541             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
542             // only fire when it awakens.
543             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
544                     mPullingAlarmListener, mHandler);
545         } finally {
546             Binder.restoreCallingIdentity(callingToken);
547         }
548     }
549 
550     @Override // Binder call
cancelPullingAlarm()551     public void cancelPullingAlarm() {
552         StatsCompanion.enforceStatsdCallingUid();
553         if (DEBUG) {
554             Log.d(TAG, "Cancelling pulling alarm");
555         }
556         final long callingToken = Binder.clearCallingIdentity();
557         try {
558             mAlarmManager.cancel(mPullingAlarmListener);
559         } finally {
560             Binder.restoreCallingIdentity(callingToken);
561         }
562     }
563 
564     @Override // Binder call
statsdReady()565     public void statsdReady() {
566         StatsCompanion.enforceStatsdCallingUid();
567         if (DEBUG) {
568             Log.d(TAG, "learned that statsdReady");
569         }
570         sayHiToStatsd(); // tell statsd that we're ready too and link to it
571 
572         final Intent intent = new Intent(StatsManager.ACTION_STATSD_STARTED);
573         // Retrieve list of broadcast receivers for this broadcast & send them directed broadcasts
574         // to wake them up (if they're in background).
575         List<ResolveInfo> resolveInfos =
576                 mContext.getPackageManager().queryBroadcastReceiversAsUser(
577                         intent, 0, UserHandle.SYSTEM);
578         if (resolveInfos == null || resolveInfos.isEmpty()) {
579             return; // No need to send broadcast.
580         }
581 
582         for (ResolveInfo resolveInfo : resolveInfos) {
583             Intent intentToSend = new Intent(intent);
584             intentToSend.setComponent(new ComponentName(
585                     resolveInfo.activityInfo.applicationInfo.packageName,
586                     resolveInfo.activityInfo.name));
587             mContext.sendBroadcastAsUser(intentToSend, UserHandle.SYSTEM,
588                     android.Manifest.permission.DUMP);
589         }
590     }
591 
592     @Override // Binder call
checkPermission(String permission, int pid, int uid)593     public boolean checkPermission(String permission, int pid, int uid) {
594         StatsCompanion.enforceStatsdCallingUid();
595         return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
596     }
597 
598     // Statsd related code
599 
600     /**
601      * Fetches the statsd IBinder service. This is a blocking call that always refetches statsd
602      * instead of returning the cached sStatsd.
603      * Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
604      * the cached sStatsd via {@link #getStatsdNonblocking()}.
605      */
fetchStatsdServiceLocked()606     private IStatsd fetchStatsdServiceLocked() {
607         sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
608                 .getStatsServiceManager()
609                 .getStatsdServiceRegisterer()
610                 .get());
611         return sStatsd;
612     }
613 
registerStatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers)614     private void registerStatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
615         StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient(statsd, receivers);
616 
617         try {
618             statsd.asBinder().linkToDeath(deathRecipient, /*flags=*/0);
619         } catch (RemoteException e) {
620             Log.e(TAG, "linkToDeath (StatsdDeathRecipient) failed");
621             // Statsd has already died. Unregister receivers ourselves.
622             for (BroadcastReceiver receiver : receivers) {
623                 mContext.unregisterReceiver(receiver);
624             }
625             synchronized (sStatsdLock) {
626                 if (statsd == sStatsd) {
627                     statsdNotReadyLocked();
628                 }
629             }
630         }
631     }
632 
633     /**
634      * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
635      */
systemReady()636     void systemReady() {
637         if (DEBUG) Log.d(TAG, "Learned that systemReady");
638         sayHiToStatsd();
639     }
640 
setStatsManagerService(StatsManagerService statsManagerService)641     void setStatsManagerService(StatsManagerService statsManagerService) {
642         mStatsManagerService = statsManagerService;
643     }
644 
645     /**
646      * Tells statsd that statscompanion is ready. If the binder call returns, link to
647      * statsd.
648      */
sayHiToStatsd()649     private void sayHiToStatsd() {
650         IStatsd statsd;
651         synchronized (sStatsdLock) {
652             if (sStatsd != null && sStatsd.asBinder().isBinderAlive()) {
653                 Log.e(TAG, "statsd has already been fetched before",
654                         new IllegalStateException("IStatsd object should be null or dead"));
655                 return;
656             }
657             statsd = fetchStatsdServiceLocked();
658         }
659 
660         if (statsd == null) {
661             Log.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
662             return;
663         }
664 
665         // Cleann up from previous statsd - cancel any alarms that had been set. Do this here
666         // instead of in binder death because statsd can come back and set different alarms, or not
667         // want to set an alarm when it had been set. This guarantees that when we get a new statsd,
668         // we cancel any alarms before it is able to set them.
669         cancelAnomalyAlarm();
670         cancelPullingAlarm();
671         cancelAlarmForSubscriberTriggering();
672 
673         if (DEBUG) Log.d(TAG, "Saying hi to statsd");
674         mStatsManagerService.statsdReady(statsd);
675         try {
676             statsd.statsCompanionReady();
677 
678             BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver();
679             BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver();
680             BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver();
681 
682             // Setup broadcast receiver for updates.
683             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
684             filter.addAction(Intent.ACTION_PACKAGE_ADDED);
685             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
686             filter.addDataScheme("package");
687             mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
688 
689             // Setup receiver for user initialize (which happens once for a new user)
690             // and if a user is removed.
691             filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
692             filter.addAction(Intent.ACTION_USER_REMOVED);
693             mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
694 
695             // Setup receiver for device reboots or shutdowns.
696             filter = new IntentFilter(Intent.ACTION_REBOOT);
697             filter.addAction(Intent.ACTION_SHUTDOWN);
698             mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, null);
699 
700             // Register death recipient.
701             List<BroadcastReceiver> broadcastReceivers =
702                     List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver);
703             registerStatsdDeathRecipient(statsd, broadcastReceivers);
704 
705             // Tell statsd that boot has completed. The signal may have already been sent, but since
706             // the signal-receiving function is idempotent, that's ok.
707             if (mBootCompleted.get()) {
708                 statsd.bootCompleted();
709             }
710 
711             // Pull the latest state of UID->app name, version mapping when statsd starts.
712             informAllUids(mContext);
713 
714             Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
715         } catch (RemoteException e) {
716             Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
717         }
718     }
719 
720     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
721 
722         private final IStatsd mStatsd;
723         private final List<BroadcastReceiver> mReceiversToUnregister;
724 
StatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers)725         StatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
726             mStatsd = statsd;
727             mReceiversToUnregister = receivers;
728         }
729 
730         // It is possible for binderDied to be called after a restarted statsd calls statsdReady,
731         // but that's alright because the code does not assume an ordering of the two calls.
732         @Override
binderDied()733         public void binderDied() {
734             Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
735             synchronized (sStatsdLock) {
736                 long now = SystemClock.elapsedRealtime();
737                 for (Long timeMillis : mDeathTimeMillis) {
738                     long ageMillis = now - timeMillis;
739                     if (ageMillis > MILLIS_IN_A_DAY) {
740                         mDeathTimeMillis.remove(timeMillis);
741                     }
742                 }
743                 for (Long timeMillis : mDeletedFiles.keySet()) {
744                     long ageMillis = now - timeMillis;
745                     if (ageMillis > MILLIS_IN_A_DAY * 7) {
746                         mDeletedFiles.remove(timeMillis);
747                     }
748                 }
749                 mDeathTimeMillis.add(now);
750                 if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
751                     mDeathTimeMillis.clear();
752                     File[] configs = new File(CONFIG_DIR).listFiles();
753                     if (configs != null && configs.length > 0) {
754                         String fileName = configs[0].getName();
755                         if (configs[0].delete()) {
756                             mDeletedFiles.put(now, fileName);
757                         }
758                     }
759                 }
760 
761                 // Unregister receivers on death because receivers can only be unregistered once.
762                 // Otherwise, an IllegalArgumentException is thrown.
763                 for (BroadcastReceiver receiver: mReceiversToUnregister) {
764                     mContext.unregisterReceiver(receiver);
765                 }
766 
767                 // It's possible for statsd to have restarted and called statsdReady, causing a new
768                 // sStatsd binder object to be fetched, before the binderDied callback runs. Only
769                 // call #statsdNotReadyLocked if that hasn't happened yet.
770                 if (mStatsd == sStatsd) {
771                     statsdNotReadyLocked();
772                 }
773             }
774         }
775     }
776 
statsdNotReadyLocked()777     private void statsdNotReadyLocked() {
778         sStatsd = null;
779         mStatsManagerService.statsdNotReady();
780     }
781 
bootCompleted()782     void bootCompleted() {
783         mBootCompleted.set(true);
784         IStatsd statsd = getStatsdNonblocking();
785         if (statsd == null) {
786             // Statsd is not yet ready.
787             // Delay the boot completed ping to {@link #sayHiToStatsd()}
788             return;
789         }
790         try {
791             statsd.bootCompleted();
792         } catch (RemoteException e) {
793             Log.e(TAG, "Failed to notify statsd that boot completed");
794         }
795     }
796 
797     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)798     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
799         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
800                 != PackageManager.PERMISSION_GRANTED) {
801             return;
802         }
803 
804         synchronized (sStatsdLock) {
805             writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
806             if (mDeletedFiles.size() > 0) {
807                 writer.println("  timestamp, deleted file name");
808             }
809             long lastBootMillis =
810                     SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
811             for (Long elapsedMillis : mDeletedFiles.keySet()) {
812                 long deletionMillis = lastBootMillis + elapsedMillis;
813                 writer.println("  " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
814             }
815         }
816     }
817 }
818