Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26ff5508 authored by Oli Lan's avatar Oli Lan
Browse files

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
parent 089dda16
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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;
+5 −0
Original line number Diff line number Diff line
@@ -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());
+6 −0
Original line number Diff line number Diff line
@@ -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;
+145 −68
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
@@ -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);
}
Loading