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