diff options
author | Oli Lan <olilan@google.com> | 2020-07-01 10:46:50 +0100 |
---|---|---|
committer | Oli Lan <olilan@google.com> | 2020-11-05 16:57:43 +0000 |
commit | 26ff5508dfd8271e24ac0a3a7cf9b5ef82073b02 (patch) | |
tree | 198c237cf757bcd1d477b3d1ad536eee90df8658 | |
parent | 089dda160cf562f7dd5498f1e53402a5b6e2e545 (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
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(); + } + } + +} |