summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/app/ProfilerInfo.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java106
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java9
4 files changed, 106 insertions, 19 deletions
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 5382e6656bf1..b25deeac75cb 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -146,6 +146,7 @@ interface IActivityManager {
void publishService(in IBinder token, in Intent intent, in IBinder service);
void activityResumed(in IBinder token);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
+ void setAgentApp(in String packageName, @nullable String agent);
void setAlwaysFinish(boolean enabled);
boolean startInstrumentation(in ComponentName className, in String profileFile,
int flags, in Bundle arguments, in IInstrumentationWatcher watcher,
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index 0ed1b0824946..6fbe9c6efb40 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -87,6 +87,15 @@ public class ProfilerInfo implements Parcelable {
}
/**
+ * Return a new ProfilerInfo instance, with fields populated from this object,
+ * and {@link agent} and {@link attachAgentDuringBind} as given.
+ */
+ public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+ return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+ this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
+ }
+
+ /**
* Close profileFd, if it is open. The field will be null after a call to this function.
*/
public void closeFd() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1f5bfe643340..168baf0fe5e2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1635,6 +1635,14 @@ public class ActivityManagerService extends IActivityManager.Stub
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
+
+ /**
+ * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+ * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+ * A non-null agent in mProfileInfo overrides this.
+ */
+ private @Nullable Map<String, String> mAppAgentMap = null;
+
int mProfileType = 0;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
@@ -7454,25 +7462,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- ProfilerInfo profilerInfo = null;
- String preBindAgent = null;
- if (mProfileApp != null && mProfileApp.equals(processName)) {
- mProfileProc = app;
- if (mProfilerInfo != null) {
- // Send a profiler info object to the app if either a file is given, or
- // an agent should be loaded at bind-time.
- boolean needsInfo = mProfilerInfo.profileFile != null
- || mProfilerInfo.attachAgentDuringBind;
- profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
- if (!mProfilerInfo.attachAgentDuringBind) {
- preBindAgent = mProfilerInfo.agent;
- }
- }
- } else if (app.instr != null && app.instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
- null, false);
- }
-
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
@@ -7497,6 +7486,39 @@ public class ActivityManagerService extends IActivityManager.Stub
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
+ ProfilerInfo profilerInfo = null;
+ String preBindAgent = null;
+ if (mProfileApp != null && mProfileApp.equals(processName)) {
+ mProfileProc = app;
+ if (mProfilerInfo != null) {
+ // Send a profiler info object to the app if either a file is given, or
+ // an agent should be loaded at bind-time.
+ boolean needsInfo = mProfilerInfo.profileFile != null
+ || mProfilerInfo.attachAgentDuringBind;
+ profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+ if (mProfilerInfo.agent != null) {
+ preBindAgent = mProfilerInfo.agent;
+ }
+ }
+ } else if (app.instr != null && app.instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+ null, false);
+ }
+ if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+ // We need to do a debuggable check here. See setAgentApp for why the check is
+ // postponed to here.
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ String agent = mAppAgentMap.get(processName);
+ // Do not overwrite already requested agent.
+ if (profilerInfo == null) {
+ profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+ mAppAgentMap.get(processName), true);
+ } else if (profilerInfo.agent == null) {
+ profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+ }
+ }
+ }
+
if (profilerInfo != null && profilerInfo.profileFd != null) {
profilerInfo.profileFd = profilerInfo.profileFd.dup();
}
@@ -13165,6 +13187,52 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * Set or remove an agent to be run whenever an app with the given process name starts.
+ *
+ * This method will not check whether the given process name matches a debuggable app. That
+ * would require scanning all current packages, and a rescan when new packages are installed
+ * or updated.
+ *
+ * Instead, do the check when an application is started and matched to a stored agent.
+ *
+ * @param packageName the process name of the app.
+ * @param agent the agent string to be used, or null to remove any previously set agent.
+ */
+ @Override
+ public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(
+ android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
+ PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+ }
+
+ if (agent == null) {
+ if (mAppAgentMap != null) {
+ mAppAgentMap.remove(packageName);
+ if (mAppAgentMap.isEmpty()) {
+ mAppAgentMap = null;
+ }
+ }
+ } else {
+ if (mAppAgentMap == null) {
+ mAppAgentMap = new HashMap<>();
+ }
+ if (mAppAgentMap.size() >= 100) {
+ // Limit the size of the map, to avoid OOMEs.
+ Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ + "/" + agent);
+ return;
+ }
+ mAppAgentMap.put(packageName, agent);
+ }
+ }
+ }
+
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 1240f5e664aa..f0c90e0f1fab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -160,6 +160,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runDumpHeap(pw);
case "set-debug-app":
return runSetDebugApp(pw);
+ case "set-agent-app":
+ return runSetAgentApp(pw);
case "clear-debug-app":
return runClearDebugApp(pw);
case "set-watch-heap":
@@ -873,6 +875,13 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ int runSetAgentApp(PrintWriter pw) throws RemoteException {
+ String pkg = getNextArgRequired();
+ String agent = getNextArg();
+ mInterface.setAgentApp(pkg, agent);
+ return 0;
+ }
+
int runClearDebugApp(PrintWriter pw) throws RemoteException {
mInterface.setDebugApp(null, false, true);
return 0;