• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2020 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 
17 package com.android.server.profcollect;
18 
19 import static android.content.Intent.ACTION_BATTERY_LOW;
20 import static android.content.Intent.ACTION_BATTERY_OKAY;
21 import static android.content.Intent.ACTION_SCREEN_OFF;
22 import static android.content.Intent.ACTION_SCREEN_ON;
23 
24 import android.Manifest;
25 import android.annotation.RequiresPermission;
26 import android.app.job.JobInfo;
27 import android.app.job.JobParameters;
28 import android.app.job.JobScheduler;
29 import android.app.job.JobService;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.hardware.camera2.CameraManager;
36 import android.hardware.usb.UsbManager;
37 import android.os.Handler;
38 import android.os.IBinder.DeathRecipient;
39 import android.os.Looper;
40 import android.os.PowerManager;
41 import android.os.RemoteException;
42 import android.os.ServiceManager;
43 import android.os.SystemProperties;
44 import android.os.UpdateEngine;
45 import android.os.UpdateEngineCallback;
46 import android.provider.DeviceConfig;
47 import android.provider.Settings;
48 import android.provider.Settings.SettingNotFoundException;
49 import android.util.Log;
50 
51 import com.android.internal.R;
52 import com.android.internal.os.BackgroundThread;
53 import com.android.server.IoThread;
54 import com.android.server.LocalManagerRegistry;
55 import com.android.server.LocalServices;
56 import com.android.server.SystemService;
57 import com.android.server.art.ArtManagerLocal;
58 import com.android.server.profcollect.Utils;
59 import com.android.server.wm.ActivityMetricsLaunchObserver;
60 import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
61 import com.android.server.wm.ActivityTaskManagerInternal;
62 
63 import java.util.Arrays;
64 import java.util.concurrent.TimeUnit;
65 
66 /**
67  * System-server-local proxy into the {@code IProfcollectd} native service.
68  */
69 public final class ProfcollectForwardingService extends SystemService {
70     public static final String LOG_TAG = "ProfcollectForwardingService";
71 
72     private static final String INTENT_UPLOAD_PROFILES =
73             "com.android.server.profcollect.UPLOAD_PROFILES";
74     private static final long BG_PROCESS_INTERVAL = TimeUnit.HOURS.toMillis(4); // every 4 hours.
75 
76     private int mUsageSetting;
77     private boolean mUploadEnabled;
78 
79     static boolean sVerityEnforced;
80     static boolean sIsInteractive;
81     static boolean sAdbActive;
82     static boolean sIsBatteryLow;
83 
84     private static IProfCollectd sIProfcollect;
85     private static ProfcollectForwardingService sSelfService;
86     private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
87 
88     private IProviderStatusCallback mProviderStatusCallback = new IProviderStatusCallback.Stub() {
89         public void onProviderReady() {
90             mHandler.sendEmptyMessage(ProfcollectdHandler.MESSAGE_REGISTER_SCHEDULERS);
91         }
92     };
93 
94     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
95         @Override
96         public void onReceive(Context context, Intent intent) {
97             if (ACTION_BATTERY_LOW.equals(intent.getAction())) {
98                 sIsBatteryLow = true;
99             } else if (ACTION_BATTERY_OKAY.equals(intent.getAction())) {
100                 sIsBatteryLow = false;
101             } else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
102                 Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
103                         + sIsInteractive);
104                 sIsInteractive = true;
105             } else if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
106                 Log.d(LOG_TAG, "Received broadcast that the device became noninteractive, was "
107                         + sIsInteractive);
108                 sIsInteractive = false;
109             } else if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
110                 Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
111                 createAndUploadReport(sSelfService);
112             } else if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
113                 boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
114                 if (isADB) {
115                     boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
116                     Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
117                             + ", was " + sAdbActive);
118                     sAdbActive = connected;
119                 }
120             }
121         }
122     };
123 
ProfcollectForwardingService(Context context)124     public ProfcollectForwardingService(Context context) {
125         super(context);
126 
127         if (sSelfService != null) {
128             throw new AssertionError("only one service instance allowed");
129         }
130         sSelfService = this;
131 
132         // Get "Usage & diagnostics" checkbox status. 1 is for enabled, 0 is for disabled.
133         try {
134             mUsageSetting = Settings.Global.getInt(context.getContentResolver(), "multi_cb");
135         } catch (SettingNotFoundException e) {
136             Log.e(LOG_TAG, "Usage setting not found: " + e.getMessage());
137             mUsageSetting = -1;
138         }
139 
140         // Check verity, disable profile upload if not enforced.
141         final String verityMode = SystemProperties.get("ro.boot.veritymode");
142         sVerityEnforced = verityMode.equals("enforcing");
143         if (!sVerityEnforced) {
144             Log.d(LOG_TAG, "verity is not enforced: " + verityMode);
145         }
146 
147         mUploadEnabled =
148             context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
149 
150         final IntentFilter filter = new IntentFilter();
151         filter.addAction(ACTION_BATTERY_LOW);
152         filter.addAction(ACTION_BATTERY_OKAY);
153         filter.addAction(ACTION_SCREEN_ON);
154         filter.addAction(ACTION_SCREEN_OFF);
155         filter.addAction(INTENT_UPLOAD_PROFILES);
156         filter.addAction(UsbManager.ACTION_USB_STATE);
157         context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
158     }
159 
160     /**
161      * Check whether profcollect is enabled through device config.
162      */
enabled()163     public static boolean enabled() {
164         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, "enabled",
165             false) || SystemProperties.getBoolean("persist.profcollectd.enabled_override", false);
166     }
167 
168     @Override
onStart()169     public void onStart() {
170         connectNativeService();
171     }
172 
173     @Override
174     @RequiresPermission(Manifest.permission.MANAGE_USB)
onBootPhase(int phase)175     public void onBootPhase(int phase) {
176         if (phase == PHASE_SYSTEM_SERVICES_READY) {
177             UsbManager usbManager = getContext().getSystemService(UsbManager.class);
178             if (usbManager == null) {
179                 sAdbActive = false;
180                 Log.d(LOG_TAG, "USBManager is not ready");
181             } else {
182                 sAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
183                 Log.d(LOG_TAG, "ADB is " + sAdbActive + " on system startup");
184             }
185 
186             PowerManager powerManager = getContext().getSystemService(PowerManager.class);
187             if (powerManager == null) {
188                 sIsInteractive = true;
189                 Log.d(LOG_TAG, "PowerManager is not ready");
190             } else {
191                 sIsInteractive = powerManager.isInteractive();
192                 Log.d(LOG_TAG, "Device is interactive " + sIsInteractive + " on system startup");
193             }
194         }
195         if (phase == PHASE_BOOT_COMPLETED) {
196             if (sIProfcollect == null) {
197                 return;
198             }
199             BackgroundThread.get().getThreadHandler().post(() -> {
200                 if (serviceHasSupportedTraceProvider()) {
201                     registerProviderStatusCallback();
202                 }
203             });
204         }
205     }
206 
registerProviderStatusCallback()207     private void registerProviderStatusCallback() {
208         if (sIProfcollect == null) {
209             return;
210         }
211         try {
212             sIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
213         } catch (RemoteException e) {
214             Log.e(LOG_TAG, "Failed to register provider status callback: " + e.getMessage());
215         }
216     }
217 
serviceHasSupportedTraceProvider()218     private boolean serviceHasSupportedTraceProvider() {
219         if (sIProfcollect == null) {
220             return false;
221         }
222         try {
223             return !sIProfcollect.get_supported_provider().isEmpty();
224         } catch (RemoteException e) {
225             Log.e(LOG_TAG, "Failed to get supported provider: " + e.getMessage());
226             return false;
227         }
228     }
229 
tryConnectNativeService()230     private boolean tryConnectNativeService() {
231         if (connectNativeService()) {
232             return true;
233         }
234         // Cannot connect to the native service at this time, retry after a short delay.
235         mHandler.sendEmptyMessageDelayed(ProfcollectdHandler.MESSAGE_BINDER_CONNECT, 5000);
236         return false;
237     }
238 
connectNativeService()239     private boolean connectNativeService() {
240         try {
241             IProfCollectd profcollectd =
242                     IProfCollectd.Stub.asInterface(
243                             ServiceManager.getServiceOrThrow("profcollectd"));
244             profcollectd.asBinder().linkToDeath(new ProfcollectdDeathRecipient(), /*flags*/0);
245             sIProfcollect = profcollectd;
246             return true;
247         } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
248             Log.w(LOG_TAG, "Failed to connect profcollectd binder service.");
249             return false;
250         }
251     }
252 
253     private class ProfcollectdHandler extends Handler {
ProfcollectdHandler(Looper looper)254         public ProfcollectdHandler(Looper looper) {
255             super(looper);
256         }
257 
258         public static final int MESSAGE_BINDER_CONNECT = 0;
259         public static final int MESSAGE_REGISTER_SCHEDULERS = 1;
260 
261         @Override
handleMessage(android.os.Message message)262         public void handleMessage(android.os.Message message) {
263             switch (message.what) {
264                 case MESSAGE_BINDER_CONNECT:
265                     connectNativeService();
266                     break;
267                 case MESSAGE_REGISTER_SCHEDULERS:
268                     registerObservers();
269                     PeriodicTraceJobService.schedule(getContext());
270                     ReportProcessJobService.schedule(getContext());
271                     break;
272                 default:
273                     throw new AssertionError("Unknown message: " + message);
274             }
275         }
276     }
277 
278     private class ProfcollectdDeathRecipient implements DeathRecipient {
279         @Override
binderDied()280         public void binderDied() {
281             Log.w(LOG_TAG, "profcollectd has died");
282 
283             sIProfcollect = null;
284             tryConnectNativeService();
285         }
286     }
287 
288     /**
289      * Background report process and upload service.
290      */
291     public static class PeriodicTraceJobService extends JobService {
292         // Unique ID in system server
293         private static final int PERIODIC_TRACE_JOB_ID = 241207;
294         private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
295                 "android",
296                 PeriodicTraceJobService.class.getName());
297 
298         /**
299          * Attach the service to the system job scheduler.
300          */
schedule(Context context)301         public static void schedule(Context context) {
302             final int interval = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
303                     "collection_interval", 600);
304             JobScheduler js = context.getSystemService(JobScheduler.class);
305             js.schedule(new JobInfo.Builder(PERIODIC_TRACE_JOB_ID, JOB_SERVICE_NAME)
306                     .setPeriodic(TimeUnit.SECONDS.toMillis(interval))
307                     // PRIORITY_DEFAULT is the highest priority we can request for a periodic job.
308                     .setPriority(JobInfo.PRIORITY_DEFAULT)
309                     .build());
310         }
311 
312         @Override
onStartJob(JobParameters params)313         public boolean onStartJob(JobParameters params) {
314             if (sIProfcollect != null) {
315                 Utils.traceSystem(sIProfcollect, "periodic");
316             }
317             jobFinished(params, false);
318             return true;
319         }
320 
321         @Override
onStopJob(JobParameters params)322         public boolean onStopJob(JobParameters params) {
323             return false;
324         }
325     }
326 
327     /**
328      * Background report process and upload service.
329      */
330     public static class ReportProcessJobService extends JobService {
331         // Unique ID in system server
332         private static final int REPORT_PROCESS_JOB_ID = 260817;
333         private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
334                 "android",
335                 ReportProcessJobService.class.getName());
336 
337         /**
338          * Attach the service to the system job scheduler.
339          */
schedule(Context context)340         public static void schedule(Context context) {
341             JobScheduler js = context.getSystemService(JobScheduler.class);
342             js.schedule(new JobInfo.Builder(REPORT_PROCESS_JOB_ID, JOB_SERVICE_NAME)
343                     .setRequiresDeviceIdle(true)
344                     .setRequiresCharging(true)
345                     .setPeriodic(BG_PROCESS_INTERVAL)
346                     .setPriority(JobInfo.PRIORITY_MIN)
347                     .build());
348         }
349 
350         @Override
onStartJob(JobParameters params)351         public boolean onStartJob(JobParameters params) {
352             createAndUploadReport(sSelfService);
353             jobFinished(params, false);
354             return true;
355         }
356 
357         @Override
onStopJob(JobParameters params)358         public boolean onStopJob(JobParameters params) {
359             return false;
360         }
361     }
362 
363     // Event observers
registerObservers()364     private void registerObservers() {
365         BackgroundThread.get().getThreadHandler().post(
366                 () -> {
367                     registerAppLaunchObserver();
368                     registerCameraOpenObserver();
369                     registerDex2oatObserver();
370                     registerOTAObserver();
371                 });
372     }
373 
374     private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
registerAppLaunchObserver()375     private void registerAppLaunchObserver() {
376         ActivityTaskManagerInternal atmInternal =
377                 LocalServices.getService(ActivityTaskManagerInternal.class);
378         ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
379                 atmInternal.getLaunchObserverRegistry();
380         launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
381     }
382 
383     private class AppLaunchObserver extends ActivityMetricsLaunchObserver {
384         @Override
onIntentStarted(Intent intent, long timestampNanos)385         public void onIntentStarted(Intent intent, long timestampNanos) {
386             if (Utils.withFrequency("applaunch_trace_freq", 5)) {
387                 Utils.traceSystem(sIProfcollect, "applaunch");
388             }
389         }
390     }
391 
registerDex2oatObserver()392     private void registerDex2oatObserver() {
393         ArtManagerLocal aml = LocalManagerRegistry.getManager(ArtManagerLocal.class);
394         if (aml == null) {
395             Log.w(LOG_TAG, "Couldn't get ArtManagerLocal");
396             return;
397         }
398         aml.setBatchDexoptStartCallback(Runnable::run,
399                 (snapshot, reason, defaultPackages, builder, passedSignal) -> {
400                     traceOnDex2oatStart();
401                 });
402     }
403 
traceOnDex2oatStart()404     private void traceOnDex2oatStart() {
405         if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
406             // Dex2oat could take a while before it starts. Add a short delay before start tracing.
407             Utils.traceSystem(sIProfcollect, "dex2oat", /* delayMs */ 1000);
408         }
409     }
410 
registerOTAObserver()411     private void registerOTAObserver() {
412         UpdateEngine updateEngine = new UpdateEngine();
413         updateEngine.bind(new UpdateEngineCallback() {
414             @Override
415             public void onStatusUpdate(int status, float percent) {
416                 if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) {
417                     createAndUploadReport(sSelfService);
418                 }
419             }
420 
421             @Override
422             public void onPayloadApplicationComplete(int errorCode) {
423                 // Ignored
424             }
425         });
426     }
427 
createAndUploadReport(ProfcollectForwardingService pfs)428     private static void createAndUploadReport(ProfcollectForwardingService pfs) {
429         BackgroundThread.get().getThreadHandler().post(() -> {
430             if (pfs.sIProfcollect == null) {
431                 return;
432             }
433             String reportName;
434             try {
435                 reportName = pfs.sIProfcollect.report(pfs.mUsageSetting) + ".zip";
436             } catch (RemoteException e) {
437                 Log.e(LOG_TAG, "Failed to create report: " + e.getMessage());
438                 return;
439             }
440             if (!pfs.mUploadEnabled) {
441                 Log.i(LOG_TAG, "Upload is not enabled.");
442                 return;
443             }
444             if (!sVerityEnforced) {
445                 Log.i(LOG_TAG, "Verity is not enforced.");
446                 return;
447             }
448             Intent intent = new Intent()
449                     .setPackage("com.android.shell")
450                     .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD")
451                     .putExtra("filename", reportName);
452             pfs.getContext().sendBroadcast(intent);
453         });
454     }
455 
registerCameraOpenObserver()456     private void registerCameraOpenObserver() {
457         CameraManager cm = getContext().getSystemService(CameraManager.class);
458         cm.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() {
459             @Override
460             public void onCameraOpened(String cameraId, String packageId) {
461                 Log.d(LOG_TAG, "Received camera open event from: " + packageId);
462                 // Skip face auth since it triggers way too often.
463                 if (packageId.startsWith("client.pid")) {
464                     return;
465                 }
466                 // Additional vendor specific list of apps to skip.
467                 String[] cameraSkipPackages =
468                     getContext().getResources().getStringArray(
469                         R.array.config_profcollectOnCameraOpenedSkipPackages);
470                 if (Arrays.asList(cameraSkipPackages).contains(packageId)) {
471                     return;
472                 }
473                 if (Utils.withFrequency("camera_trace_freq", 10)) {
474                     Utils.traceProcess(sIProfcollect,
475                             "camera",
476                             "android.hardware.camera.provider",
477                             /* durationMs */ 5000);
478                 }
479             }
480         }, null);
481     }
482 }
483