Merge "profcollect: Move periodic trace worker to the system server" into main
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4be7928..201e0c8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8980,7 +8980,11 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
- <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService"
+ <service android:name="com.android.server.profcollect.ProfcollectForwardingService$PeriodicTraceJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ReportProcessJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 228e32e..c31594a 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,9 @@
package com.android.server.profcollect;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
+
import android.Manifest;
import android.annotation.RequiresPermission;
import android.app.job.JobInfo;
@@ -32,6 +35,7 @@
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -70,10 +74,11 @@
private int mUsageSetting;
private boolean mUploadEnabled;
- private static boolean sVerityEnforced;
- private boolean mAdbActive;
+ static boolean sVerityEnforced;
+ static boolean sIsInteractive;
+ static boolean sAdbActive;
- private IProfCollectd mIProfcollect;
+ private static IProfCollectd sIProfcollect;
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
@@ -86,17 +91,24 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
+ if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+ Log.d(LOG_TAG, "Received broadcast that the device became interactive, was "
+ + sIsInteractive);
+ sIsInteractive = true;
+ } else if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ Log.d(LOG_TAG, "Received broadcast that the device became noninteractive, was "
+ + sIsInteractive);
+ sIsInteractive = false;
+ } else if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) {
Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
createAndUploadReport(sSelfService);
- }
- if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+ } else if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
if (isADB) {
boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
- + ", was " + mAdbActive);
- mAdbActive = connected;
+ + ", was " + sAdbActive);
+ sAdbActive = connected;
}
}
}
@@ -129,6 +141,8 @@
context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SCREEN_ON);
+ filter.addAction(ACTION_SCREEN_OFF);
filter.addAction(INTENT_UPLOAD_PROFILES);
filter.addAction(UsbManager.ACTION_USB_STATE);
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
@@ -153,14 +167,24 @@
if (phase == PHASE_SYSTEM_SERVICES_READY) {
UsbManager usbManager = getContext().getSystemService(UsbManager.class);
if (usbManager == null) {
- mAdbActive = false;
- return;
+ sAdbActive = false;
+ Log.d(LOG_TAG, "USBManager is not ready");
+ } else {
+ sAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
+ Log.d(LOG_TAG, "ADB is " + sAdbActive + " on system startup");
}
- mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
- Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
+
+ PowerManager powerManager = getContext().getSystemService(PowerManager.class);
+ if (powerManager == null) {
+ sIsInteractive = true;
+ Log.d(LOG_TAG, "PowerManager is not ready");
+ } else {
+ sIsInteractive = powerManager.isInteractive();
+ Log.d(LOG_TAG, "Device is interactive " + sIsInteractive + " on system startup");
+ }
}
if (phase == PHASE_BOOT_COMPLETED) {
- if (mIProfcollect == null) {
+ if (sIProfcollect == null) {
return;
}
BackgroundThread.get().getThreadHandler().post(() -> {
@@ -172,22 +196,22 @@
}
private void registerProviderStatusCallback() {
- if (mIProfcollect == null) {
+ if (sIProfcollect == null) {
return;
}
try {
- mIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
+ sIProfcollect.registerProviderStatusCallback(mProviderStatusCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to register provider status callback: " + e.getMessage());
}
}
private boolean serviceHasSupportedTraceProvider() {
- if (mIProfcollect == null) {
+ if (sIProfcollect == null) {
return false;
}
try {
- return !mIProfcollect.get_supported_provider().isEmpty();
+ return !sIProfcollect.get_supported_provider().isEmpty();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to get supported provider: " + e.getMessage());
return false;
@@ -209,7 +233,7 @@
IProfCollectd.Stub.asInterface(
ServiceManager.getServiceOrThrow("profcollectd"));
profcollectd.asBinder().linkToDeath(new ProfcollectdDeathRecipient(), /*flags*/0);
- mIProfcollect = profcollectd;
+ sIProfcollect = profcollectd;
return true;
} catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
Log.w(LOG_TAG, "Failed to connect profcollectd binder service.");
@@ -233,7 +257,8 @@
break;
case MESSAGE_REGISTER_SCHEDULERS:
registerObservers();
- ProfcollectBGJobService.schedule(getContext());
+ PeriodicTraceJobService.schedule(getContext());
+ ReportProcessJobService.schedule(getContext());
break;
default:
throw new AssertionError("Unknown message: " + message);
@@ -246,27 +271,66 @@
public void binderDied() {
Log.w(LOG_TAG, "profcollectd has died");
- mIProfcollect = null;
+ sIProfcollect = null;
tryConnectNativeService();
}
}
/**
- * Background trace process service.
+ * Background report process and upload service.
*/
- public static class ProfcollectBGJobService extends JobService {
- // Unique ID in system service
- private static final int JOB_IDLE_PROCESS = 260817;
+ public static class PeriodicTraceJobService extends JobService {
+ // Unique ID in system server
+ private static final int PERIODIC_TRACE_JOB_ID = 241207;
private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
"android",
- ProfcollectBGJobService.class.getName());
+ PeriodicTraceJobService.class.getName());
+
+ /**
+ * Attach the service to the system job scheduler.
+ */
+ public static void schedule(Context context) {
+ final int interval = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+ "collection_interval", 600);
+ JobScheduler js = context.getSystemService(JobScheduler.class);
+ js.schedule(new JobInfo.Builder(PERIODIC_TRACE_JOB_ID, JOB_SERVICE_NAME)
+ .setPeriodic(TimeUnit.SECONDS.toMillis(interval))
+ // PRIORITY_DEFAULT is the highest priority we can request for a periodic job.
+ .setPriority(JobInfo.PRIORITY_DEFAULT)
+ .build());
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ if (sIProfcollect != null) {
+ Utils.traceSystem(sIProfcollect, "periodic");
+ }
+ jobFinished(params, false);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return false;
+ }
+ }
+
+ /**
+ * Background report process and upload service.
+ */
+ public static class ReportProcessJobService extends JobService {
+ // Unique ID in system server
+ private static final int REPORT_PROCESS_JOB_ID = 260817;
+ private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
+ "android",
+ ReportProcessJobService.class.getName());
/**
* Attach the service to the system job scheduler.
*/
public static void schedule(Context context) {
JobScheduler js = context.getSystemService(JobScheduler.class);
- js.schedule(new JobInfo.Builder(JOB_IDLE_PROCESS, JOB_SERVICE_NAME)
+ js.schedule(new JobInfo.Builder(REPORT_PROCESS_JOB_ID, JOB_SERVICE_NAME)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setPeriodic(BG_PROCESS_INTERVAL)
@@ -283,7 +347,6 @@
@Override
public boolean onStopJob(JobParameters params) {
- // TODO: Handle this?
return false;
}
}
@@ -311,14 +374,8 @@
private class AppLaunchObserver extends ActivityMetricsLaunchObserver {
@Override
public void onIntentStarted(Intent intent, long timestampNanos) {
- if (mIProfcollect == null) {
- return;
- }
- if (mAdbActive) {
- return;
- }
if (Utils.withFrequency("applaunch_trace_freq", 5)) {
- Utils.traceSystem(mIProfcollect, "applaunch");
+ Utils.traceSystem(sIProfcollect, "applaunch");
}
}
}
@@ -336,15 +393,9 @@
}
private void traceOnDex2oatStart() {
- if (mIProfcollect == null) {
- return;
- }
- if (mAdbActive) {
- return;
- }
if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
// Dex2oat could take a while before it starts. Add a short delay before start tracing.
- Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
+ Utils.traceSystem(sIProfcollect, "dex2oat", /* delayMs */ 1000);
}
}
@@ -367,12 +418,12 @@
private static void createAndUploadReport(ProfcollectForwardingService pfs) {
BackgroundThread.get().getThreadHandler().post(() -> {
- if (pfs.mIProfcollect == null) {
+ if (pfs.sIProfcollect == null) {
return;
}
String reportName;
try {
- reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
+ reportName = pfs.sIProfcollect.report(pfs.mUsageSetting) + ".zip";
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to create report: " + e.getMessage());
return;
@@ -411,7 +462,7 @@
return;
}
if (Utils.withFrequency("camera_trace_freq", 10)) {
- Utils.traceProcess(mIProfcollect,
+ Utils.traceProcess(sIProfcollect,
"camera",
"android.hardware.camera.provider",
/* durationMs */ 5000);
diff --git a/services/profcollect/src/com/android/server/profcollect/Utils.java b/services/profcollect/src/com/android/server/profcollect/Utils.java
index a8016a0..b754ca1 100644
--- a/services/profcollect/src/com/android/server/profcollect/Utils.java
+++ b/services/profcollect/src/com/android/server/profcollect/Utils.java
@@ -28,28 +28,29 @@
import java.time.Instant;
import java.util.concurrent.ThreadLocalRandom;
-public final class Utils {
+final class Utils {
private static Instant lastTraceTime = Instant.EPOCH;
private static final int TRACE_COOLDOWN_SECONDS = 30;
- public static boolean withFrequency(String configName, int defaultFrequency) {
+ static boolean withFrequency(String configName, int defaultFrequency) {
int threshold = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, configName, defaultFrequency);
int randomNum = ThreadLocalRandom.current().nextInt(100);
return randomNum < threshold;
}
- public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName) {
- if (mIProfcollect == null) {
- return false;
- }
- if (isInCooldownOrReset()) {
+ /**
+ * Request a system-wide trace.
+ * Will be ignored if the device does not meet trace criteria or is being rate limited.
+ */
+ static boolean traceSystem(IProfCollectd iprofcollectd, String eventName) {
+ if (!checkPrerequisites(iprofcollectd)) {
return false;
}
BackgroundThread.get().getThreadHandler().post(() -> {
try {
- mIProfcollect.trace_system(eventName);
+ iprofcollectd.trace_system(eventName);
} catch (RemoteException | ServiceSpecificException e) {
Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
}
@@ -57,16 +58,17 @@
return true;
}
- public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName, int delayMs) {
- if (mIProfcollect == null) {
- return false;
- }
- if (isInCooldownOrReset()) {
+ /**
+ * Request a system-wide trace after a delay.
+ * Will be ignored if the device does not meet trace criteria or is being rate limited.
+ */
+ static boolean traceSystem(IProfCollectd iprofcollectd, String eventName, int delayMs) {
+ if (!checkPrerequisites(iprofcollectd)) {
return false;
}
BackgroundThread.get().getThreadHandler().postDelayed(() -> {
try {
- mIProfcollect.trace_system(eventName);
+ iprofcollectd.trace_system(eventName);
} catch (RemoteException | ServiceSpecificException e) {
Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
}
@@ -74,17 +76,18 @@
return true;
}
- public static boolean traceProcess(IProfCollectd mIProfcollect,
+ /**
+ * Request a single-process trace.
+ * Will be ignored if the device does not meet trace criteria or is being rate limited.
+ */
+ static boolean traceProcess(IProfCollectd iprofcollectd,
String eventName, String processName, int durationMs) {
- if (mIProfcollect == null) {
- return false;
- }
- if (isInCooldownOrReset()) {
+ if (!checkPrerequisites(iprofcollectd)) {
return false;
}
BackgroundThread.get().getThreadHandler().post(() -> {
try {
- mIProfcollect.trace_process(eventName,
+ iprofcollectd.trace_process(eventName,
processName,
durationMs);
} catch (RemoteException | ServiceSpecificException e) {
@@ -105,4 +108,16 @@
}
return true;
}
+
+ private static boolean checkPrerequisites(IProfCollectd iprofcollectd) {
+ if (iprofcollectd == null) {
+ return false;
+ }
+ if (isInCooldownOrReset()) {
+ return false;
+ }
+ return ProfcollectForwardingService.sVerityEnforced
+ && !ProfcollectForwardingService.sAdbActive
+ && ProfcollectForwardingService.sIsInteractive;
+ }
}