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