From 340417356d92d0db71d0692344e66886ca795dfd Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 31 Jan 2017 15:27:13 -0800 Subject: [PATCH] Implement issue #30977956: Enable Instrumentation tests for multi-process apps New android:targetProcess attribute on allows you to specify the processes the instrumentation will run in. This reworks how instrumentation is run in the activity manager to better formalize its state and semantics, allowing us to keep track of it across multiple processes. This also clearly defines what happens when multiple instrumentations are running at the same time, which is useful for writing CTS tests that test the instrumentation APIs themselves. Adds a couple new APIs to Instrumentation that helps with the new situation where instrumentation can run concurrently in multiple processes. Test: new CTS tests added (textXxxProcessInstrumentation in ActivityManagerTest.java in cts/tests/app/src) Change-Id: I2811e6c75bc98d4856045b2f0a45fb24af5d366f --- api/current.txt | 5 +- api/system-current.txt | 5 +- api/test-current.txt | 5 +- core/java/android/app/IActivityManager.aidl | 1 + core/java/android/app/Instrumentation.java | 30 ++- .../content/pm/InstrumentationInfo.java | 10 + .../android/content/pm/PackageParser.java | 5 + core/res/res/values/attrs_manifest.xml | 7 + core/res/res/values/public.xml | 2 +- .../server/am/ActiveInstrumentation.java | 122 +++++++++ .../server/am/ActivityManagerService.java | 240 ++++++++++++++---- .../java/com/android/server/am/AppErrors.java | 2 +- .../com/android/server/am/ProcessRecord.java | 23 +- .../android/server/pm/PackageParserTest.java | 1 + 14 files changed, 376 insertions(+), 82 deletions(-) create mode 100644 services/core/java/com/android/server/am/ActiveInstrumentation.java 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); }