am instrument gets protobuf
Refactor the am instrument command and add a version that
outputs protobuf in addition to the old one that prints
loosely formatted text.
Change-Id: I34079d8af2b7b6c6c59837d54719806109ba286c
Test: bit tool
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk
index f8350dc..5586dd4 100644
--- a/cmds/am/Android.mk
+++ b/cmds/am/Android.mk
@@ -3,8 +3,11 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-proto-files-under, proto)
LOCAL_MODULE := am
+LOCAL_PROTOC_OPTIMIZE_TYPE := stream
include $(BUILD_JAVA_LIBRARY)
include $(CLEAR_VARS)
@@ -13,3 +16,14 @@
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(call all-proto-files-under, proto)
+LOCAL_MODULE := libinstrumentation
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(call intermediates-dir-for,STATIC_LIBRARIES,libinstrumentation,HOST,,,)/proto/$(LOCAL_PATH)/proto
+include $(BUILD_HOST_STATIC_LIBRARY)
+
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
new file mode 100644
index 0000000..12a18a2
--- /dev/null
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+package android.am;
+
+option java_package = "com.android.commands.am";
+
+message ResultsBundleEntry {
+ optional string key = 1;
+
+ optional string value_string = 2;
+ optional sint32 value_int = 3;
+ optional float value_float = 4;
+ optional double value_double = 5;
+ optional sint64 value_long = 6;
+ optional ResultsBundle value_bundle = 7;
+}
+
+message ResultsBundle {
+ repeated ResultsBundleEntry entries = 1;
+}
+
+message TestStatus {
+ optional sint32 result_code = 3;
+ optional ResultsBundle results = 4;
+}
+
+enum SessionStatusCode {
+ /**
+ * The command ran successfully. This does not imply that the tests passed.
+ */
+ SESSION_FINISHED = 0;
+
+ /**
+ * There was an unrecoverable error running the tests.
+ */
+ SESSION_ABORTED = 1;
+}
+
+message SessionStatus {
+ optional SessionStatusCode status_code = 1;
+ optional string error_text = 2;
+ optional sint32 result_code = 3;
+ optional ResultsBundle results = 4;
+}
+
+message Session {
+ repeated TestStatus test_status = 1;
+ optional SessionStatus session_status = 2;
+}
+
+
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index e197bfc..91a4549 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1,20 +1,18 @@
/*
-**
-** Copyright 2007, 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.
-*/
-
+ * Copyright (C) 2007 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.commands.am;
@@ -235,6 +233,7 @@
" -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
" common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
" -p <FILE>: write profiling data to <FILE>\n" +
+ " -m: Write output as protobuf (machine readable)\n" +
" -w: wait for instrumentation to finish before returning. Required for\n" +
" test runners.\n" +
" --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
@@ -543,208 +542,43 @@
receiver.waitForFinish();
}
- private void runInstrument() throws Exception {
- String profileFile = null;
- boolean wait = false;
- boolean rawMode = false;
- boolean no_window_animation = false;
- int userId = UserHandle.USER_CURRENT;
- Bundle args = new Bundle();
- String argKey = null, argValue = null;
- IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
- String abi = null;
+ public void runInstrument() throws Exception {
+ Instrument instrument = new Instrument(mAm, mPm);
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-p")) {
- profileFile = nextArgRequired();
+ instrument.profileFile = nextArgRequired();
} else if (opt.equals("-w")) {
- wait = true;
+ instrument.wait = true;
} else if (opt.equals("-r")) {
- rawMode = true;
+ instrument.rawMode = true;
+ } else if (opt.equals("-m")) {
+ instrument.proto = true;
} else if (opt.equals("-e")) {
- argKey = nextArgRequired();
- argValue = nextArgRequired();
- args.putString(argKey, argValue);
+ final String argKey = nextArgRequired();
+ final String argValue = nextArgRequired();
+ instrument.args.putString(argKey, argValue);
} else if (opt.equals("--no_window_animation")
|| opt.equals("--no-window-animation")) {
- no_window_animation = true;
+ instrument.noWindowAnimation = true;
} else if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
+ instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
- abi = nextArgRequired();
+ instrument.abi = nextArgRequired();
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
- if (userId == UserHandle.USER_ALL) {
+ if (instrument.userId == UserHandle.USER_ALL) {
System.err.println("Error: Can't start instrumentation with user 'all'");
return;
}
- String cnArg = nextArgRequired();
+ instrument.componentNameArg = nextArgRequired();
- ComponentName cn;
- if (cnArg.contains("/")) {
- cn = ComponentName.unflattenFromString(cnArg);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
- } else {
- List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
-
- final int numInfos = infos == null ? 0: infos.size();
- List<ComponentName> cns = new ArrayList<>();
- for (int i = 0; i < numInfos; i++) {
- InstrumentationInfo info = infos.get(i);
-
- ComponentName c = new ComponentName(info.packageName, info.name);
- if (cnArg.equals(info.packageName)) {
- cns.add(c);
- }
- }
-
- if (cns.size() == 0) {
- throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
- } else if (cns.size() == 1) {
- cn = cns.get(0);
- } else {
- StringBuilder cnsStr = new StringBuilder();
- final int numCns = cns.size();
- for (int i = 0; i < numCns; i++) {
- cnsStr.append(cns.get(i).flattenToString());
- cnsStr.append(", ");
- }
-
- // Remove last ", "
- cnsStr.setLength(cnsStr.length() - 2);
-
- throw new IllegalArgumentException("Found multiple instrumentations: "
- + cnsStr.toString());
- }
- }
-
- InstrumentationWatcher watcher = null;
- UiAutomationConnection connection = null;
- if (wait) {
- watcher = new InstrumentationWatcher();
- watcher.setRawOutput(rawMode);
- connection = new UiAutomationConnection();
- }
-
- float[] oldAnims = null;
- if (no_window_animation) {
- oldAnims = wm.getAnimationScales();
- wm.setAnimationScale(0, 0.0f);
- wm.setAnimationScale(1, 0.0f);
- }
-
- if (abi != null) {
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- boolean matched = false;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- matched = true;
- break;
- }
- }
-
- if (!matched) {
- throw new AndroidException(
- "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
- }
- }
-
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
- throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
- }
-
- if (watcher != null) {
- if (!watcher.waitForFinish()) {
- System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
- }
- }
-
- if (oldAnims != null) {
- wm.setAnimationScales(oldAnims);
- }
- }
-
- private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
- private boolean mFinished = false;
- private boolean mRawMode = false;
-
- /**
- * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode",
- * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
- * @param rawMode true for raw mode, false for pretty mode.
- */
- public void setRawOutput(boolean rawMode) {
- mRawMode = rawMode;
- }
-
- @Override
- public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- System.out.print(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- System.out.println(
- "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
- }
- }
- System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
- }
- notifyAll();
- }
- }
-
- @Override
- public void instrumentationFinished(ComponentName name, int resultCode,
- Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- System.out.println(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- System.out.println(
- "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
- }
- }
- System.out.println("INSTRUMENTATION_CODE: " + resultCode);
- }
- mFinished = true;
- notifyAll();
- }
- }
-
- public boolean waitForFinish() {
- synchronized (this) {
- while (!mFinished) {
- try {
- if (!mAm.asBinder().pingBinder()) {
- return false;
- }
- wait(1000);
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
- }
- return true;
- }
+ instrument.run();
}
}
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
new file mode 100644
index 0000000..8eefd25
--- /dev/null
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2007 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.commands.am;
+
+import android.app.IActivityManager;
+import android.app.IInstrumentationWatcher;
+import android.app.Instrumentation;
+import android.app.UiAutomationConnection;
+import android.content.ComponentName;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AndroidException;
+import android.util.proto.ProtoOutputStream;
+import android.view.IWindowManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Runs the am instrument command
+ */
+public class Instrument {
+ private final IActivityManager mAm;
+ private final IPackageManager mPm;
+ private final IWindowManager mWm;
+
+ // Command line arguments
+ public String profileFile = null;
+ public boolean wait = false;
+ public boolean rawMode = false;
+ public boolean proto = false;
+ public boolean noWindowAnimation = false;
+ public String abi = null;
+ public int userId = UserHandle.USER_CURRENT;
+ public Bundle args = new Bundle();
+ // Required
+ public String componentNameArg;
+
+ /**
+ * Construct the instrument command runner.
+ */
+ public Instrument(IActivityManager am, IPackageManager pm) {
+ mAm = am;
+ mPm = pm;
+ mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+ }
+
+ /**
+ * Base class for status reporting.
+ *
+ * All the methods on this interface are called within the synchronized block
+ * of the InstrumentationWatcher, so calls are in order. However, that means
+ * you must be careful not to do blocking operations because you don't know
+ * exactly the locking dependencies.
+ */
+ private interface StatusReporter {
+ /**
+ * Status update for tests.
+ */
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results);
+
+ /**
+ * The tests finished.
+ */
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results);
+
+ /**
+ * @param errorText a description of the error
+ * @param commandError True if the error is related to the commandline, as opposed
+ * to a test failing.
+ */
+ public void onError(String errorText, boolean commandError);
+ }
+
+ /**
+ * Printer for the 'classic' text based status reporting.
+ */
+ private class TextStatusReporter implements StatusReporter {
+ private boolean mRawMode;
+
+ /**
+ * Human-ish readable output.
+ *
+ * @param rawMode In "raw mode" (true), all bundles are dumped.
+ * In "pretty mode" (false), if a bundle includes
+ * Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
+ */
+ public TextStatusReporter(boolean rawMode) {
+ mRawMode = rawMode;
+ }
+
+ @Override
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ // pretty printer mode?
+ String pretty = null;
+ if (!mRawMode && results != null) {
+ pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+ }
+ if (pretty != null) {
+ System.out.print(pretty);
+ } else {
+ if (results != null) {
+ for (String key : results.keySet()) {
+ System.out.println(
+ "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
+ }
+ }
+ System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
+ }
+ }
+
+ @Override
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ // pretty printer mode?
+ String pretty = null;
+ if (!mRawMode && results != null) {
+ pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+ }
+ if (pretty != null) {
+ System.out.println(pretty);
+ } else {
+ if (results != null) {
+ for (String key : results.keySet()) {
+ System.out.println(
+ "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
+ }
+ }
+ System.out.println("INSTRUMENTATION_CODE: " + resultCode);
+ }
+ }
+
+ @Override
+ public void onError(String errorText, boolean commandError) {
+ // The regular BaseCommand error printing will print the commandErrors.
+ if (!commandError) {
+ System.out.println(errorText);
+ }
+ }
+ }
+
+ /**
+ * Printer for the protobuf based status reporting.
+ */
+ private class ProtoStatusReporter implements StatusReporter {
+ @Override
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS);
+
+ proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
+ writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
+
+ proto.endRepeatedObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ @Override
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
+
+ proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ InstrumentationData.SESSION_FINISHED);
+ proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
+ writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
+
+ proto.endObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ @Override
+ public void onError(String errorText, boolean commandError) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
+
+ proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ InstrumentationData.SESSION_ABORTED);
+ proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+
+ proto.endObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) {
+ final long bundleToken = proto.startObject(fieldId);
+
+ for (final String key: bundle.keySet()) {
+ final long entryToken = proto.startRepeatedObject(
+ InstrumentationData.ResultsBundle.ENTRIES);
+
+ proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key);
+
+ final Object val = bundle.get(key);
+ if (val instanceof String) {
+ proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
+ (String)val);
+ } else if (val instanceof Byte) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Byte)val).intValue());
+ } else if (val instanceof Double) {
+ proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE,
+ ((Double)val).doubleValue());
+ } else if (val instanceof Float) {
+ proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT,
+ ((Float)val).floatValue());
+ } else if (val instanceof Integer) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Integer)val).intValue());
+ } else if (val instanceof Long) {
+ proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG,
+ ((Long)val).longValue());
+ } else if (val instanceof Short) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Short)val).intValue());
+ } else if (val instanceof Bundle) {
+ writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE,
+ (Bundle)val);
+ }
+
+ proto.endRepeatedObject(entryToken);
+ }
+
+ proto.endObject(bundleToken);
+ }
+
+ private void writeProtoToStdout(ProtoOutputStream proto) {
+ try {
+ System.out.write(proto.getBytes());
+ System.out.flush();
+ } catch (IOException ex) {
+ System.err.println("Error writing finished response: ");
+ ex.printStackTrace(System.err);
+ }
+ }
+ }
+
+
+ /**
+ * Callbacks from the remote instrumentation instance.
+ */
+ private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
+ private final StatusReporter mReporter;
+
+ private boolean mFinished = false;
+
+ public InstrumentationWatcher(StatusReporter reporter) {
+ mReporter = reporter;
+ }
+
+ @Override
+ public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
+ synchronized (this) {
+ mReporter.onInstrumentationStatusLocked(name, resultCode, results);
+ notifyAll();
+ }
+ }
+
+ @Override
+ public void instrumentationFinished(ComponentName name, int resultCode, Bundle results) {
+ synchronized (this) {
+ mReporter.onInstrumentationFinishedLocked(name, resultCode, results);
+ mFinished = true;
+ notifyAll();
+ }
+ }
+
+ public boolean waitForFinish() {
+ synchronized (this) {
+ while (!mFinished) {
+ try {
+ if (!mAm.asBinder().pingBinder()) {
+ return false;
+ }
+ wait(1000);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Figure out which component they really meant.
+ */
+ private ComponentName parseComponentName(String cnArg) throws Exception {
+ if (cnArg.contains("/")) {
+ ComponentName cn = ComponentName.unflattenFromString(cnArg);
+ if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+ return cn;
+ } else {
+ List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
+
+ final int numInfos = infos == null ? 0: infos.size();
+ ArrayList<ComponentName> cns = new ArrayList<>();
+ for (int i = 0; i < numInfos; i++) {
+ InstrumentationInfo info = infos.get(i);
+
+ ComponentName c = new ComponentName(info.packageName, info.name);
+ if (cnArg.equals(info.packageName)) {
+ cns.add(c);
+ }
+ }
+
+ if (cns.size() == 0) {
+ throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
+ } else if (cns.size() == 1) {
+ return cns.get(0);
+ } else {
+ StringBuilder cnsStr = new StringBuilder();
+ final int numCns = cns.size();
+ for (int i = 0; i < numCns; i++) {
+ cnsStr.append(cns.get(i).flattenToString());
+ cnsStr.append(", ");
+ }
+
+ // Remove last ", "
+ cnsStr.setLength(cnsStr.length() - 2);
+
+ throw new IllegalArgumentException("Found multiple instrumentations: "
+ + cnsStr.toString());
+ }
+ }
+ }
+
+ /**
+ * Run the instrumentation.
+ */
+ public void run() throws Exception {
+ StatusReporter reporter = null;
+ float[] oldAnims = null;
+
+ try {
+ // Choose which output we will do.
+ if (proto) {
+ reporter = new ProtoStatusReporter();
+ } else if (wait) {
+ reporter = new TextStatusReporter(rawMode);
+ }
+
+ // Choose whether we have to wait for the results.
+ InstrumentationWatcher watcher = null;
+ UiAutomationConnection connection = null;
+ if (reporter != null) {
+ watcher = new InstrumentationWatcher(reporter);
+ connection = new UiAutomationConnection();
+ }
+
+ // Set the window animation if necessary
+ if (noWindowAnimation) {
+ oldAnims = mWm.getAnimationScales();
+ mWm.setAnimationScale(0, 0.0f);
+ mWm.setAnimationScale(1, 0.0f);
+ }
+
+ // Figure out which component we are tring to do.
+ final ComponentName cn = parseComponentName(componentNameArg);
+
+ // Choose an ABI if necessary
+ if (abi != null) {
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ boolean matched = false;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ throw new AndroidException(
+ "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
+ }
+ }
+
+ // Start the instrumentation
+ if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+ abi)) {
+ throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
+ }
+
+ // If we have been requested to wait, do so until the instrumentation is finished.
+ if (watcher != null) {
+ if (!watcher.waitForFinish()) {
+ reporter.onError("INSTRUMENTATION_ABORTED: System has crashed.", false);
+ return;
+ }
+ }
+ } catch (Exception ex) {
+ // Report failures
+ if (reporter != null) {
+ reporter.onError(ex.getMessage(), true);
+ }
+
+ // And re-throw the exception
+ throw ex;
+ } finally {
+ // Clean up
+ if (oldAnims != null) {
+ mWm.setAnimationScales(oldAnims);
+ }
+ }
+ }
+}
+