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