blob: b5e72247a4a3585ce8d695730fe6c2a559d674a6 [file] [log] [blame]
Tej Singh9a1f75b2019-09-20 16:29:13 -07001/*
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 */
16package com.android.server.stats;
17
Jeffrey Huang447a0782019-11-26 15:10:13 -080018import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
Tej Singh9a1f75b2019-09-20 16:29:13 -070019
Tej Singh9a1f75b2019-09-20 16:29:13 -070020import android.app.AlarmManager;
21import android.app.AlarmManager.OnAlarmListener;
Tej Singh9a1f75b2019-09-20 16:29:13 -070022import android.app.StatsManager;
Tej Singh9a1f75b2019-09-20 16:29:13 -070023import android.content.BroadcastReceiver;
Muhammad Qureshiddfc1ba2020-02-19 13:41:59 -080024import android.content.ComponentName;
Tej Singh9a1f75b2019-09-20 16:29:13 -070025import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
Tej Singh9a1f75b2019-09-20 16:29:13 -070028import android.content.pm.PackageInfo;
29import android.content.pm.PackageManager;
Muhammad Qureshiddfc1ba2020-02-19 13:41:59 -080030import android.content.pm.ResolveInfo;
Tej Singh9a1f75b2019-09-20 16:29:13 -070031import android.os.Binder;
Tej Singh9a1f75b2019-09-20 16:29:13 -070032import android.os.Bundle;
Anton Hanssond0ab8c92020-03-19 15:40:36 +000033import android.os.FileUtils;
Tej Singh9a1f75b2019-09-20 16:29:13 -070034import android.os.Handler;
35import android.os.HandlerThread;
36import android.os.IBinder;
37import android.os.IStatsCompanionService;
Jeffrey Huang9d2dbad2019-12-11 14:47:32 -080038import android.os.IStatsd;
Tej Singh9a1f75b2019-09-20 16:29:13 -070039import android.os.Looper;
40import android.os.ParcelFileDescriptor;
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -070041import android.os.PowerManager;
Tej Singh9a1f75b2019-09-20 16:29:13 -070042import android.os.RemoteException;
Jeffrey Huang49790742020-01-23 13:22:10 -080043import android.os.StatsFrameworkInitializer;
Tej Singh9a1f75b2019-09-20 16:29:13 -070044import android.os.SystemClock;
Tej Singh9a1f75b2019-09-20 16:29:13 -070045import android.os.UserHandle;
46import android.os.UserManager;
Muhammad Qureshie7121622020-02-07 09:04:54 -080047import android.util.Log;
Tej Singh9a1f75b2019-09-20 16:29:13 -070048import android.util.proto.ProtoOutputStream;
Tej Singh9a1f75b2019-09-20 16:29:13 -070049
50import com.android.internal.annotations.GuardedBy;
Ioannis Ilkos589ac642019-11-07 18:57:48 +000051
Tej Singh9a1f75b2019-09-20 16:29:13 -070052import java.io.File;
53import java.io.FileDescriptor;
54import java.io.FileOutputStream;
55import java.io.IOException;
Tej Singh9a1f75b2019-09-20 16:29:13 -070056import java.io.PrintWriter;
Tej Singh9a1f75b2019-09-20 16:29:13 -070057import java.util.HashMap;
58import java.util.HashSet;
59import java.util.List;
Tej Singh9a1f75b2019-09-20 16:29:13 -070060import java.util.concurrent.TimeUnit;
Ruchir Rastogi1fc17992020-04-22 15:37:32 -070061import java.util.concurrent.atomic.AtomicBoolean;
Tej Singh9a1f75b2019-09-20 16:29:13 -070062
63/**
64 * Helper service for statsd (the native stats management service in cmds/statsd/).
65 * Used for registering and receiving alarms on behalf of statsd.
66 *
67 * @hide
68 */
69public class StatsCompanionService extends IStatsCompanionService.Stub {
Jeffrey Huang2b241252020-01-23 14:13:25 -080070
Tej Singh9a1f75b2019-09-20 16:29:13 -070071 private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
72
73 public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
74 public static final String CONFIG_DIR = "/data/misc/stats-service";
75
76 static final String TAG = "StatsCompanionService";
77 static final boolean DEBUG = false;
78 /**
79 * Hard coded field ids of frameworks/base/cmds/statsd/src/uid_data.proto
80 * to be used in ProtoOutputStream.
81 */
82 private static final int APPLICATION_INFO_FIELD_ID = 1;
83 private static final int UID_FIELD_ID = 1;
84 private static final int VERSION_FIELD_ID = 2;
85 private static final int VERSION_STRING_FIELD_ID = 3;
86 private static final int PACKAGE_NAME_FIELD_ID = 4;
87 private static final int INSTALLER_FIELD_ID = 5;
88
Tej Singh9a1f75b2019-09-20 16:29:13 -070089 public static final int DEATH_THRESHOLD = 10;
Tej Singh9a1f75b2019-09-20 16:29:13 -070090
91 static final class CompanionHandler extends Handler {
92 CompanionHandler(Looper looper) {
93 super(looper);
94 }
95 }
96
97 private final Context mContext;
98 private final AlarmManager mAlarmManager;
Tej Singh9a1f75b2019-09-20 16:29:13 -070099 @GuardedBy("sStatsdLock")
Jeffrey Huang9d2dbad2019-12-11 14:47:32 -0800100 private static IStatsd sStatsd;
Tej Singh9a1f75b2019-09-20 16:29:13 -0700101 private static final Object sStatsdLock = new Object();
102
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700103 private final OnAlarmListener mPullingAlarmListener;
104 private final OnAlarmListener mPeriodicAlarmListener;
Tej Singhb877e352019-10-11 11:07:06 -0700105
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800106 private StatsManagerService mStatsManagerService;
107
Tej Singh9a1f75b2019-09-20 16:29:13 -0700108 @GuardedBy("sStatsdLock")
109 private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
110 @GuardedBy("sStatsdLock")
111 private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
112 private final CompanionHandler mHandler;
113
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700114 // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle.
115 private AtomicBoolean mBootCompleted = new AtomicBoolean(false);
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700116
Tej Singh9a1f75b2019-09-20 16:29:13 -0700117 public StatsCompanionService(Context context) {
118 super();
119 mContext = context;
120 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
Muhammad Qureshie7121622020-02-07 09:04:54 -0800121 if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700122 HandlerThread handlerThread = new HandlerThread(TAG);
123 handlerThread.start();
124 mHandler = new CompanionHandler(handlerThread.getLooper());
125
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700126 mPullingAlarmListener = new PullingAlarmListener(context);
127 mPeriodicAlarmListener = new PeriodicAlarmListener(context);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700128 }
129
Tej Singh9a1f75b2019-09-20 16:29:13 -0700130 private final static int[] toIntArray(List<Integer> list) {
131 int[] ret = new int[list.size()];
132 for (int i = 0; i < ret.length; i++) {
133 ret[i] = list.get(i);
134 }
135 return ret;
136 }
137
138 private final static long[] toLongArray(List<Long> list) {
139 long[] ret = new long[list.size()];
140 for (int i = 0; i < ret.length; i++) {
141 ret[i] = list.get(i);
142 }
143 return ret;
144 }
145
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800146 /**
147 * Non-blocking call to retrieve a reference to statsd
148 *
149 * @return IStatsd object if statsd is ready, null otherwise.
150 */
151 private static IStatsd getStatsdNonblocking() {
152 synchronized (sStatsdLock) {
153 return sStatsd;
154 }
155 }
156
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700157 private static void informAllUids(Context context) {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700158 ParcelFileDescriptor[] fds;
159 try {
160 fds = ParcelFileDescriptor.createPipe();
161 } catch (IOException e) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800162 Log.e(TAG, "Failed to create a pipe to send uid map data.", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700163 return;
164 }
Jeffrey Huang447a0782019-11-26 15:10:13 -0800165 HandlerThread backgroundThread = new HandlerThread(
166 "statsCompanionService.bg", THREAD_PRIORITY_BACKGROUND);
167 backgroundThread.start();
168 Handler handler = new Handler(backgroundThread.getLooper());
169 handler.post(() -> {
Jeffrey Huangbde54392020-04-10 17:40:59 -0700170 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
171 PackageManager pm = context.getPackageManager();
172 final List<UserHandle> users = um.getUserHandles(true);
173 if (DEBUG) {
174 Log.d(TAG, "Iterating over " + users.size() + " userHandles.");
175 }
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700176 IStatsd statsd = getStatsdNonblocking();
177 if (statsd == null) {
178 return;
179 }
180 try {
181 statsd.informAllUidData(fds[0]);
182 } catch (RemoteException e) {
183 Log.e(TAG, "Failed to send uid map to statsd");
184 }
185 try {
186 fds[0].close();
187 } catch (IOException e) {
188 Log.e(TAG, "Failed to close the read side of the pipe.", e);
189 }
190 final ParcelFileDescriptor writeFd = fds[1];
Tej Singh9a1f75b2019-09-20 16:29:13 -0700191 FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
192 try {
193 ProtoOutputStream output = new ProtoOutputStream(fout);
194 int numRecords = 0;
195 // Add in all the apps for every user/profile.
Jeffrey Huang48216742019-12-19 13:02:11 -0800196 for (UserHandle userHandle : users) {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700197 List<PackageInfo> pi =
Jeffrey Huangf727d482019-11-21 11:52:40 -0800198 pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700199 | PackageManager.MATCH_ANY_USER
200 | PackageManager.MATCH_APEX,
Jeffrey Huang48216742019-12-19 13:02:11 -0800201 userHandle.getIdentifier());
Tej Singh9a1f75b2019-09-20 16:29:13 -0700202 for (int j = 0; j < pi.size(); j++) {
203 if (pi.get(j).applicationInfo != null) {
204 String installer;
205 try {
206 installer = pm.getInstallerPackageName(pi.get(j).packageName);
207 } catch (IllegalArgumentException e) {
208 installer = "";
209 }
210 long applicationInfoToken =
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800211 output.start(ProtoOutputStream.FIELD_TYPE_MESSAGE
212 | ProtoOutputStream.FIELD_COUNT_REPEATED
Tej Singh9a1f75b2019-09-20 16:29:13 -0700213 | APPLICATION_INFO_FIELD_ID);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800214 output.write(ProtoOutputStream.FIELD_TYPE_INT32
215 | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
Tej Singh9a1f75b2019-09-20 16:29:13 -0700216 pi.get(j).applicationInfo.uid);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800217 output.write(ProtoOutputStream.FIELD_TYPE_INT64
218 | ProtoOutputStream.FIELD_COUNT_SINGLE
Tej Singh9a1f75b2019-09-20 16:29:13 -0700219 | VERSION_FIELD_ID, pi.get(j).getLongVersionCode());
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800220 output.write(ProtoOutputStream.FIELD_TYPE_STRING
221 | ProtoOutputStream.FIELD_COUNT_SINGLE
222 | VERSION_STRING_FIELD_ID,
Tej Singh9a1f75b2019-09-20 16:29:13 -0700223 pi.get(j).versionName);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800224 output.write(ProtoOutputStream.FIELD_TYPE_STRING
225 | ProtoOutputStream.FIELD_COUNT_SINGLE
Tej Singh9a1f75b2019-09-20 16:29:13 -0700226 | PACKAGE_NAME_FIELD_ID, pi.get(j).packageName);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800227 output.write(ProtoOutputStream.FIELD_TYPE_STRING
228 | ProtoOutputStream.FIELD_COUNT_SINGLE
Tej Singh9a1f75b2019-09-20 16:29:13 -0700229 | INSTALLER_FIELD_ID,
230 installer == null ? "" : installer);
231 numRecords++;
232 output.end(applicationInfoToken);
233 }
234 }
235 }
236 output.flush();
237 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800238 Log.d(TAG, "Sent data for " + numRecords + " apps");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700239 }
240 } finally {
Anton Hanssond0ab8c92020-03-19 15:40:36 +0000241 FileUtils.closeQuietly(fout);
Jeffrey Huang447a0782019-11-26 15:10:13 -0800242 backgroundThread.quit();
243 backgroundThread.interrupt();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700244 }
245 });
246 }
247
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700248 private static class WakelockThread extends Thread {
249 private final PowerManager.WakeLock mWl;
250 private final Runnable mRunnable;
251
252 WakelockThread(Context context, String wakelockName, Runnable runnable) {
253 PowerManager powerManager = (PowerManager)
254 context.getSystemService(Context.POWER_SERVICE);
255 mWl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakelockName);
256 mRunnable = runnable;
257 }
258 @Override
259 public void run() {
260 try {
261 mRunnable.run();
262 } finally {
263 mWl.release();
264 }
265 }
266 @Override
267 public void start() {
268 mWl.acquire();
269 super.start();
270 }
271 }
272
Tej Singh9a1f75b2019-09-20 16:29:13 -0700273 private final static class AppUpdateReceiver extends BroadcastReceiver {
274 @Override
275 public void onReceive(Context context, Intent intent) {
276 /**
277 * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
278 * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
279 * If we can't find the value for EXTRA_REPLACING, we default to false.
280 */
281 if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
282 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
283 return; // Keep only replacing or normal add and remove.
284 }
Muhammad Qureshie7121622020-02-07 09:04:54 -0800285 if (DEBUG) Log.d(TAG, "StatsCompanionService noticed an app was updated.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700286 synchronized (sStatsdLock) {
287 if (sStatsd == null) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800288 Log.w(TAG, "Could not access statsd to inform it of an app update");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700289 return;
290 }
291 try {
292 if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
293 Bundle b = intent.getExtras();
294 int uid = b.getInt(Intent.EXTRA_UID);
295 boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
296 if (!replacing) {
297 // Don't bother sending an update if we're right about to get another
298 // intent for the new version that's added.
Tej Singh9a1f75b2019-09-20 16:29:13 -0700299 String app = intent.getData().getSchemeSpecificPart();
300 sStatsd.informOnePackageRemoved(app, uid);
301 }
302 } else {
303 PackageManager pm = context.getPackageManager();
304 Bundle b = intent.getExtras();
305 int uid = b.getInt(Intent.EXTRA_UID);
306 String app = intent.getData().getSchemeSpecificPart();
307 PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
308 String installer;
309 try {
310 installer = pm.getInstallerPackageName(app);
311 } catch (IllegalArgumentException e) {
312 installer = "";
313 }
Tony Makd04f7d52019-11-04 13:01:11 +0000314 sStatsd.informOnePackage(
315 app,
316 uid,
317 pi.getLongVersionCode(),
318 pi.versionName == null ? "" : pi.versionName,
Tej Singh9a1f75b2019-09-20 16:29:13 -0700319 installer == null ? "" : installer);
320 }
321 } catch (Exception e) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800322 Log.w(TAG, "Failed to inform statsd of an app update", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700323 }
324 }
325 }
326 }
327
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800328 private static final class UserUpdateReceiver extends BroadcastReceiver {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700329 @Override
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800330 public void onReceive(Context context, Intent intent) {
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700331 // Pull the latest state of UID->app name, version mapping.
332 // Needed since the new user basically has a version of every app.
333 informAllUids(context);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800334 }
335 }
336
Tej Singh9a1f75b2019-09-20 16:29:13 -0700337 public final static class PullingAlarmListener implements OnAlarmListener {
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700338 private final Context mContext;
339
340 PullingAlarmListener(Context context) {
341 mContext = context;
342 }
343
Tej Singh9a1f75b2019-09-20 16:29:13 -0700344 @Override
345 public void onAlarm() {
346 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800347 Log.d(TAG, "Time to poll something.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700348 }
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800349 IStatsd statsd = getStatsdNonblocking();
350 if (statsd == null) {
351 Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
352 return;
353 }
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700354
355 // Wakelock needs to be retained while calling statsd.
356 Thread thread = new WakelockThread(mContext,
357 PullingAlarmListener.class.getCanonicalName(), new Runnable() {
358 @Override
359 public void run() {
360 try {
361 statsd.informPollAlarmFired();
362 } catch (RemoteException e) {
363 Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
364 }
365 }
366 });
367 thread.start();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700368 }
369 }
370
371 public final static class PeriodicAlarmListener implements OnAlarmListener {
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700372 private final Context mContext;
373
374 PeriodicAlarmListener(Context context) {
375 mContext = context;
376 }
377
Tej Singh9a1f75b2019-09-20 16:29:13 -0700378 @Override
379 public void onAlarm() {
380 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800381 Log.d(TAG, "Time to trigger periodic alarm.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700382 }
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800383 IStatsd statsd = getStatsdNonblocking();
384 if (statsd == null) {
385 Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
386 return;
387 }
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700388
389 // Wakelock needs to be retained while calling statsd.
390 Thread thread = new WakelockThread(mContext,
391 PeriodicAlarmListener.class.getCanonicalName(), new Runnable() {
392 @Override
393 public void run() {
394 try {
395 statsd.informAlarmForSubscriberTriggeringFired();
396 } catch (RemoteException e) {
397 Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
398 }
399 }
400 });
401 thread.start();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700402 }
403 }
404
405 public final static class ShutdownEventReceiver extends BroadcastReceiver {
406 @Override
407 public void onReceive(Context context, Intent intent) {
408 /**
409 * Skip immediately if intent is not relevant to device shutdown.
410 */
411 if (!intent.getAction().equals(Intent.ACTION_REBOOT)
412 && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
413 && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
414 return;
415 }
416
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800417 if (DEBUG) {
418 Log.i(TAG, "StatsCompanionService noticed a shutdown.");
419 }
420 IStatsd statsd = getStatsdNonblocking();
421 if (statsd == null) {
422 Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
423 return;
424 }
425 try {
426 // two way binder call
427 statsd.informDeviceShutdown();
428 } catch (Exception e) {
429 Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700430 }
431 }
432 }
433
434 @Override // Binder call
Tej Singh9a1f75b2019-09-20 16:29:13 -0700435 public void setAlarmForSubscriberTriggering(long timestampMs) {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800436 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700437 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800438 Log.d(TAG,
Tej Singh9a1f75b2019-09-20 16:29:13 -0700439 "Setting periodic alarm in about " + (timestampMs
440 - SystemClock.elapsedRealtime()));
441 }
442 final long callingToken = Binder.clearCallingIdentity();
443 try {
444 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
445 // only fire when it awakens.
446 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
447 mPeriodicAlarmListener, mHandler);
448 } finally {
449 Binder.restoreCallingIdentity(callingToken);
450 }
451 }
452
453 @Override // Binder call
454 public void cancelAlarmForSubscriberTriggering() {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800455 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700456 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800457 Log.d(TAG, "Cancelling periodic alarm");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700458 }
459 final long callingToken = Binder.clearCallingIdentity();
460 try {
461 mAlarmManager.cancel(mPeriodicAlarmListener);
462 } finally {
463 Binder.restoreCallingIdentity(callingToken);
464 }
465 }
466
467 @Override // Binder call
468 public void setPullingAlarm(long nextPullTimeMs) {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800469 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700470 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800471 Log.d(TAG, "Setting pulling alarm in about "
Tej Singh9a1f75b2019-09-20 16:29:13 -0700472 + (nextPullTimeMs - SystemClock.elapsedRealtime()));
473 }
474 final long callingToken = Binder.clearCallingIdentity();
475 try {
476 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
477 // only fire when it awakens.
478 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
479 mPullingAlarmListener, mHandler);
480 } finally {
481 Binder.restoreCallingIdentity(callingToken);
482 }
483 }
484
485 @Override // Binder call
486 public void cancelPullingAlarm() {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800487 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700488 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800489 Log.d(TAG, "Cancelling pulling alarm");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700490 }
491 final long callingToken = Binder.clearCallingIdentity();
492 try {
493 mAlarmManager.cancel(mPullingAlarmListener);
494 } finally {
495 Binder.restoreCallingIdentity(callingToken);
496 }
497 }
498
Tej Singh9a1f75b2019-09-20 16:29:13 -0700499 @Override // Binder call
500 public void statsdReady() {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800501 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700502 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800503 Log.d(TAG, "learned that statsdReady");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700504 }
505 sayHiToStatsd(); // tell statsd that we're ready too and link to it
Muhammad Qureshiddfc1ba2020-02-19 13:41:59 -0800506
507 final Intent intent = new Intent(StatsManager.ACTION_STATSD_STARTED);
508 // Retrieve list of broadcast receivers for this broadcast & send them directed broadcasts
509 // to wake them up (if they're in background).
510 List<ResolveInfo> resolveInfos =
511 mContext.getPackageManager().queryBroadcastReceiversAsUser(
512 intent, 0, UserHandle.SYSTEM);
513 if (resolveInfos == null || resolveInfos.isEmpty()) {
514 return; // No need to send broadcast.
515 }
516
517 for (ResolveInfo resolveInfo : resolveInfos) {
518 Intent intentToSend = new Intent(intent);
519 intentToSend.setComponent(new ComponentName(
520 resolveInfo.activityInfo.applicationInfo.packageName,
521 resolveInfo.activityInfo.name));
522 mContext.sendBroadcastAsUser(intentToSend, UserHandle.SYSTEM,
523 android.Manifest.permission.DUMP);
524 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700525 }
526
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800527 @Override // Binder call
Ruchir Rastogi9c65baf2020-01-28 17:43:13 -0800528 public boolean checkPermission(String permission, int pid, int uid) {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800529 StatsCompanion.enforceStatsdCallingUid();
Ruchir Rastogi9c65baf2020-01-28 17:43:13 -0800530 return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
531 }
Tej Singh774d6cd2019-12-05 20:36:54 -0800532
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800533 // Statsd related code
Tej Singh9a1f75b2019-09-20 16:29:13 -0700534
535 /**
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700536 * Fetches the statsd IBinder service. This is a blocking call that always refetches statsd
537 * instead of returning the cached sStatsd.
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800538 * Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
539 * the cached sStatsd via {@link #getStatsdNonblocking()}.
Tej Singh9a1f75b2019-09-20 16:29:13 -0700540 */
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700541 private IStatsd fetchStatsdServiceLocked() {
542 sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
543 .getStatsServiceManager()
544 .getStatsdServiceRegisterer()
545 .get());
546 return sStatsd;
547 }
548
549 private void registerStatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
550 StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient(statsd, receivers);
551
552 try {
553 statsd.asBinder().linkToDeath(deathRecipient, /*flags=*/0);
554 } catch (RemoteException e) {
555 Log.e(TAG, "linkToDeath (StatsdDeathRecipient) failed");
556 // Statsd has already died. Unregister receivers ourselves.
557 for (BroadcastReceiver receiver : receivers) {
558 mContext.unregisterReceiver(receiver);
559 }
560 synchronized (sStatsdLock) {
561 if (statsd == sStatsd) {
562 statsdNotReadyLocked();
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800563 }
564 }
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800565 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700566 }
567
Tej Singh9a1f75b2019-09-20 16:29:13 -0700568 /**
569 * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
570 */
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800571 void systemReady() {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800572 if (DEBUG) Log.d(TAG, "Learned that systemReady");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700573 sayHiToStatsd();
574 }
575
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800576 void setStatsManagerService(StatsManagerService statsManagerService) {
577 mStatsManagerService = statsManagerService;
578 }
579
Tej Singh9a1f75b2019-09-20 16:29:13 -0700580 /**
581 * Tells statsd that statscompanion is ready. If the binder call returns, link to
582 * statsd.
583 */
584 private void sayHiToStatsd() {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700585 IStatsd statsd;
586 synchronized (sStatsdLock) {
587 if (sStatsd != null && sStatsd.asBinder().isBinderAlive()) {
588 Log.e(TAG, "statsd has already been fetched before",
589 new IllegalStateException("IStatsd object should be null or dead"));
590 return;
591 }
592 statsd = fetchStatsdServiceLocked();
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800593 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700594
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800595 if (statsd == null) {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700596 Log.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800597 return;
598 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700599
Stanislav Zholnin99a16c02020-05-18 02:37:23 +0100600 // Cleann up from previous statsd - cancel any alarms that had been set. Do this here
601 // instead of in binder death because statsd can come back and set different alarms, or not
602 // want to set an alarm when it had been set. This guarantees that when we get a new statsd,
603 // we cancel any alarms before it is able to set them.
Stanislav Zholnin99a16c02020-05-18 02:37:23 +0100604 cancelPullingAlarm();
605 cancelAlarmForSubscriberTriggering();
606
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800607 if (DEBUG) Log.d(TAG, "Saying hi to statsd");
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700608 mStatsManagerService.statsdReady(statsd);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800609 try {
610 statsd.statsCompanionReady();
611
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800612 BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver();
613 BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver();
614 BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver();
615
616 // Setup broadcast receiver for updates.
617 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
618 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
619 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
620 filter.addDataScheme("package");
621 mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
622
623 // Setup receiver for user initialize (which happens once for a new user)
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700624 // and if a user is removed.
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800625 filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
626 filter.addAction(Intent.ACTION_USER_REMOVED);
627 mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
628
629 // Setup receiver for device reboots or shutdowns.
630 filter = new IntentFilter(Intent.ACTION_REBOOT);
631 filter.addAction(Intent.ACTION_SHUTDOWN);
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700632 mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, null);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800633
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700634 // Register death recipient.
635 List<BroadcastReceiver> broadcastReceivers =
636 List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver);
637 registerStatsdDeathRecipient(statsd, broadcastReceivers);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800638
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700639 // Tell statsd that boot has completed. The signal may have already been sent, but since
640 // the signal-receiving function is idempotent, that's ok.
641 if (mBootCompleted.get()) {
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700642 statsd.bootCompleted();
643 }
644
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700645 // Pull the latest state of UID->app name, version mapping when statsd starts.
Jeffrey Huangbde54392020-04-10 17:40:59 -0700646 informAllUids(mContext);
647
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800648 Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
649 } catch (RemoteException e) {
650 Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700651 }
652 }
653
654 private class StatsdDeathRecipient implements IBinder.DeathRecipient {
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800655
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700656 private final IStatsd mStatsd;
657 private final List<BroadcastReceiver> mReceiversToUnregister;
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800658
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700659 StatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
660 mStatsd = statsd;
661 mReceiversToUnregister = receivers;
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800662 }
663
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700664 // It is possible for binderDied to be called after a restarted statsd calls statsdReady,
665 // but that's alright because the code does not assume an ordering of the two calls.
Tej Singh9a1f75b2019-09-20 16:29:13 -0700666 @Override
667 public void binderDied() {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800668 Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700669 synchronized (sStatsdLock) {
670 long now = SystemClock.elapsedRealtime();
671 for (Long timeMillis : mDeathTimeMillis) {
672 long ageMillis = now - timeMillis;
673 if (ageMillis > MILLIS_IN_A_DAY) {
674 mDeathTimeMillis.remove(timeMillis);
675 }
676 }
677 for (Long timeMillis : mDeletedFiles.keySet()) {
678 long ageMillis = now - timeMillis;
679 if (ageMillis > MILLIS_IN_A_DAY * 7) {
680 mDeletedFiles.remove(timeMillis);
681 }
682 }
683 mDeathTimeMillis.add(now);
684 if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
685 mDeathTimeMillis.clear();
Jeffrey Huangca9eb672020-01-22 09:59:00 -0800686 File[] configs = new File(CONFIG_DIR).listFiles();
687 if (configs != null && configs.length > 0) {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700688 String fileName = configs[0].getName();
689 if (configs[0].delete()) {
690 mDeletedFiles.put(now, fileName);
691 }
692 }
693 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700694
695 // Unregister receivers on death because receivers can only be unregistered once.
696 // Otherwise, an IllegalArgumentException is thrown.
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800697 for (BroadcastReceiver receiver: mReceiversToUnregister) {
698 mContext.unregisterReceiver(receiver);
699 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700700
701 // It's possible for statsd to have restarted and called statsdReady, causing a new
702 // sStatsd binder object to be fetched, before the binderDied callback runs. Only
703 // call #statsdNotReadyLocked if that hasn't happened yet.
704 if (mStatsd == sStatsd) {
705 statsdNotReadyLocked();
706 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700707 }
708 }
709 }
710
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800711 private void statsdNotReadyLocked() {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700712 sStatsd = null;
Jeffrey Huangdf5eea22019-12-16 13:50:06 -0800713 mStatsManagerService.statsdNotReady();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700714 }
715
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700716 void bootCompleted() {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700717 mBootCompleted.set(true);
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700718 IStatsd statsd = getStatsdNonblocking();
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700719 if (statsd == null) {
720 // Statsd is not yet ready.
721 // Delay the boot completed ping to {@link #sayHiToStatsd()}
722 return;
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700723 }
724 try {
725 statsd.bootCompleted();
726 } catch (RemoteException e) {
727 Log.e(TAG, "Failed to notify statsd that boot completed");
728 }
729 }
730
Tej Singh9a1f75b2019-09-20 16:29:13 -0700731 @Override
732 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800733 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
734 != PackageManager.PERMISSION_GRANTED) {
735 return;
736 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700737
738 synchronized (sStatsdLock) {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700739 writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
Tej Singh9a1f75b2019-09-20 16:29:13 -0700740 if (mDeletedFiles.size() > 0) {
741 writer.println(" timestamp, deleted file name");
742 }
743 long lastBootMillis =
744 SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
745 for (Long elapsedMillis : mDeletedFiles.keySet()) {
746 long deletionMillis = lastBootMillis + elapsedMillis;
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700747 writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
Tej Singh9a1f75b2019-09-20 16:29:13 -0700748 }
749 }
750 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700751}