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