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