summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOli Lan <olilan@google.com>2020-07-01 10:46:50 +0100
committerOli Lan <olilan@google.com>2020-11-05 16:57:43 +0000
commit26ff5508dfd8271e24ac0a3a7cf9b5ef82073b02 (patch)
tree198c237cf757bcd1d477b3d1ad536eee90df8658
parent089dda160cf562f7dd5498f1e53402a5b6e2e545 (diff)
Enable instrumentation of system server on debuggable devices.
This makes two changes to enable the instrumentation of system server and other system processes: - A new option '--no-restart' has been added to 'am instrument', that causes the test apk to be loaded without restarting the target app. - On debuggable devices, the check that the test apk has the same signature as the target app is not performed. With these changes, a test apk with instrumentation configured with targetPackage="android" can run a test from within the system server process, on debuggable devices. These options may also allow other system processes to be tested. See go/internal-api-testing for more information. Test: atest FrameworksInProcessTests Change-Id: I8829bcbe7f3373170bcf1e7fa869d5d31f40576d
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java2
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java5
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/app/ActivityThread.java213
-rw-r--r--core/java/android/app/IApplicationThread.aidl5
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java6
-rw-r--r--services/core/java/com/android/server/am/ActiveInstrumentation.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java99
-rw-r--r--services/tests/inprocesstests/Android.bp12
-rw-r--r--services/tests/inprocesstests/AndroidManifest.xml30
-rw-r--r--services/tests/inprocesstests/AndroidTest.xml36
-rw-r--r--services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java52
12 files changed, 375 insertions, 94 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index bdb83804d903..ebe6199690f7 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -186,6 +186,8 @@ public class Am extends BaseCommand {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
instrument.abi = nextArgRequired();
+ } else if (opt.equals("--no-restart")) {
+ instrument.noRestart = true;
} else {
System.err.println("Error: Unknown option: " + opt);
return;
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 7c30c8b1e1dd..2be8264856f8 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -19,6 +19,7 @@ package com.android.commands.am;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
@@ -89,6 +90,7 @@ public class Instrument {
public boolean disableTestApiChecks = true;
public boolean disableIsolatedStorage = false;
public String abi = null;
+ public boolean noRestart = false;
public int userId = UserHandle.USER_CURRENT;
public Bundle args = new Bundle();
// Required
@@ -514,6 +516,9 @@ public class Instrument {
if (disableIsolatedStorage) {
flags |= INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
}
+ if (noRestart) {
+ flags |= INSTR_FLAG_NO_RESTART;
+ }
if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 250f2f0b2dc9..a496451e5801 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -171,6 +171,12 @@ public class ActivityManager {
*/
public static final int INSTR_FLAG_DISABLE_TEST_API_CHECKS = 1 << 2;
+ /**
+ * Do not restart the target process when starting or finishing instrumentation.
+ * @hide
+ */
+ public static final int INSTR_FLAG_NO_RESTART = 1 << 3;
+
static final class UidObserver extends IUidObserver.Stub {
final OnUidImportanceListener mListener;
final Context mContext;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 87c729b20c71..4b97c6eab4fe 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -380,6 +380,7 @@ public final class ActivityThread extends ClientTransactionHandler {
String mInstrumentedAppDir = null;
String[] mInstrumentedSplitAppDirs = null;
String mInstrumentedLibDir = null;
+ boolean mInstrumentingWithoutRestart;
boolean mSystemThread = false;
boolean mSomeActivitiesChanged = false;
/* package */ boolean mHiddenApiWarningShown = false;
@@ -1773,6 +1774,19 @@ public final class ActivityThread extends ClientTransactionHandler {
key.mLock.notifyAll();
}
}
+
+ @Override
+ public void instrumentWithoutRestart(ComponentName instrumentationName,
+ Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
+ IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) {
+ AppBindData data = new AppBindData();
+ data.instrumentationName = instrumentationName;
+ data.instrumentationArgs = instrumentationArgs;
+ data.instrumentationWatcher = instrumentationWatcher;
+ data.instrumentationUiAutomationConnection = instrumentationUiConnection;
+ data.appInfo = targetInfo;
+ sendMessage(H.INSTRUMENT_WITHOUT_RESTART, data);
+ }
}
private @NonNull SafeCancellationTransport createSafeCancellationTransport(
@@ -1878,6 +1892,9 @@ public final class ActivityThread extends ClientTransactionHandler {
public static final int PURGE_RESOURCES = 161;
public static final int ATTACH_STARTUP_AGENTS = 162;
+ public static final int INSTRUMENT_WITHOUT_RESTART = 170;
+ public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
+
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
@@ -1920,6 +1937,9 @@ public final class ActivityThread extends ClientTransactionHandler {
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PURGE_RESOURCES: return "PURGE_RESOURCES";
case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
+ case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
+ case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
+ return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
}
}
return Integer.toString(code);
@@ -2101,6 +2121,12 @@ public final class ActivityThread extends ClientTransactionHandler {
case ATTACH_STARTUP_AGENTS:
handleAttachStartupAgents((String) msg.obj);
break;
+ case INSTRUMENT_WITHOUT_RESTART:
+ handleInstrumentWithoutRestart((AppBindData) msg.obj);
+ break;
+ case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
+ handleFinishInstrumentationWithoutRestart();
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -6487,32 +6513,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// setting up the app context.
final InstrumentationInfo ii;
if (data.instrumentationName != null) {
- try {
- ii = new ApplicationPackageManager(
- null, getPackageManager(), getPermissionManager())
- .getInstrumentationInfo(data.instrumentationName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(
- "Unable to find instrumentation info for: " + data.instrumentationName);
- }
-
- // Warn of potential ABI mismatches.
- if (!Objects.equals(data.appInfo.primaryCpuAbi, ii.primaryCpuAbi)
- || !Objects.equals(data.appInfo.secondaryCpuAbi, ii.secondaryCpuAbi)) {
- Slog.w(TAG, "Package uses different ABI(s) than its instrumentation: "
- + "package[" + data.appInfo.packageName + "]: "
- + data.appInfo.primaryCpuAbi + ", " + data.appInfo.secondaryCpuAbi
- + " instrumentation[" + ii.packageName + "]: "
- + ii.primaryCpuAbi + ", " + ii.secondaryCpuAbi);
- }
-
- mInstrumentationPackageName = ii.packageName;
- mInstrumentationAppDir = ii.sourceDir;
- mInstrumentationSplitAppDirs = ii.splitSourceDirs;
- mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
- mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
- mInstrumentedLibDir = data.info.getLibDir();
+ ii = prepareInstrumentation(data);
} else {
ii = null;
}
@@ -6541,48 +6542,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// Continue loading instrumentation.
if (ii != null) {
- ApplicationInfo instrApp;
- try {
- instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0,
- UserHandle.myUserId());
- } catch (RemoteException e) {
- instrApp = null;
- }
- if (instrApp == null) {
- instrApp = new ApplicationInfo();
- }
- ii.copyTo(instrApp);
- instrApp.initForUser(UserHandle.myUserId());
- final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
- appContext.getClassLoader(), false, true, false);
-
- // The test context's op package name == the target app's op package name, because
- // the app ops manager checks the op package name against the real calling UID,
- // which is what the target package name is associated with.
- final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
- appContext.getOpPackageName());
-
- try {
- final ClassLoader cl = instrContext.getClassLoader();
- mInstrumentation = (Instrumentation)
- cl.loadClass(data.instrumentationName.getClassName()).newInstance();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate instrumentation "
- + data.instrumentationName + ": " + e.toString(), e);
- }
-
- final ComponentName component = new ComponentName(ii.packageName, ii.name);
- mInstrumentation.init(this, instrContext, appContext, component,
- data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
-
- if (mProfiler.profileFile != null && !ii.handleProfiling
- && mProfiler.profileFd == null) {
- mProfiler.handlingProfiling = true;
- final File file = new File(mProfiler.profileFile);
- file.getParentFile().mkdirs();
- Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
- }
+ initInstrumentation(ii, data, appContext);
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
@@ -6673,6 +6633,120 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ private void handleInstrumentWithoutRestart(AppBindData data) {
+ try {
+ data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+ mInstrumentingWithoutRestart = true;
+ final InstrumentationInfo ii = prepareInstrumentation(data);
+ final ContextImpl appContext =
+ ContextImpl.createAppContext(this, data.info);
+
+ initInstrumentation(ii, data, appContext);
+
+ try {
+ mInstrumentation.onCreate(data.instrumentationArgs);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Exception thrown in onCreate() of "
+ + data.instrumentationName + ": " + e.toString(), e);
+ }
+
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in handleInstrumentWithoutRestart", e);
+ }
+ }
+
+ private InstrumentationInfo prepareInstrumentation(AppBindData data) {
+ final InstrumentationInfo ii;
+ try {
+ ii = new ApplicationPackageManager(
+ null, getPackageManager(), getPermissionManager())
+ .getInstrumentationInfo(data.instrumentationName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(
+ "Unable to find instrumentation info for: " + data.instrumentationName);
+ }
+
+ // Warn of potential ABI mismatches.
+ if (!Objects.equals(data.appInfo.primaryCpuAbi, ii.primaryCpuAbi)
+ || !Objects.equals(data.appInfo.secondaryCpuAbi, ii.secondaryCpuAbi)) {
+ Slog.w(TAG, "Package uses different ABI(s) than its instrumentation: "
+ + "package[" + data.appInfo.packageName + "]: "
+ + data.appInfo.primaryCpuAbi + ", " + data.appInfo.secondaryCpuAbi
+ + " instrumentation[" + ii.packageName + "]: "
+ + ii.primaryCpuAbi + ", " + ii.secondaryCpuAbi);
+ }
+
+ mInstrumentationPackageName = ii.packageName;
+ mInstrumentationAppDir = ii.sourceDir;
+ mInstrumentationSplitAppDirs = ii.splitSourceDirs;
+ mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
+ mInstrumentedAppDir = data.info.getAppDir();
+ mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+ mInstrumentedLibDir = data.info.getLibDir();
+
+ return ii;
+ }
+
+ private void initInstrumentation(
+ InstrumentationInfo ii, AppBindData data, ContextImpl appContext) {
+ ApplicationInfo instrApp;
+ try {
+ instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ instrApp = null;
+ }
+ if (instrApp == null) {
+ instrApp = new ApplicationInfo();
+ }
+ ii.copyTo(instrApp);
+ instrApp.initForUser(UserHandle.myUserId());
+ final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+ appContext.getClassLoader(), false, true, false);
+
+ // The test context's op package name == the target app's op package name, because
+ // the app ops manager checks the op package name against the real calling UID,
+ // which is what the target package name is associated with.
+ final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
+ appContext.getOpPackageName());
+
+ try {
+ final ClassLoader cl = instrContext.getClassLoader();
+ mInstrumentation = (Instrumentation)
+ cl.loadClass(data.instrumentationName.getClassName()).newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Unable to instantiate instrumentation "
+ + data.instrumentationName + ": " + e.toString(), e);
+ }
+
+ final ComponentName component = new ComponentName(ii.packageName, ii.name);
+ mInstrumentation.init(this, instrContext, appContext, component,
+ data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
+
+ if (mProfiler.profileFile != null && !ii.handleProfiling
+ && mProfiler.profileFd == null) {
+ mProfiler.handlingProfiling = true;
+ final File file = new File(mProfiler.profileFile);
+ file.getParentFile().mkdirs();
+ Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+ }
+ }
+
+ private void handleFinishInstrumentationWithoutRestart() {
+ mInstrumentation.onDestroy();
+ mInstrumentationPackageName = null;
+ mInstrumentationAppDir = null;
+ mInstrumentationSplitAppDirs = null;
+ mInstrumentationLibDir = null;
+ mInstrumentedAppDir = null;
+ mInstrumentedSplitAppDirs = null;
+ mInstrumentedLibDir = null;
+ mInstrumentingWithoutRestart = false;
+ }
+
/*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
IActivityManager am = ActivityManager.getService();
if (mProfiler.profileFile != null && mProfiler.handlingProfiling
@@ -6686,6 +6760,9 @@ public final class ActivityThread extends ClientTransactionHandler {
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
+ if (mInstrumentingWithoutRestart) {
+ sendMessage(H.FINISH_INSTRUMENTATION_WITHOUT_RESTART, null);
+ }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index dc9918ade9c2..22ca42eb9cf4 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -150,4 +150,9 @@ oneway interface IApplicationThread {
in RemoteCallback resultCallback);
void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth,
int userId, boolean published);
+ void instrumentWithoutRestart(in ComponentName instrumentationName,
+ in Bundle instrumentationArgs,
+ IInstrumentationWatcher instrumentationWatcher,
+ IUiAutomationConnection instrumentationUiConnection,
+ in ApplicationInfo targetInfo);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 7766b575c156..5871e2e04687 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -672,5 +672,11 @@ public class TransactionParcelTests {
public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth,
int userId, boolean published) {
}
+
+ @Override
+ public void instrumentWithoutRestart(ComponentName instrumentationName,
+ Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
+ IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) {
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index db63638bbfba..43474d5f22d4 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -70,6 +70,9 @@ class ActiveInstrumentation {
// The uid of the process who started this instrumentation.
int mSourceUid;
+ // True if instrumentation should take place without restarting the target process.
+ boolean mNoRestart;
+
ActiveInstrumentation(ActivityManagerService service) {
mService = service;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 63f7d44da1de..1e3227f61d49 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
@@ -14165,9 +14166,12 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized(this) {
InstrumentationInfo ii = null;
ApplicationInfo ai = null;
+
+ boolean noRestart = (flags & INSTR_FLAG_NO_RESTART) != 0;
+
try {
ii = mContext.getPackageManager().getInstrumentationInfo(
- className, STOCK_PM_FLAGS);
+ className, STOCK_PM_FLAGS);
ai = AppGlobals.getPackageManager().getApplicationInfo(
ii.targetPackage, STOCK_PM_FLAGS, userId);
} catch (PackageManager.NameNotFoundException e) {
@@ -14183,24 +14187,33 @@ public class ActivityManagerService extends IActivityManager.Stub
"Unable to find instrumentation target package: " + ii.targetPackage);
return false;
}
- if (!ai.hasCode()) {
+
+ if (ii.targetPackage.equals("android")) {
+ if (!noRestart) {
+ reportStartInstrumentationFailureLocked(watcher, className,
+ "Cannot instrument system server without 'no-restart'");
+ return false;
+ }
+ } else if (!ai.hasCode()) {
reportStartInstrumentationFailureLocked(watcher, className,
"Instrumentation target has no code: " + ii.targetPackage);
return false;
}
- int match = mContext.getPackageManager().checkSignatures(
- ii.targetPackage, ii.packageName);
- if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
- String msg = "Permission Denial: starting instrumentation "
- + className + " from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingPid()
- + " not allowed because package " + ii.packageName
- + " does not have a signature matching the target "
- + ii.targetPackage;
- reportStartInstrumentationFailureLocked(watcher, className, msg);
- throw new SecurityException(msg);
+ if (!Build.IS_DEBUGGABLE) {
+ int match = mContext.getPackageManager().checkSignatures(
+ ii.targetPackage, ii.packageName);
+ if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+ String msg = "Permission Denial: starting instrumentation "
+ + className + " from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingPid()
+ + " not allowed because package " + ii.packageName
+ + " does not have a signature matching the target "
+ + ii.targetPackage;
+ reportStartInstrumentationFailureLocked(watcher, className, msg);
+ throw new SecurityException(msg);
+ }
}
ActiveInstrumentation activeInstr = new ActiveInstrumentation(this);
@@ -14223,6 +14236,7 @@ public class ActivityManagerService extends IActivityManager.Stub
activeInstr.mHasBackgroundActivityStartsPermission = checkPermission(
START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
+ activeInstr.mNoRestart = noRestart;
boolean disableHiddenApiChecks = ai.usesNonSdkApi()
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
@@ -14238,18 +14252,25 @@ public class ActivityManagerService extends IActivityManager.Stub
&& (flags & INSTR_FLAG_DISABLE_ISOLATED_STORAGE) != 0;
final long origId = Binder.clearCallingIdentity();
- // Instrumentation can kill and relaunch even persistent processes
- forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
- "start instr");
- // Inform usage stats to make the target package active
- if (mUsageStatsService != null) {
- mUsageStatsService.reportEvent(ii.targetPackage, userId,
- UsageEvents.Event.SYSTEM_INTERACTION);
+
+ ProcessRecord app;
+ if (noRestart) {
+ app = getProcessRecordLocked(ai.processName, ai.uid, true);
+ } else {
+ // Instrumentation can kill and relaunch even persistent processes
+ forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
+ "start instr");
+ // Inform usage stats to make the target package active
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(ii.targetPackage, userId,
+ UsageEvents.Event.SYSTEM_INTERACTION);
+ }
+ app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+ disableTestApiChecks, mountExtStorageFull, abiOverride,
+ ZYGOTE_POLICY_FLAG_EMPTY);
}
- ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, mountExtStorageFull, abiOverride,
- ZYGOTE_POLICY_FLAG_EMPTY);
+
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mSourceUid = callingUid;
@@ -14265,11 +14286,34 @@ public class ActivityManagerService extends IActivityManager.Stub
ii.packageName, AppOpsManager.MODE_ALLOWED);
}
Binder.restoreCallingIdentity(origId);
+
+ if (noRestart) {
+ instrumentWithoutRestart(activeInstr, ai);
+ }
}
return true;
}
+ private void instrumentWithoutRestart(ActiveInstrumentation activeInstr,
+ ApplicationInfo targetInfo) {
+ ProcessRecord pr;
+ synchronized (this) {
+ pr = getProcessRecordLocked(targetInfo.processName, targetInfo.uid, true);
+ }
+
+ try {
+ pr.thread.instrumentWithoutRestart(
+ activeInstr.mClass,
+ activeInstr.mArguments,
+ activeInstr.mWatcher,
+ activeInstr.mUiAutomationConnection,
+ targetInfo);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "RemoteException from instrumentWithoutRestart", e);
+ }
+ }
+
private boolean isCallerShell() {
final int callingUid = Binder.getCallingUid();
return callingUid == SHELL_UID || callingUid == ROOT_UID;
@@ -14368,8 +14412,11 @@ public class ActivityManagerService extends IActivityManager.Stub
instr.removeProcess(app);
app.setActiveInstrumentation(null);
- forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId,
- "finished inst");
+ if (!instr.mNoRestart) {
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
+ app.userId,
+ "finished inst");
+ }
}
public void finishInstrumentation(IApplicationThread target,
diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp
new file mode 100644
index 000000000000..6dd059fc919d
--- /dev/null
+++ b/services/tests/inprocesstests/Android.bp
@@ -0,0 +1,12 @@
+android_test {
+ name: "FrameworksInProcessTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "services.core",
+ "truth-prebuilt",
+ "platform-test-annotations",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/services/tests/inprocesstests/AndroidManifest.xml b/services/tests/inprocesstests/AndroidManifest.xml
new file mode 100644
index 000000000000..efb4a53aad24
--- /dev/null
+++ b/services/tests/inprocesstests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.inprocesstests" >
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android"
+ android:label="Frameworks In-Process Tests"/>
+
+</manifest>
diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml
new file mode 100644
index 000000000000..89abe3c0891c
--- /dev/null
+++ b/services/tests/inprocesstests/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs frameworks in-process tests">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="FrameworksInProcessTests.apk"/>
+ </target_preparer>
+
+ <!-- Restart to clear test code from system server -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="teardown-command" value="am restart" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.frameworks.inprocesstests"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="restart" value="false" />
+ </test>
+</configuration>
diff --git a/services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java b/services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java
new file mode 100644
index 000000000000..47fc73fed521
--- /dev/null
+++ b/services/tests/inprocesstests/src/com/android/frameworks/inprocesstests/InstrumentSystemServerTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.inprocesstests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Process;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+
+public class InstrumentSystemServerTest {
+
+ private static final String TAG = "InstrumentSystemServerTest";
+
+ @Test
+ public void testCodeIsRunningInSystemServer() throws Exception {
+ assertThat(InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName())
+ .isEqualTo("android");
+ assertThat(Process.myUid()).isEqualTo(Process.SYSTEM_UID);
+ assertThat(readCmdLine()).isEqualTo("system_server");
+ }
+
+ private static String readCmdLine() throws Exception {
+ BufferedReader in = null;
+ try {
+ in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+ return in.readLine().trim();
+ } finally {
+ in.close();
+ }
+ }
+
+}