diff --git a/api/current.txt b/api/current.txt index eac0653297341..39097e77971d3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -203,7 +203,6 @@ package android { public static final class R.attr { ctor public R.attr(); - field public static final int __removed0 = 16844097; // 0x1010541 field public static final int __removed1 = 16844099; // 0x1010543 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 @@ -1270,6 +1269,7 @@ package android { field public static final int targetId = 16843740; // 0x10103dc field public static final int targetName = 16843853; // 0x101044d field public static final int targetPackage = 16842785; // 0x1010021 + field public static final int targetProcess = 16844097; // 0x1010541 field public static final int targetSandboxVersion = 16844110; // 0x101054e field public static final int targetSdkVersion = 16843376; // 0x1010270 field public static final int taskAffinity = 16842770; // 0x1010012 @@ -4804,6 +4804,7 @@ package android.app { method public void addMonitor(android.app.Instrumentation.ActivityMonitor); method public android.app.Instrumentation.ActivityMonitor addMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean); method public android.app.Instrumentation.ActivityMonitor addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean); + method public void addResults(android.os.Bundle); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnDestroy(android.app.Activity); @@ -4828,6 +4829,7 @@ package android.app { method public android.os.Bundle getBinderCounts(); method public android.content.ComponentName getComponentName(); method public android.content.Context getContext(); + method public java.lang.String getProcessName(); method public android.content.Context getTargetContext(); method public android.app.UiAutomation getUiAutomation(); method public android.app.UiAutomation getUiAutomation(int); @@ -9903,6 +9905,7 @@ package android.content.pm { field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; + field public java.lang.String targetProcess; } public class LabeledIntent extends android.content.Intent { diff --git a/api/system-current.txt b/api/system-current.txt index 3b2ad3fd44a17..781600fd1776a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -315,7 +315,6 @@ package android { public static final class R.attr { ctor public R.attr(); - field public static final int __removed0 = 16844097; // 0x1010541 field public static final int __removed1 = 16844099; // 0x1010543 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 @@ -1386,6 +1385,7 @@ package android { field public static final int targetId = 16843740; // 0x10103dc field public static final int targetName = 16843853; // 0x101044d field public static final int targetPackage = 16842785; // 0x1010021 + field public static final int targetProcess = 16844097; // 0x1010541 field public static final int targetSandboxVersion = 16844110; // 0x101054e field public static final int targetSdkVersion = 16843376; // 0x1010270 field public static final int taskAffinity = 16842770; // 0x1010012 @@ -4964,6 +4964,7 @@ package android.app { method public void addMonitor(android.app.Instrumentation.ActivityMonitor); method public android.app.Instrumentation.ActivityMonitor addMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean); method public android.app.Instrumentation.ActivityMonitor addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean); + method public void addResults(android.os.Bundle); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnDestroy(android.app.Activity); @@ -4988,6 +4989,7 @@ package android.app { method public android.os.Bundle getBinderCounts(); method public android.content.ComponentName getComponentName(); method public android.content.Context getContext(); + method public java.lang.String getProcessName(); method public android.content.Context getTargetContext(); method public android.app.UiAutomation getUiAutomation(); method public android.app.UiAutomation getUiAutomation(int); @@ -10358,6 +10360,7 @@ package android.content.pm { field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; + field public java.lang.String targetProcess; } public final class IntentFilterVerificationInfo implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index bfb0d14455efc..f07420a6a95b0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -203,7 +203,6 @@ package android { public static final class R.attr { ctor public R.attr(); - field public static final int __removed0 = 16844097; // 0x1010541 field public static final int __removed1 = 16844099; // 0x1010543 field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 @@ -1270,6 +1269,7 @@ package android { field public static final int targetId = 16843740; // 0x10103dc field public static final int targetName = 16843853; // 0x101044d field public static final int targetPackage = 16842785; // 0x1010021 + field public static final int targetProcess = 16844097; // 0x1010541 field public static final int targetSandboxVersion = 16844110; // 0x101054e field public static final int targetSdkVersion = 16843376; // 0x1010270 field public static final int taskAffinity = 16842770; // 0x1010012 @@ -4814,6 +4814,7 @@ package android.app { method public void addMonitor(android.app.Instrumentation.ActivityMonitor); method public android.app.Instrumentation.ActivityMonitor addMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean); method public android.app.Instrumentation.ActivityMonitor addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean); + method public void addResults(android.os.Bundle); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle); method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle); method public void callActivityOnDestroy(android.app.Activity); @@ -4838,6 +4839,7 @@ package android.app { method public android.os.Bundle getBinderCounts(); method public android.content.ComponentName getComponentName(); method public android.content.Context getContext(); + method public java.lang.String getProcessName(); method public android.content.Context getTargetContext(); method public android.app.UiAutomation getUiAutomation(); method public android.app.UiAutomation getUiAutomation(int); @@ -9931,6 +9933,7 @@ package android.content.pm { field public java.lang.String[] splitPublicSourceDirs; field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; + field public java.lang.String targetProcess; } public class LabeledIntent extends android.content.Intent { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 3cc6282d6ab3d..99ae96df01eb5 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -145,6 +145,7 @@ interface IActivityManager { int flags, in Bundle arguments, in IInstrumentationWatcher watcher, in IUiAutomationConnection connection, int userId, in String abiOverride); + void addInstrumentationResults(in IApplicationThread target, in Bundle results); void finishInstrumentation(in IApplicationThread target, int resultCode, in Bundle results); /** diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index b1bdea1cb5026..4db29fb5a72ac 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -186,11 +186,25 @@ public class Instrumentation { } } } - + + /** + * Report some results in the middle of instrumentation execution. Later results (including + * those provided by {@link #finish}) will be combined with {@link Bundle#putAll}. + */ + public void addResults(Bundle results) { + IActivityManager am = ActivityManager.getService(); + try { + am.addInstrumentationResults(mThread.getApplicationThread(), results); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + /** * Terminate instrumentation of the application. This will cause the * application process to exit, removing this instrumentation from the next - * time the application is started. + * time the application is started. If multiple processes are currently running + * for this instrumentation, all of those processes will be killed. * * @param resultCode Overall success/failure of instrumentation. * @param results Any results to send back to the code that started the @@ -277,6 +291,18 @@ public class Instrumentation { return mAppContext; } + /** + * Return the name of the process this instrumentation is running in. Note this should + * only be used for testing and debugging. If you are thinking about using this to, + * for example, conditionalize what is initialized in an Application class, it is strongly + * recommended to instead use lazy initialization (such as a getter for the state that + * only creates it when requested). This can greatly reduce the work your process does + * when created for secondary things, such as to receive a broadcast. + */ + public String getProcessName() { + return mThread.getProcessName(); + } + /** * Check whether this instrumentation was started with profiling enabled. * diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java index a135d8f11acd9..59c5307301709 100644 --- a/core/java/android/content/pm/InstrumentationInfo.java +++ b/core/java/android/content/pm/InstrumentationInfo.java @@ -33,6 +33,13 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { */ public String targetPackage; + /** + * Names of the process(es) this instrumentation will run in. If not specified, only + * runs in the main process of the targetPackage. Can either be a comma-separated list + * of process names or '*' for any process that launches to run targetPackage code. + */ + public String targetProcess; + /** * Full path to the base APK for this application. */ @@ -113,6 +120,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { public InstrumentationInfo(InstrumentationInfo orig) { super(orig); targetPackage = orig.targetPackage; + targetProcess = orig.targetProcess; sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; splitNames = orig.splitNames; @@ -141,6 +149,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); dest.writeString(targetPackage); + dest.writeString(targetProcess); dest.writeString(sourceDir); dest.writeString(publicSourceDir); dest.writeStringArray(splitNames); @@ -170,6 +179,7 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { private InstrumentationInfo(Parcel source) { super(source); targetPackage = source.readString(); + targetProcess = source.readString(); sourceDir = source.readString(); publicSourceDir = source.readString(); splitNames = source.readStringArray(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ff928a0dd81dd..e5dfe5425cd19 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1623,6 +1623,7 @@ public class PackageParser { return parseApkLite(apkPath, parser, attrs, flags, signatures, certificates); } catch (XmlPullParserException | IOException | RuntimeException e) { + Slog.w(TAG, "Failed to parse " + apkPath, e); throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to parse " + apkPath, e); } finally { @@ -3160,6 +3161,10 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage); a.info.targetPackage = str != null ? str.intern() : null; + str = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestInstrumentation_targetProcess); + a.info.targetProcess = str != null ? str.intern() : null; + a.info.handleProfiling = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling, false); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 6d0fdb6033ec5..92ef17edec7cc 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -885,6 +885,12 @@ will run against. --> + + + + diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index d795d80f52daa..1b48469b100e9 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2777,7 +2777,7 @@ - + diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java new file mode 100644 index 0000000000000..84e4ea9d0ee8d --- /dev/null +++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 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.server.am; + +import android.app.IInstrumentationWatcher; +import android.app.IUiAutomationConnection; +import android.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.os.Bundle; +import android.util.PrintWriterPrinter; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; + +class ActiveInstrumentation { + final ActivityManagerService mService; + + // Class installed to instrument app + ComponentName mClass; + + // All process names that should be instrumented + String[] mTargetProcesses; + + // The application being instrumented + ApplicationInfo mTargetInfo; + + // Where to save profiling + String mProfileFile; + + // Who is waiting + IInstrumentationWatcher mWatcher; + + // Connection to use the UI introspection APIs. + IUiAutomationConnection mUiAutomationConnection; + + // As given to us + Bundle mArguments; + + // Any intermediate results that have been collected. + Bundle mCurResults; + + // Copy of instrumentationClass. + ComponentName mResultClass; + + // Contains all running processes that have active instrumentation. + final ArrayList mRunningProcesses = new ArrayList<>(); + + // Set to true when we have told the watcher the instrumentation is finished. + boolean mFinished; + + ActiveInstrumentation(ActivityManagerService service) { + mService = service; + } + + void removeProcess(ProcessRecord proc) { + mFinished = true; + mRunningProcesses.remove(proc); + if (mRunningProcesses.size() == 0) { + mService.mActiveInstrumentation.remove(this); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("ActiveInstrumentation{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(' '); + sb.append(mClass.toShortString()); + if (mFinished) { + sb.append(" FINISHED"); + } + sb.append(" "); + sb.append(mRunningProcesses.size()); + sb.append(" procs"); + sb.append('}'); + return sb.toString(); + } + + void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("mClass="); pw.print(mClass); + pw.print(" mFinished="); pw.println(mFinished); + pw.print(prefix); pw.println("mRunningProcesses:"); + for (int i=0; i 0 && app.instr == null) { + for (int i = mActiveInstrumentation.size() - 1; i >= 0 && app.instr == null; i--) { + ActiveInstrumentation aInstr = mActiveInstrumentation.get(i); + if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) { + if (aInstr.mTargetProcesses.length == 0) { + // This is the wildcard mode, where every process brought up for + // the target instrumentation should be included. + if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) { + app.instr = aInstr; + aInstr.mRunningProcesses.add(app); + } + } else { + for (String proc : aInstr.mTargetProcesses) { + if (proc.equals(app.processName)) { + app.instr = aInstr; + aInstr.mRunningProcesses.add(app); + break; + } + } + } + } + } + } + checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); - thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, - profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, - app.instrumentationUiAutomationConnection, testMode, - mBinderTransactionTrackingEnabled, enableTrackAllocation, - isRestrictedBackupMode || !normalMode, app.persistent, - new Configuration(getGlobalConfiguration()), app.compat, - getCommonServicesLocked(app.isolated), - mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial); + if (app.instr != null) { + thread.bindApplication(processName, appInfo, providers, + app.instr.mClass, + profilerInfo, app.instr.mArguments, + app.instr.mWatcher, + app.instr.mUiAutomationConnection, testMode, + mBinderTransactionTrackingEnabled, enableTrackAllocation, + isRestrictedBackupMode || !normalMode, app.persistent, + new Configuration(getGlobalConfiguration()), app.compat, + getCommonServicesLocked(app.isolated), + mCoreSettingsObserver.getCoreSettingsLocked(), + buildSerial); + } else { + thread.bindApplication(processName, appInfo, providers, null, profilerInfo, + null, null, null, testMode, + mBinderTransactionTrackingEnabled, enableTrackAllocation, + isRestrictedBackupMode || !normalMode, app.persistent, + new Configuration(getGlobalConfiguration()), app.compat, + getCommonServicesLocked(app.isolated), + mCoreSettingsObserver.getCoreSettingsLocked(), + buildSerial); + } checkTime(startTime, "attachApplicationLocked: immediately after bindApplication"); updateLruProcessLocked(app, false, null); @@ -11595,7 +11637,7 @@ public class ActivityManagerService extends IActivityManager.Stub .getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList(); for (ApplicationInfo app : apps) { if (!"android".equals(app.packageName)) { - addAppLocked(app, false, null /* ABI override */); + addAppLocked(app, null, false, null /* ABI override */); } } } catch (RemoteException ex) { @@ -11783,17 +11825,18 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } - final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated, + final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, String abiOverride) { ProcessRecord app; if (!isolated) { - app = getProcessRecordLocked(info.processName, info.uid, true); + app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName, + info.uid, true); } else { app = null; } if (app == null) { - app = newProcessRecordLocked(info, null, isolated, 0); + app = newProcessRecordLocked(info, customProcess, isolated, 0); updateLruProcessLocked(app, false, null); updateOomAdjLocked(); } @@ -11814,7 +11857,8 @@ public class ActivityManagerService extends IActivityManager.Stub } if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); - startProcessLocked(app, "added application", app.processName, abiOverride, + startProcessLocked(app, "added application", + customProcess != null ? customProcess : app.processName, abiOverride, null /* entryPoint */, null /* entryPointArgs */); } @@ -12269,11 +12313,11 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { synchronized (mPidsSelfLocked) { final int callingPid = Binder.getCallingPid(); - ProcessRecord precessRecord = mPidsSelfLocked.get(callingPid); - if (precessRecord == null) { + ProcessRecord proc = mPidsSelfLocked.get(callingPid); + if (proc == null) { throw new SecurityException("Unknown process: " + callingPid); } - if (precessRecord.instrumentationUiAutomationConnection == null) { + if (proc.instr == null || proc.instr.mUiAutomationConnection == null) { throw new SecurityException("Only an instrumentation process " + "with a UiAutomation can call setUserIsMonkey"); } @@ -12330,7 +12374,7 @@ public class ActivityManagerService extends IActivityManager.Stub } public static long getInputDispatchingTimeoutLocked(ProcessRecord r) { - if (r != null && (r.instrumentationClass != null || r.usingWrapper)) { + if (r != null && (r.instr != null || r.usingWrapper)) { return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT; } return KEY_DISPATCHING_TIMEOUT; @@ -12391,7 +12435,7 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } - if (proc.instrumentationClass != null) { + if (proc.instr != null) { Bundle info = new Bundle(); info.putString("shortMsg", "keyDispatchingTimedOut"); info.putString("longMsg", annotation); @@ -14901,8 +14945,31 @@ public class ActivityManagerService extends IActivityManager.Stub printed = true; needSep = true; } - pw.println(String.format("%sIsolated #%2d: %s", - " ", i, r.toString())); + pw.print(" Isolated #"); pw.print(i); pw.print(": "); + pw.println(r); + } + } + + if (mActiveInstrumentation.size() > 0) { + boolean printed = false; + for (int i=0; i curReceivers = new ArraySet();// receivers currently running in the app long lastWakeTime; // How long proc held wake lock at last check @@ -248,19 +242,8 @@ final class ProcessRecord { pw.println("}"); } pw.print(prefix); pw.print("compat="); pw.println(compat); - if (instrumentationClass != null || instrumentationProfileFile != null - || instrumentationArguments != null) { - pw.print(prefix); pw.print("instrumentationClass="); - pw.print(instrumentationClass); - pw.print(" instrumentationProfileFile="); - pw.println(instrumentationProfileFile); - pw.print(prefix); pw.print("instrumentationArguments="); - pw.println(instrumentationArguments); - pw.print(prefix); pw.print("instrumentationInfo="); - pw.println(instrumentationInfo); - if (instrumentationInfo != null) { - instrumentationInfo.dump(new PrintWriterPrinter(pw), prefix + " "); - } + if (instr != null) { + pw.print(prefix); pw.print("instr="); pw.println(instr); } pw.print(prefix); pw.print("thread="); pw.println(thread); pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting="); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index e30bd5d966727..e5640c7e08c18 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -309,6 +309,7 @@ public class PackageParserTest { // Sanity check for InstrumentationInfo. assertEquals(a.info.targetPackage, b.info.targetPackage); + assertEquals(a.info.targetProcess, b.info.targetProcess); assertEquals(a.info.sourceDir, b.info.sourceDir); assertEquals(a.info.publicSourceDir, b.info.publicSourceDir); }