diff options
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; |