blob: fbda86f9ce224b55c14720d6490d5a4060d1af9e [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;
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -070057import java.util.ArrayList;
Tej Singh9a1f75b2019-09-20 16:29:13 -070058import java.util.HashMap;
59import java.util.HashSet;
60import java.util.List;
Tej Singh9a1f75b2019-09-20 16:29:13 -070061import java.util.concurrent.TimeUnit;
Ruchir Rastogi1fc17992020-04-22 15:37:32 -070062import java.util.concurrent.atomic.AtomicBoolean;
Tej Singh9a1f75b2019-09-20 16:29:13 -070063
64/**
65 * Helper service for statsd (the native stats management service in cmds/statsd/).
66 * Used for registering and receiving alarms on behalf of statsd.
67 *
68 * @hide
69 */
70public class StatsCompanionService extends IStatsCompanionService.Stub {
Jeffrey Huang2b241252020-01-23 14:13:25 -080071
Tej Singh9a1f75b2019-09-20 16:29:13 -070072 private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
73
74 public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
75 public static final String CONFIG_DIR = "/data/misc/stats-service";
76
77 static final String TAG = "StatsCompanionService";
78 static final boolean DEBUG = false;
79 /**
80 * Hard coded field ids of frameworks/base/cmds/statsd/src/uid_data.proto
81 * to be used in ProtoOutputStream.
82 */
83 private static final int APPLICATION_INFO_FIELD_ID = 1;
84 private static final int UID_FIELD_ID = 1;
85 private static final int VERSION_FIELD_ID = 2;
86 private static final int VERSION_STRING_FIELD_ID = 3;
87 private static final int PACKAGE_NAME_FIELD_ID = 4;
88 private static final int INSTALLER_FIELD_ID = 5;
89
Tej Singh9a1f75b2019-09-20 16:29:13 -070090 public static final int DEATH_THRESHOLD = 10;
Tej Singh9a1f75b2019-09-20 16:29:13 -070091
92 static final class CompanionHandler extends Handler {
93 CompanionHandler(Looper looper) {
94 super(looper);
95 }
96 }
97
98 private final Context mContext;
99 private final AlarmManager mAlarmManager;
Tej Singh9a1f75b2019-09-20 16:29:13 -0700100 @GuardedBy("sStatsdLock")
Jeffrey Huang9d2dbad2019-12-11 14:47:32 -0800101 private static IStatsd sStatsd;
Tej Singh9a1f75b2019-09-20 16:29:13 -0700102 private static final Object sStatsdLock = new Object();
103
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700104 private final OnAlarmListener mPullingAlarmListener;
105 private final OnAlarmListener mPeriodicAlarmListener;
Tej Singhb877e352019-10-11 11:07:06 -0700106
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800107 private StatsManagerService mStatsManagerService;
108
Tej Singh9a1f75b2019-09-20 16:29:13 -0700109 @GuardedBy("sStatsdLock")
110 private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
111 @GuardedBy("sStatsdLock")
112 private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
113 private final CompanionHandler mHandler;
114
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700115 // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle.
116 private AtomicBoolean mBootCompleted = new AtomicBoolean(false);
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700117
Tej Singh9a1f75b2019-09-20 16:29:13 -0700118 public StatsCompanionService(Context context) {
119 super();
120 mContext = context;
121 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
Muhammad Qureshie7121622020-02-07 09:04:54 -0800122 if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700123 HandlerThread handlerThread = new HandlerThread(TAG);
124 handlerThread.start();
125 mHandler = new CompanionHandler(handlerThread.getLooper());
126
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700127 mPullingAlarmListener = new PullingAlarmListener(context);
128 mPeriodicAlarmListener = new PeriodicAlarmListener(context);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700129 }
130
Tej Singh9a1f75b2019-09-20 16:29:13 -0700131 private final static int[] toIntArray(List<Integer> list) {
132 int[] ret = new int[list.size()];
133 for (int i = 0; i < ret.length; i++) {
134 ret[i] = list.get(i);
135 }
136 return ret;
137 }
138
139 private final static long[] toLongArray(List<Long> list) {
140 long[] ret = new long[list.size()];
141 for (int i = 0; i < ret.length; i++) {
142 ret[i] = list.get(i);
143 }
144 return ret;
145 }
146
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800147 /**
148 * Non-blocking call to retrieve a reference to statsd
149 *
150 * @return IStatsd object if statsd is ready, null otherwise.
151 */
152 private static IStatsd getStatsdNonblocking() {
153 synchronized (sStatsdLock) {
154 return sStatsd;
155 }
156 }
157
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700158 private static void informAllUids(Context context) {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700159 ParcelFileDescriptor[] fds;
160 try {
161 fds = ParcelFileDescriptor.createPipe();
162 } catch (IOException e) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800163 Log.e(TAG, "Failed to create a pipe to send uid map data.", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700164 return;
165 }
Jeffrey Huang447a0782019-11-26 15:10:13 -0800166 HandlerThread backgroundThread = new HandlerThread(
167 "statsCompanionService.bg", THREAD_PRIORITY_BACKGROUND);
168 backgroundThread.start();
169 Handler handler = new Handler(backgroundThread.getLooper());
170 handler.post(() -> {
Jeffrey Huangbde54392020-04-10 17:40:59 -0700171 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
172 PackageManager pm = context.getPackageManager();
173 final List<UserHandle> users = um.getUserHandles(true);
174 if (DEBUG) {
175 Log.d(TAG, "Iterating over " + users.size() + " userHandles.");
176 }
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700177 IStatsd statsd = getStatsdNonblocking();
178 if (statsd == null) {
179 return;
180 }
181 try {
182 statsd.informAllUidData(fds[0]);
183 } catch (RemoteException e) {
184 Log.e(TAG, "Failed to send uid map to statsd");
185 }
186 try {
187 fds[0].close();
188 } catch (IOException e) {
189 Log.e(TAG, "Failed to close the read side of the pipe.", e);
190 }
191 final ParcelFileDescriptor writeFd = fds[1];
Tej Singh9a1f75b2019-09-20 16:29:13 -0700192 FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
193 try {
194 ProtoOutputStream output = new ProtoOutputStream(fout);
195 int numRecords = 0;
196 // Add in all the apps for every user/profile.
Jeffrey Huang48216742019-12-19 13:02:11 -0800197 for (UserHandle userHandle : users) {
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700198 List<PackageInfo> packagesPlusApex = getAllPackagesWithApex(pm, userHandle);
199 for (int j = 0; j < packagesPlusApex.size(); j++) {
200 if (packagesPlusApex.get(j).applicationInfo != null) {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700201 String installer;
202 try {
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700203 installer = pm.getInstallerPackageName(
204 packagesPlusApex.get(j).packageName);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700205 } catch (IllegalArgumentException e) {
206 installer = "";
207 }
208 long applicationInfoToken =
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800209 output.start(ProtoOutputStream.FIELD_TYPE_MESSAGE
210 | ProtoOutputStream.FIELD_COUNT_REPEATED
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700211 | APPLICATION_INFO_FIELD_ID);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800212 output.write(ProtoOutputStream.FIELD_TYPE_INT32
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700213 | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
214 packagesPlusApex.get(j).applicationInfo.uid);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800215 output.write(ProtoOutputStream.FIELD_TYPE_INT64
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700216 | ProtoOutputStream.FIELD_COUNT_SINGLE
217 | VERSION_FIELD_ID,
218 packagesPlusApex.get(j).getLongVersionCode());
219 output.write(ProtoOutputStream.FIELD_TYPE_STRING
220 | ProtoOutputStream.FIELD_COUNT_SINGLE
221 | VERSION_STRING_FIELD_ID,
222 packagesPlusApex.get(j).versionName);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800223 output.write(ProtoOutputStream.FIELD_TYPE_STRING
224 | ProtoOutputStream.FIELD_COUNT_SINGLE
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700225 | PACKAGE_NAME_FIELD_ID, packagesPlusApex.get(j).packageName);
Jeffrey Huangf08724e2019-12-02 13:31:57 -0800226 output.write(ProtoOutputStream.FIELD_TYPE_STRING
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700227 | ProtoOutputStream.FIELD_COUNT_SINGLE
Tej Singh9a1f75b2019-09-20 16:29:13 -0700228 | INSTALLER_FIELD_ID,
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700229 installer == null ? "" : installer);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700230 numRecords++;
231 output.end(applicationInfoToken);
232 }
233 }
234 }
235 output.flush();
236 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800237 Log.d(TAG, "Sent data for " + numRecords + " apps");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700238 }
239 } finally {
Anton Hanssond0ab8c92020-03-19 15:40:36 +0000240 FileUtils.closeQuietly(fout);
Jeffrey Huang447a0782019-11-26 15:10:13 -0800241 backgroundThread.quit();
242 backgroundThread.interrupt();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700243 }
244 });
245 }
246
Jeffrey Huanga0d48eb2020-10-26 14:27:48 -0700247 private static List<PackageInfo> getAllPackagesWithApex(PackageManager pm,
248 UserHandle userHandle) {
249 // We want all the uninstalled packages because uninstalled package uids can still be logged
250 // to statsd.
251 List<PackageInfo> allPackages = new ArrayList<>(
252 pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
253 | PackageManager.MATCH_ANY_USER,
254 userHandle.getIdentifier()));
255 // We make a second query to package manager for the apex modules because package manager
256 // returns both installed and uninstalled apexes with
257 // PackageManager.MATCH_UNINSTALLED_PACKAGES flag. We only want active apexes because
258 // inactive apexes can conflict with active ones.
259 for (PackageInfo packageInfo : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
260 if (packageInfo.isApex) {
261 allPackages.add(packageInfo);
262 }
263 }
264 return allPackages;
265 }
266
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700267 private static class WakelockThread extends Thread {
268 private final PowerManager.WakeLock mWl;
269 private final Runnable mRunnable;
270
271 WakelockThread(Context context, String wakelockName, Runnable runnable) {
272 PowerManager powerManager = (PowerManager)
273 context.getSystemService(Context.POWER_SERVICE);
274 mWl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakelockName);
275 mRunnable = runnable;
276 }
277 @Override
278 public void run() {
279 try {
280 mRunnable.run();
281 } finally {
282 mWl.release();
283 }
284 }
285 @Override
286 public void start() {
287 mWl.acquire();
288 super.start();
289 }
290 }
291
Tej Singh9a1f75b2019-09-20 16:29:13 -0700292 private final static class AppUpdateReceiver extends BroadcastReceiver {
293 @Override
294 public void onReceive(Context context, Intent intent) {
295 /**
296 * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
297 * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
298 * If we can't find the value for EXTRA_REPLACING, we default to false.
299 */
300 if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
301 && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
302 return; // Keep only replacing or normal add and remove.
303 }
Muhammad Qureshie7121622020-02-07 09:04:54 -0800304 if (DEBUG) Log.d(TAG, "StatsCompanionService noticed an app was updated.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700305 synchronized (sStatsdLock) {
306 if (sStatsd == null) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800307 Log.w(TAG, "Could not access statsd to inform it of an app update");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700308 return;
309 }
310 try {
311 if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
312 Bundle b = intent.getExtras();
313 int uid = b.getInt(Intent.EXTRA_UID);
314 boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
315 if (!replacing) {
316 // Don't bother sending an update if we're right about to get another
317 // intent for the new version that's added.
Tej Singh9a1f75b2019-09-20 16:29:13 -0700318 String app = intent.getData().getSchemeSpecificPart();
319 sStatsd.informOnePackageRemoved(app, uid);
320 }
321 } else {
322 PackageManager pm = context.getPackageManager();
323 Bundle b = intent.getExtras();
324 int uid = b.getInt(Intent.EXTRA_UID);
325 String app = intent.getData().getSchemeSpecificPart();
326 PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
327 String installer;
328 try {
329 installer = pm.getInstallerPackageName(app);
330 } catch (IllegalArgumentException e) {
331 installer = "";
332 }
Tony Makd04f7d52019-11-04 13:01:11 +0000333 sStatsd.informOnePackage(
334 app,
335 uid,
336 pi.getLongVersionCode(),
337 pi.versionName == null ? "" : pi.versionName,
Tej Singh9a1f75b2019-09-20 16:29:13 -0700338 installer == null ? "" : installer);
339 }
340 } catch (Exception e) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800341 Log.w(TAG, "Failed to inform statsd of an app update", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700342 }
343 }
344 }
345 }
346
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800347 private static final class UserUpdateReceiver extends BroadcastReceiver {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700348 @Override
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800349 public void onReceive(Context context, Intent intent) {
Jeffrey Huang1643a5e2020-03-31 16:43:32 -0700350 // Pull the latest state of UID->app name, version mapping.
351 // Needed since the new user basically has a version of every app.
352 informAllUids(context);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800353 }
354 }
355
Tej Singh9a1f75b2019-09-20 16:29:13 -0700356 public final static class PullingAlarmListener implements OnAlarmListener {
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700357 private final Context mContext;
358
359 PullingAlarmListener(Context context) {
360 mContext = context;
361 }
362
Tej Singh9a1f75b2019-09-20 16:29:13 -0700363 @Override
364 public void onAlarm() {
365 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800366 Log.d(TAG, "Time to poll something.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700367 }
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800368 IStatsd statsd = getStatsdNonblocking();
369 if (statsd == null) {
370 Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
371 return;
372 }
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700373
374 // Wakelock needs to be retained while calling statsd.
375 Thread thread = new WakelockThread(mContext,
376 PullingAlarmListener.class.getCanonicalName(), new Runnable() {
377 @Override
378 public void run() {
379 try {
380 statsd.informPollAlarmFired();
381 } catch (RemoteException e) {
382 Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
383 }
384 }
385 });
386 thread.start();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700387 }
388 }
389
390 public final static class PeriodicAlarmListener implements OnAlarmListener {
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700391 private final Context mContext;
392
393 PeriodicAlarmListener(Context context) {
394 mContext = context;
395 }
396
Tej Singh9a1f75b2019-09-20 16:29:13 -0700397 @Override
398 public void onAlarm() {
399 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800400 Log.d(TAG, "Time to trigger periodic alarm.");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700401 }
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800402 IStatsd statsd = getStatsdNonblocking();
403 if (statsd == null) {
404 Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
405 return;
406 }
Jeffrey Huang3d19ddb2020-03-11 14:58:13 -0700407
408 // Wakelock needs to be retained while calling statsd.
409 Thread thread = new WakelockThread(mContext,
410 PeriodicAlarmListener.class.getCanonicalName(), new Runnable() {
411 @Override
412 public void run() {
413 try {
414 statsd.informAlarmForSubscriberTriggeringFired();
415 } catch (RemoteException e) {
416 Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
417 }
418 }
419 });
420 thread.start();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700421 }
422 }
423
424 public final static class ShutdownEventReceiver extends BroadcastReceiver {
425 @Override
426 public void onReceive(Context context, Intent intent) {
427 /**
428 * Skip immediately if intent is not relevant to device shutdown.
429 */
430 if (!intent.getAction().equals(Intent.ACTION_REBOOT)
431 && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
432 && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
433 return;
434 }
435
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800436 if (DEBUG) {
437 Log.i(TAG, "StatsCompanionService noticed a shutdown.");
438 }
439 IStatsd statsd = getStatsdNonblocking();
440 if (statsd == null) {
441 Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
442 return;
443 }
444 try {
445 // two way binder call
446 statsd.informDeviceShutdown();
447 } catch (Exception e) {
448 Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700449 }
450 }
451 }
452
453 @Override // Binder call
Tej Singh9a1f75b2019-09-20 16:29:13 -0700454 public void setAlarmForSubscriberTriggering(long timestampMs) {
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,
Tej Singh9a1f75b2019-09-20 16:29:13 -0700458 "Setting periodic alarm in about " + (timestampMs
459 - SystemClock.elapsedRealtime()));
460 }
461 final long callingToken = Binder.clearCallingIdentity();
462 try {
463 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
464 // only fire when it awakens.
465 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
466 mPeriodicAlarmListener, mHandler);
467 } finally {
468 Binder.restoreCallingIdentity(callingToken);
469 }
470 }
471
472 @Override // Binder call
473 public void cancelAlarmForSubscriberTriggering() {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800474 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700475 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800476 Log.d(TAG, "Cancelling periodic alarm");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700477 }
478 final long callingToken = Binder.clearCallingIdentity();
479 try {
480 mAlarmManager.cancel(mPeriodicAlarmListener);
481 } finally {
482 Binder.restoreCallingIdentity(callingToken);
483 }
484 }
485
486 @Override // Binder call
487 public void setPullingAlarm(long nextPullTimeMs) {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800488 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700489 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800490 Log.d(TAG, "Setting pulling alarm in about "
Tej Singh9a1f75b2019-09-20 16:29:13 -0700491 + (nextPullTimeMs - SystemClock.elapsedRealtime()));
492 }
493 final long callingToken = Binder.clearCallingIdentity();
494 try {
495 // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
496 // only fire when it awakens.
497 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
498 mPullingAlarmListener, mHandler);
499 } finally {
500 Binder.restoreCallingIdentity(callingToken);
501 }
502 }
503
504 @Override // Binder call
505 public void cancelPullingAlarm() {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800506 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700507 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800508 Log.d(TAG, "Cancelling pulling alarm");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700509 }
510 final long callingToken = Binder.clearCallingIdentity();
511 try {
512 mAlarmManager.cancel(mPullingAlarmListener);
513 } finally {
514 Binder.restoreCallingIdentity(callingToken);
515 }
516 }
517
Tej Singh9a1f75b2019-09-20 16:29:13 -0700518 @Override // Binder call
519 public void statsdReady() {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800520 StatsCompanion.enforceStatsdCallingUid();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700521 if (DEBUG) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800522 Log.d(TAG, "learned that statsdReady");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700523 }
524 sayHiToStatsd(); // tell statsd that we're ready too and link to it
Muhammad Qureshiddfc1ba2020-02-19 13:41:59 -0800525
526 final Intent intent = new Intent(StatsManager.ACTION_STATSD_STARTED);
527 // Retrieve list of broadcast receivers for this broadcast & send them directed broadcasts
528 // to wake them up (if they're in background).
529 List<ResolveInfo> resolveInfos =
530 mContext.getPackageManager().queryBroadcastReceiversAsUser(
531 intent, 0, UserHandle.SYSTEM);
532 if (resolveInfos == null || resolveInfos.isEmpty()) {
533 return; // No need to send broadcast.
534 }
535
536 for (ResolveInfo resolveInfo : resolveInfos) {
537 Intent intentToSend = new Intent(intent);
538 intentToSend.setComponent(new ComponentName(
539 resolveInfo.activityInfo.applicationInfo.packageName,
540 resolveInfo.activityInfo.name));
541 mContext.sendBroadcastAsUser(intentToSend, UserHandle.SYSTEM,
542 android.Manifest.permission.DUMP);
543 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700544 }
545
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800546 @Override // Binder call
Ruchir Rastogi9c65baf2020-01-28 17:43:13 -0800547 public boolean checkPermission(String permission, int pid, int uid) {
Jeffrey Huang3dea5392020-02-05 13:57:20 -0800548 StatsCompanion.enforceStatsdCallingUid();
Ruchir Rastogi9c65baf2020-01-28 17:43:13 -0800549 return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
550 }
Tej Singh774d6cd2019-12-05 20:36:54 -0800551
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800552 // Statsd related code
Tej Singh9a1f75b2019-09-20 16:29:13 -0700553
554 /**
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700555 * Fetches the statsd IBinder service. This is a blocking call that always refetches statsd
556 * instead of returning the cached sStatsd.
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800557 * Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
558 * the cached sStatsd via {@link #getStatsdNonblocking()}.
Tej Singh9a1f75b2019-09-20 16:29:13 -0700559 */
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700560 private IStatsd fetchStatsdServiceLocked() {
561 sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
562 .getStatsServiceManager()
563 .getStatsdServiceRegisterer()
564 .get());
565 return sStatsd;
566 }
567
568 private void registerStatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
569 StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient(statsd, receivers);
570
571 try {
572 statsd.asBinder().linkToDeath(deathRecipient, /*flags=*/0);
573 } catch (RemoteException e) {
574 Log.e(TAG, "linkToDeath (StatsdDeathRecipient) failed");
575 // Statsd has already died. Unregister receivers ourselves.
576 for (BroadcastReceiver receiver : receivers) {
577 mContext.unregisterReceiver(receiver);
578 }
579 synchronized (sStatsdLock) {
580 if (statsd == sStatsd) {
581 statsdNotReadyLocked();
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800582 }
583 }
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800584 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700585 }
586
Tej Singh9a1f75b2019-09-20 16:29:13 -0700587 /**
588 * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
589 */
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800590 void systemReady() {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800591 if (DEBUG) Log.d(TAG, "Learned that systemReady");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700592 sayHiToStatsd();
593 }
594
Jeffrey Huang3b3304e2019-12-12 10:56:24 -0800595 void setStatsManagerService(StatsManagerService statsManagerService) {
596 mStatsManagerService = statsManagerService;
597 }
598
Tej Singh9a1f75b2019-09-20 16:29:13 -0700599 /**
600 * Tells statsd that statscompanion is ready. If the binder call returns, link to
601 * statsd.
602 */
603 private void sayHiToStatsd() {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700604 IStatsd statsd;
605 synchronized (sStatsdLock) {
606 if (sStatsd != null && sStatsd.asBinder().isBinderAlive()) {
607 Log.e(TAG, "statsd has already been fetched before",
608 new IllegalStateException("IStatsd object should be null or dead"));
609 return;
610 }
611 statsd = fetchStatsdServiceLocked();
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800612 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700613
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800614 if (statsd == null) {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700615 Log.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800616 return;
617 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700618
Stanislav Zholnin99a16c02020-05-18 02:37:23 +0100619 // Cleann up from previous statsd - cancel any alarms that had been set. Do this here
620 // instead of in binder death because statsd can come back and set different alarms, or not
621 // want to set an alarm when it had been set. This guarantees that when we get a new statsd,
622 // we cancel any alarms before it is able to set them.
Stanislav Zholnin99a16c02020-05-18 02:37:23 +0100623 cancelPullingAlarm();
624 cancelAlarmForSubscriberTriggering();
625
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800626 if (DEBUG) Log.d(TAG, "Saying hi to statsd");
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700627 mStatsManagerService.statsdReady(statsd);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800628 try {
629 statsd.statsCompanionReady();
630
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800631 BroadcastReceiver appUpdateReceiver = new AppUpdateReceiver();
632 BroadcastReceiver userUpdateReceiver = new UserUpdateReceiver();
633 BroadcastReceiver shutdownEventReceiver = new ShutdownEventReceiver();
634
635 // Setup broadcast receiver for updates.
636 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
637 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
638 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
639 filter.addDataScheme("package");
640 mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
641
642 // Setup receiver for user initialize (which happens once for a new user)
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700643 // and if a user is removed.
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800644 filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
645 filter.addAction(Intent.ACTION_USER_REMOVED);
646 mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
647
648 // Setup receiver for device reboots or shutdowns.
649 filter = new IntentFilter(Intent.ACTION_REBOOT);
650 filter.addAction(Intent.ACTION_SHUTDOWN);
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700651 mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, null);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800652
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700653 // Register death recipient.
654 List<BroadcastReceiver> broadcastReceivers =
655 List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver);
656 registerStatsdDeathRecipient(statsd, broadcastReceivers);
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800657
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700658 // Tell statsd that boot has completed. The signal may have already been sent, but since
659 // the signal-receiving function is idempotent, that's ok.
660 if (mBootCompleted.get()) {
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700661 statsd.bootCompleted();
662 }
663
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700664 // Pull the latest state of UID->app name, version mapping when statsd starts.
Jeffrey Huangbde54392020-04-10 17:40:59 -0700665 informAllUids(mContext);
666
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800667 Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
668 } catch (RemoteException e) {
669 Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
Tej Singh9a1f75b2019-09-20 16:29:13 -0700670 }
671 }
672
673 private class StatsdDeathRecipient implements IBinder.DeathRecipient {
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800674
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700675 private final IStatsd mStatsd;
676 private final List<BroadcastReceiver> mReceiversToUnregister;
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800677
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700678 StatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
679 mStatsd = statsd;
680 mReceiversToUnregister = receivers;
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800681 }
682
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700683 // It is possible for binderDied to be called after a restarted statsd calls statsdReady,
684 // but that's alright because the code does not assume an ordering of the two calls.
Tej Singh9a1f75b2019-09-20 16:29:13 -0700685 @Override
686 public void binderDied() {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800687 Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
Tej Singh9a1f75b2019-09-20 16:29:13 -0700688 synchronized (sStatsdLock) {
689 long now = SystemClock.elapsedRealtime();
690 for (Long timeMillis : mDeathTimeMillis) {
691 long ageMillis = now - timeMillis;
692 if (ageMillis > MILLIS_IN_A_DAY) {
693 mDeathTimeMillis.remove(timeMillis);
694 }
695 }
696 for (Long timeMillis : mDeletedFiles.keySet()) {
697 long ageMillis = now - timeMillis;
698 if (ageMillis > MILLIS_IN_A_DAY * 7) {
699 mDeletedFiles.remove(timeMillis);
700 }
701 }
702 mDeathTimeMillis.add(now);
703 if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
704 mDeathTimeMillis.clear();
Jeffrey Huangca9eb672020-01-22 09:59:00 -0800705 File[] configs = new File(CONFIG_DIR).listFiles();
706 if (configs != null && configs.length > 0) {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700707 String fileName = configs[0].getName();
708 if (configs[0].delete()) {
709 mDeletedFiles.put(now, fileName);
710 }
711 }
712 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700713
714 // Unregister receivers on death because receivers can only be unregistered once.
715 // Otherwise, an IllegalArgumentException is thrown.
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800716 for (BroadcastReceiver receiver: mReceiversToUnregister) {
717 mContext.unregisterReceiver(receiver);
718 }
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700719
720 // It's possible for statsd to have restarted and called statsdReady, causing a new
721 // sStatsd binder object to be fetched, before the binderDied callback runs. Only
722 // call #statsdNotReadyLocked if that hasn't happened yet.
723 if (mStatsd == sStatsd) {
724 statsdNotReadyLocked();
725 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700726 }
727 }
728 }
729
Jeffrey Huangbb01ba92020-02-26 15:41:11 -0800730 private void statsdNotReadyLocked() {
Tej Singh9a1f75b2019-09-20 16:29:13 -0700731 sStatsd = null;
Jeffrey Huangdf5eea22019-12-16 13:50:06 -0800732 mStatsManagerService.statsdNotReady();
Tej Singh9a1f75b2019-09-20 16:29:13 -0700733 }
734
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700735 void bootCompleted() {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700736 mBootCompleted.set(true);
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700737 IStatsd statsd = getStatsdNonblocking();
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700738 if (statsd == null) {
739 // Statsd is not yet ready.
740 // Delay the boot completed ping to {@link #sayHiToStatsd()}
741 return;
Jeffrey Huang9ebb5722020-04-06 18:09:55 -0700742 }
743 try {
744 statsd.bootCompleted();
745 } catch (RemoteException e) {
746 Log.e(TAG, "Failed to notify statsd that boot completed");
747 }
748 }
749
Tej Singh9a1f75b2019-09-20 16:29:13 -0700750 @Override
751 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Muhammad Qureshie7121622020-02-07 09:04:54 -0800752 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
753 != PackageManager.PERMISSION_GRANTED) {
754 return;
755 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700756
757 synchronized (sStatsdLock) {
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700758 writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
Tej Singh9a1f75b2019-09-20 16:29:13 -0700759 if (mDeletedFiles.size() > 0) {
760 writer.println(" timestamp, deleted file name");
761 }
762 long lastBootMillis =
763 SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
764 for (Long elapsedMillis : mDeletedFiles.keySet()) {
765 long deletionMillis = lastBootMillis + elapsedMillis;
Ruchir Rastogi1fc17992020-04-22 15:37:32 -0700766 writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
Tej Singh9a1f75b2019-09-20 16:29:13 -0700767 }
768 }
769 }
Tej Singh9a1f75b2019-09-20 16:29:13 -0700770}