summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2016-08-22 17:00:05 -0700
committerDianne Hackborn <hackbod@google.com>2016-09-29 10:58:44 -0700
commit354736e196ff79962b3ddb52619a674044d773e2 (patch)
tree3a70250f8ba7f69f1961491c55e4b5b48ebe99ef
parent015deed8104aae1f306394cdf66088592995f0da (diff)
New infrastructure to switch remaining commands to "cmd" calls.
This introduces a new feature of the IBinder command protocol to allow the shell command implementation to call back into its caller to ask it to open files in the calling context. This is needed so that commands that have arguments specifying files can open those files as the calling shell, not the system (or whatever) process. To test this all out, move the "am start" implementation over to ActivityManagerShellCommand, in particular along with its option to specify a file in which to write profiling data. Test: Manual Change-Id: I0c1e3857defefbd19a2ac29413aafbb34b1e48a3
-rw-r--r--Android.mk1
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java267
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java38
-rw-r--r--core/java/android/os/Binder.java16
-rw-r--r--core/java/android/os/IBinder.java4
-rw-r--r--core/java/android/os/ShellCallback.java114
-rw-r--r--core/java/android/os/ShellCommand.java17
-rw-r--r--core/java/com/android/internal/os/BaseCommand.java12
-rw-r--r--core/java/com/android/internal/os/IShellCallback.aidl24
-rw-r--r--services/core/java/com/android/server/AppOpsService.java6
-rw-r--r--services/core/java/com/android/server/BatteryService.java8
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java295
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java2
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java12
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java6
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java8
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java2
-rw-r--r--tests/VoiceInteraction/AndroidManifest.xml2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java3
27 files changed, 617 insertions, 274 deletions
diff --git a/Android.mk b/Android.mk
index 121a38efd587..e352bc70d085 100644
--- a/Android.mk
+++ b/Android.mk
@@ -332,6 +332,7 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/os/IDropBoxManagerService.aidl \
core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl \
core/java/com/android/internal/os/IResultReceiver.aidl \
+ core/java/com/android/internal/os/IShellCallback.aidl \
core/java/com/android/internal/statusbar/IStatusBar.aidl \
core/java/com/android/internal/statusbar/IStatusBarService.aidl \
core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 91334bd87296..3759de2e7197 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -26,7 +26,6 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
import android.app.IActivityContainer;
import android.app.IActivityController;
import android.app.IActivityManager;
@@ -46,7 +45,6 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -55,10 +53,11 @@ import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -72,6 +71,7 @@ import com.android.internal.util.Preconditions;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -96,6 +96,7 @@ public class Am extends BaseCommand {
// Amount we reduce the stack size by when testing a task re-size.
private static final int STACK_BOUNDS_INSET = 10;
+ public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private IActivityManager mAm;
private IPackageManager mPm;
@@ -198,7 +199,7 @@ public class Am extends BaseCommand {
" --track-allocation: enable tracking of object allocations\n" +
" --user <USER_ID> | current: Specify which user to run as; if not\n" +
" specified then run as the current user.\n" +
- " --stack <STACK_ID>: Specify into which stack should the activity be put." +
+ " --stack <STACK_ID>: Specify into which stack should the activity be put.\n" +
"\n" +
"am startservice: start a Service. Options are:\n" +
" --user <USER_ID> | current: Specify which user to run as; if not\n" +
@@ -385,17 +386,13 @@ public class Am extends BaseCommand {
String op = nextArgRequired();
if (op.equals("start")) {
- runStart();
+ runAmCmd(getRawArgs());
} else if (op.equals("startservice")) {
runStartService();
} else if (op.equals("stopservice")) {
runStopService();
- } else if (op.equals("force-stop")) {
- runForceStop();
- } else if (op.equals("kill")) {
- runKill();
- } else if (op.equals("kill-all")) {
- runKillAll();
+ } else if (op.equals("force-stop") || op.equals("kill") || op.equals("kill-all")) {
+ runAmCmd(getRawArgs());
} else if (op.equals("instrument")) {
runInstrument();
} else if (op.equals("trace-ipc")) {
@@ -475,6 +472,49 @@ public class Am extends BaseCommand {
return userId;
}
+ static final class MyShellCallback extends ShellCallback {
+ @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ File file = new File(path);
+ //System.err.println("Opening file: " + file.getAbsolutePath());
+ //Log.i("Am", "Opening file: " + file.getAbsolutePath());
+ final ParcelFileDescriptor fd;
+ try {
+ fd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
+ } catch (FileNotFoundException e) {
+ String msg = "Unable to open file " + path + ": " + e;
+ System.err.println(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (seLinuxContext != null) {
+ final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+ if (!SELinux.checkSELinuxAccess(seLinuxContext, tcon, "file", "write")) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ String msg = "System server has no access to file context " + tcon;
+ System.err.println(msg + " (from path " + file.getAbsolutePath()
+ + ", context " + seLinuxContext + ")");
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ return fd;
+ }
+ }
+
+ void runAmCmd(String[] args) throws AndroidException {
+ try {
+ mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+ args, new MyShellCallback(), new ResultReceiver(null) { });
+ } catch (RemoteException e) {
+ System.err.println(NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException("Can't call activity manager; is the system running?");
+ }
+ }
+
private Intent makeIntent(int defUser) throws URISyntaxException {
mStartFlags = 0;
mWaitOption = false;
@@ -558,211 +598,6 @@ public class Am extends BaseCommand {
}
}
- private void runStart() throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
-
- if (mUserId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't start service with user 'all'");
- return;
- }
-
- String mimeType = intent.getType();
- if (mimeType == null && intent.getData() != null
- && "content".equals(intent.getData().getScheme())) {
- mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);
- }
-
-
- do {
- if (mStopOption) {
- String packageName;
- if (intent.getComponent() != null) {
- packageName = intent.getComponent().getPackageName();
- } else {
- List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
- mUserId).getList();
- if (activities == null || activities.size() <= 0) {
- System.err.println("Error: Intent does not match any activities: "
- + intent);
- return;
- } else if (activities.size() > 1) {
- System.err.println("Error: Intent matches multiple activities; can't stop: "
- + intent);
- return;
- }
- packageName = activities.get(0).activityInfo.packageName;
- }
- System.out.println("Stopping: " + packageName);
- mAm.forceStopPackage(packageName, mUserId);
- Thread.sleep(250);
- }
-
- System.out.println("Starting: " + intent);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- ParcelFileDescriptor fd = null;
- ProfilerInfo profilerInfo = null;
-
- if (mProfileFile != null) {
- try {
- fd = openForSystemServer(
- new File(mProfileFile),
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_WRITE_ONLY);
- } catch (FileNotFoundException e) {
- System.err.println("Error: Unable to open file: " + mProfileFile);
- System.err.println("Consider using a file under /data/local/tmp/");
- return;
- }
- profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
- }
-
- IActivityManager.WaitResult result = null;
- int res;
- final long startTime = SystemClock.uptimeMillis();
- ActivityOptions options = null;
- if (mStackId != INVALID_STACK_ID) {
- options = ActivityOptions.makeBasic();
- options.setLaunchStackId(mStackId);
- }
- if (mWaitOption) {
- result = mAm.startActivityAndWait(null, null, intent, mimeType,
- null, null, 0, mStartFlags, profilerInfo,
- options != null ? options.toBundle() : null, mUserId);
- res = result.result;
- } else {
- res = mAm.startActivityAsUser(null, null, intent, mimeType,
- null, null, 0, mStartFlags, profilerInfo,
- options != null ? options.toBundle() : null, mUserId);
- }
- final long endTime = SystemClock.uptimeMillis();
- PrintStream out = mWaitOption ? System.out : System.err;
- boolean launched = false;
- switch (res) {
- case ActivityManager.START_SUCCESS:
- launched = true;
- break;
- case ActivityManager.START_SWITCHES_CANCELED:
- launched = true;
- out.println(
- "Warning: Activity not started because the "
- + " current activity is being kept for the user.");
- break;
- case ActivityManager.START_DELIVERED_TO_TOP:
- launched = true;
- out.println(
- "Warning: Activity not started, intent has "
- + "been delivered to currently running "
- + "top-most instance.");
- break;
- case ActivityManager.START_RETURN_INTENT_TO_CALLER:
- launched = true;
- out.println(
- "Warning: Activity not started because intent "
- + "should be handled by the caller");
- break;
- case ActivityManager.START_TASK_TO_FRONT:
- launched = true;
- out.println(
- "Warning: Activity not started, its current "
- + "task has been brought to the front");
- break;
- case ActivityManager.START_INTENT_NOT_RESOLVED:
- out.println(
- "Error: Activity not started, unable to "
- + "resolve " + intent.toString());
- break;
- case ActivityManager.START_CLASS_NOT_FOUND:
- out.println(NO_CLASS_ERROR_CODE);
- out.println("Error: Activity class " +
- intent.getComponent().toShortString()
- + " does not exist.");
- break;
- case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
- out.println(
- "Error: Activity not started, you requested to "
- + "both forward and receive its result");
- break;
- case ActivityManager.START_PERMISSION_DENIED:
- out.println(
- "Error: Activity not started, you do not "
- + "have permission to access it.");
- break;
- case ActivityManager.START_NOT_VOICE_COMPATIBLE:
- out.println(
- "Error: Activity not started, voice control not allowed for: "
- + intent);
- break;
- case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
- out.println(
- "Error: Not allowed to start background user activity"
- + " that shouldn't be displayed for all users.");
- break;
- default:
- out.println(
- "Error: Activity not started, unknown error code " + res);
- break;
- }
- if (mWaitOption && launched) {
- if (result == null) {
- result = new IActivityManager.WaitResult();
- result.who = intent.getComponent();
- }
- System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
- if (result.who != null) {
- System.out.println("Activity: " + result.who.flattenToShortString());
- }
- if (result.thisTime >= 0) {
- System.out.println("ThisTime: " + result.thisTime);
- }
- if (result.totalTime >= 0) {
- System.out.println("TotalTime: " + result.totalTime);
- }
- System.out.println("WaitTime: " + (endTime-startTime));
- System.out.println("Complete");
- }
- mRepeat--;
- if (mRepeat > 0) {
- mAm.unhandledBack();
- }
- } while (mRepeat > 0);
- }
-
- private void runForceStop() throws Exception {
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- mAm.forceStopPackage(nextArgRequired(), userId);
- }
-
- private void runKill() throws Exception {
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- mAm.killBackgroundProcesses(nextArgRequired(), userId);
- }
-
- private void runKillAll() throws Exception {
- mAm.killAllBackgroundProcesses();
- }
-
private void sendBroadcast() throws Exception {
Intent intent = makeIntent(UserHandle.USER_CURRENT);
IntentReceiver receiver = new IntentReceiver();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 32a8088e9c4e..ace4e3284930 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -49,9 +49,12 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IUserManager;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -68,6 +71,7 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -284,13 +288,45 @@ public final class Pm {
}
}
+ static final class MyShellCallback extends ShellCallback {
+ @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ File file = new File(path);
+ final ParcelFileDescriptor fd;
+ try {
+ fd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
+ } catch (FileNotFoundException e) {
+ String msg = "Unable to open file " + path + ": " + e;
+ System.err.println(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (seLinuxContext != null) {
+ final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+ if (!SELinux.checkSELinuxAccess(seLinuxContext, tcon, "file", "write")) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ String msg = "System server has no access to file context " + tcon;
+ System.err.println(msg + " (from path " + file.getAbsolutePath()
+ + ", context " + seLinuxContext + ")");
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ return fd;
+ }
+ }
+
private int runShellCommand(String serviceName, String[] args) {
final HandlerThread handlerThread = new HandlerThread("results");
handlerThread.start();
try {
ServiceManager.getService(serviceName).shellCommand(
FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
- args, new ResultReceiver(new Handler(handlerThread.getLooper())));
+ args, new MyShellCallback(),
+ new ResultReceiver(new Handler(handlerThread.getLooper())));
return 0;
} catch (RemoteException e) {
e.printStackTrace();
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ea8ba2f1e6df..cf77567540f0 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -23,7 +23,6 @@ import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
@@ -361,13 +360,14 @@ public class Binder implements IBinder {
ParcelFileDescriptor out = data.readFileDescriptor();
ParcelFileDescriptor err = data.readFileDescriptor();
String[] args = data.readStringArray();
+ ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
try {
if (out != null) {
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
- args, resultReceiver);
+ args, shellCallback, resultReceiver);
}
} finally {
IoUtils.closeQuietly(in);
@@ -459,13 +459,15 @@ public class Binder implements IBinder {
* @param out The raw file descriptor that normal command messages should be written to.
* @param err The raw file descriptor that command error messages should be written to.
* @param args Command-line arguments.
+ * @param callback Callback through which to interact with the invoking shell.
* @param resultReceiver Called when the command has finished executing, with the result code.
* @throws RemoteException
* @hide
*/
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
- onShellCommand(in, out, err, args, resultReceiver);
+ String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) throws RemoteException {
+ onShellCommand(in, out, err, args, callback, resultReceiver);
}
/**
@@ -477,7 +479,7 @@ public class Binder implements IBinder {
* @hide
*/
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) throws RemoteException {
FileOutputStream fout = new FileOutputStream(err != null ? err : out);
PrintWriter pw = new FastPrintWriter(fout);
pw.println("No shell command implementation.");
@@ -650,13 +652,15 @@ final class BinderProxy implements IBinder {
}
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeFileDescriptor(in);
data.writeFileDescriptor(out);
data.writeFileDescriptor(err);
data.writeStringArray(args);
+ ShellCallback.writeToParcel(callback, data);
resultReceiver.writeToParcel(data, 0);
try {
transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 0fa87509a77a..f762a052cb41 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -220,11 +220,13 @@ public interface IBinder {
* @param out The raw file descriptor that normal command messages should be written to.
* @param err The raw file descriptor that command error messages should be written to.
* @param args Command-line arguments.
+ * @param shellCallback Optional callback to the caller's shell to perform operations in it.
* @param resultReceiver Called when the command has finished executing, with the result code.
* @hide
*/
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException;
+ String[] args, ShellCallback shellCallback,
+ ResultReceiver resultReceiver) throws RemoteException;
/**
* Perform a generic operation with the object.
diff --git a/core/java/android/os/ShellCallback.java b/core/java/android/os/ShellCallback.java
new file mode 100644
index 000000000000..e7fe697f9c54
--- /dev/null
+++ b/core/java/android/os/ShellCallback.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import com.android.internal.os.IShellCallback;
+
+/**
+ * Special-purpose API for use with {@link IBinder#shellCommand IBinder.shellCommand} for
+ * performing operations back on the invoking shell.
+ * @hide
+ */
+public class ShellCallback implements Parcelable {
+ final static String TAG = "ShellCallback";
+
+ final static boolean DEBUG = false;
+
+ final boolean mLocal;
+
+ IShellCallback mShellCallback;
+
+ class MyShellCallback extends IShellCallback.Stub {
+ public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) {
+ return onOpenOutputFile(path, seLinuxContext);
+ }
+ }
+
+ /**
+ * Create a new ShellCallback to receive requests.
+ */
+ public ShellCallback() {
+ mLocal = true;
+ }
+
+ /**
+ * Ask the shell to open a file for writing. This will truncate the file if it
+ * already exists. It will create the file if it doesn't exist.
+ * @param path Path of the file to be opened/created.
+ * @param seLinuxContext Optional SELinux context that must be allowed to have
+ * access to the file; if null, nothing is required.
+ */
+ public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) {
+ if (DEBUG) Log.d(TAG, "openOutputFile " + this + ": mLocal=" + mLocal
+ + " mShellCallback=" + mShellCallback);
+
+ if (mLocal) {
+ return onOpenOutputFile(path, seLinuxContext);
+ }
+
+ if (mShellCallback != null) {
+ try {
+ return mShellCallback.openOutputFile(path, seLinuxContext);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure opening " + path, e);
+ }
+ }
+ return null;
+ }
+
+ public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ return null;
+ }
+
+ public static void writeToParcel(ShellCallback callback, Parcel out) {
+ if (callback == null) {
+ out.writeStrongBinder(null);
+ } else {
+ callback.writeToParcel(out, 0);
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ synchronized (this) {
+ if (mShellCallback == null) {
+ mShellCallback = new MyShellCallback();
+ }
+ out.writeStrongBinder(mShellCallback.asBinder());
+ }
+ }
+
+ ShellCallback(Parcel in) {
+ mLocal = false;
+ mShellCallback = IShellCallback.Stub.asInterface(in.readStrongBinder());
+ }
+
+ public static final Parcelable.Creator<ShellCallback> CREATOR
+ = new Parcelable.Creator<ShellCallback>() {
+ public ShellCallback createFromParcel(Parcel in) {
+ return new ShellCallback(in);
+ }
+ public ShellCallback[] newArray(int size) {
+ return new ShellCallback[size];
+ }
+ };
+}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index fc804e592148..831c9b27ac45 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -40,6 +40,7 @@ public abstract class ShellCommand {
private FileDescriptor mOut;
private FileDescriptor mErr;
private String[] mArgs;
+ private ShellCallback mShellCallback;
private ResultReceiver mResultReceiver;
private String mCmd;
@@ -55,12 +56,13 @@ public abstract class ShellCommand {
private InputStream mInputStream;
public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, int firstArgPos) {
+ String[] args, ShellCallback callback, int firstArgPos) {
mTarget = target;
mIn = in;
mOut = out;
mErr = err;
mArgs = args;
+ mShellCallback = callback;
mResultReceiver = null;
mCmd = null;
mArgPos = firstArgPos;
@@ -74,7 +76,7 @@ public abstract class ShellCommand {
}
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
String cmd;
int start;
if (args != null && args.length > 0) {
@@ -84,7 +86,7 @@ public abstract class ShellCommand {
cmd = null;
start = 0;
}
- init(target, in, out, err, args, start);
+ init(target, in, out, err, args, callback, start);
mCmd = cmd;
mResultReceiver = resultReceiver;
@@ -105,7 +107,7 @@ public abstract class ShellCommand {
// go.
PrintWriter eout = getErrPrintWriter();
eout.println();
- eout.println("Exception occurred while dumping:");
+ eout.println("Exception occurred while executing:");
e.printStackTrace(eout);
} finally {
if (DEBUG) Slog.d(TAG, "Flushing output streams on " + mTarget);
@@ -257,6 +259,13 @@ public abstract class ShellCommand {
return arg;
}
+ /**
+ * Return the {@link ShellCallback} for communicating back with the calling shell.
+ */
+ public ShellCallback getShellCallback() {
+ return mShellCallback;
+ }
+
public int handleDefaultCommands(String cmd) {
if ("dump".equals(cmd)) {
String[] newArgs = new String[mArgs.length-1];
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c067da7e1dec..3baccee049b0 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -36,6 +36,8 @@ public abstract class BaseCommand {
public static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+ private String[] mRawArgs;
+
/**
* Call to run the command.
*/
@@ -45,7 +47,8 @@ public abstract class BaseCommand {
return;
}
- mArgs.init(null, null, null, null, args, 0);
+ mRawArgs = args;
+ mArgs.init(null, null, null, null, args, null, 0);
try {
onRun();
@@ -109,4 +112,11 @@ public abstract class BaseCommand {
public String nextArgRequired() {
return mArgs.getNextArgRequired();
}
+
+ /**
+ * Return the original raw argument list supplied to the command.
+ */
+ public String[] getRawArgs() {
+ return mRawArgs;
+ }
}
diff --git a/core/java/com/android/internal/os/IShellCallback.aidl b/core/java/com/android/internal/os/IShellCallback.aidl
new file mode 100644
index 000000000000..57d67890d840
--- /dev/null
+++ b/core/java/com/android/internal/os/IShellCallback.aidl
@@ -0,0 +1,24 @@
+/**
+ * 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.
+ */
+
+package com.android.internal.os;
+
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+interface IShellCallback {
+ ParcelFileDescriptor openOutputFile(String path, String seLinuxContext);
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 238bf927c43e..7c2eea3f68aa 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -51,6 +51,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.os.storage.MountServiceInternal;
@@ -1787,8 +1788,9 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
- (new Shell(this, this)).exec(this, in, out, err, args, resultReceiver);
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new Shell(this, this)).exec(this, in, out, err, args, callback, resultReceiver);
}
static void dumpCommandHelp(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 6b517210ef31..d2cfb6d51002 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -20,6 +20,7 @@ import android.database.ContentObserver;
import android.os.BatteryStats;
import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.ShellCommand;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
@@ -789,7 +790,7 @@ public final class BatteryService extends SystemService {
pw.println(" technology: " + mBatteryProps.batteryTechnology);
} else {
Shell shell = new Shell();
- shell.exec(mBinderService, null, fd, null, args, new ResultReceiver(null));
+ shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
}
}
}
@@ -875,8 +876,9 @@ public final class BatteryService extends SystemService {
}
@Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
- (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 6b73fec8c920..dbc1f31a9348 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -60,6 +60,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -1232,8 +1233,8 @@ public class DeviceIdleController extends SystemService
}
@Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
- (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
}
@@ -2838,7 +2839,8 @@ public class DeviceIdleController extends SystemService
shell.userId = userId;
String[] newArgs = new String[args.length-i];
System.arraycopy(args, i, newArgs, 0, args.length-i);
- shell.exec(mBinderService, null, fd, null, newArgs, new ResultReceiver(null));
+ shell.exec(mBinderService, null, fd, null, newArgs, null,
+ new ResultReceiver(null));
return;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e5b611ca3231..03610868d04b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -182,6 +182,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -14024,9 +14025,10 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
(new ActivityManagerShellCommand(this, false)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
@Override
@@ -14249,7 +14251,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// Dumping a single activity?
if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacks)) {
ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
- int res = shell.exec(this, null, fd, null, args, new ResultReceiver(null));
+ int res = shell.exec(this, null, fd, null, args, null,
+ new ResultReceiver(null));
if (res < 0) {
pw.println("Bad activity command, or no activities match: " + cmd);
pw.println("Use -h for help.");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index adf6d3670af3..aed9fa4435fd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -17,26 +17,57 @@
package com.android.server.am;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
import android.app.IActivityManager;
+import android.app.ProfilerInfo;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.DebugUtils;
import java.io.PrintWriter;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
class ActivityManagerShellCommand extends ShellCommand {
+ public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
// IPC interface to activity manager -- don't need to do additional security checks.
final IActivityManager mInterface;
// Internal service impl -- must perform security checks before touching.
final ActivityManagerService mInternal;
+ // Convenience for interacting with package manager.
+ final IPackageManager mPm;
+
+ private int mStartFlags = 0;
+ private boolean mWaitOption = false;
+ private boolean mStopOption = false;
+
+ private int mRepeat = 0;
+ private int mUserId;
+ private String mReceiverPermission;
+
+ private String mProfileFile;
+ private int mSamplingInterval;
+ private boolean mAutoStop;
+ private int mStackId;
+
final boolean mDumping;
ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
mInterface = service;
mInternal = service;
+ mPm = AppGlobals.getPackageManager();
mDumping = dumping;
}
@@ -48,6 +79,15 @@ class ActivityManagerShellCommand extends ShellCommand {
PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
+ case "start":
+ case "start-activity":
+ return runStartActivity(pw);
+ case "startservice":
+ case "start-service":
+ return 1; //runStartService(pw);
+ case "stopservice":
+ case "stop-service":
+ return 1; //runStopService(pw);
case "force-stop":
return runForceStop(pw);
case "kill":
@@ -75,6 +115,241 @@ class ActivityManagerShellCommand extends ShellCommand {
return -1;
}
+ private Intent makeIntent(int defUser) throws URISyntaxException {
+ mStartFlags = 0;
+ mWaitOption = false;
+ mStopOption = false;
+ mRepeat = 0;
+ mProfileFile = null;
+ mSamplingInterval = 0;
+ mAutoStop = false;
+ mUserId = defUser;
+ mStackId = INVALID_STACK_ID;
+
+ return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
+ @Override
+ public boolean handleOption(String opt, ShellCommand cmd) {
+ if (opt.equals("-D")) {
+ mStartFlags |= ActivityManager.START_FLAG_DEBUG;
+ } else if (opt.equals("-N")) {
+ mStartFlags |= ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+ } else if (opt.equals("-W")) {
+ mWaitOption = true;
+ } else if (opt.equals("-P")) {
+ mProfileFile = getNextArgRequired();
+ mAutoStop = true;
+ } else if (opt.equals("--start-profiler")) {
+ mProfileFile = getNextArgRequired();
+ mAutoStop = false;
+ } else if (opt.equals("--sampling")) {
+ mSamplingInterval = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("-R")) {
+ mRepeat = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("-S")) {
+ mStopOption = true;
+ } else if (opt.equals("--track-allocation")) {
+ mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
+ } else if (opt.equals("--user")) {
+ mUserId = UserHandle.parseUserArg(getNextArgRequired());
+ } else if (opt.equals("--receiver-permission")) {
+ mReceiverPermission = getNextArgRequired();
+ } else if (opt.equals("--stack")) {
+ mStackId = Integer.parseInt(getNextArgRequired());
+ } else {
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+
+ ParcelFileDescriptor openOutputFile(String path) {
+ try {
+ ParcelFileDescriptor pfd = getShellCallback().openOutputFile(path,
+ "u:r:system_server:s0");
+ if (pfd != null) {
+ return pfd;
+ }
+ } catch (RuntimeException e) {
+ getErrPrintWriter().println("Failure opening file: " + e.getMessage());
+ }
+ getErrPrintWriter().println("Error: Unable to open file: " + path);
+ getErrPrintWriter().println("Consider using a file under /data/local/tmp/");
+ return null;
+ }
+
+ int runStartActivity(PrintWriter pw) throws RemoteException {
+ Intent intent;
+ try {
+ intent = makeIntent(UserHandle.USER_CURRENT);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+
+ if (mUserId == UserHandle.USER_ALL) {
+ getErrPrintWriter().println("Error: Can't start service with user 'all'");
+ return 1;
+ }
+
+ String mimeType = intent.getType();
+ if (mimeType == null && intent.getData() != null
+ && "content".equals(intent.getData().getScheme())) {
+ mimeType = mInterface.getProviderMimeType(intent.getData(), mUserId);
+ }
+
+ do {
+ if (mStopOption) {
+ String packageName;
+ if (intent.getComponent() != null) {
+ packageName = intent.getComponent().getPackageName();
+ } else {
+ List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
+ mUserId).getList();
+ if (activities == null || activities.size() <= 0) {
+ getErrPrintWriter().println("Error: Intent does not match any activities: "
+ + intent);
+ return 1;
+ } else if (activities.size() > 1) {
+ getErrPrintWriter().println(
+ "Error: Intent matches multiple activities; can't stop: "
+ + intent);
+ return 1;
+ }
+ packageName = activities.get(0).activityInfo.packageName;
+ }
+ pw.println("Stopping: " + packageName);
+ mInterface.forceStopPackage(packageName, mUserId);
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ ProfilerInfo profilerInfo = null;
+
+ if (mProfileFile != null) {
+ ParcelFileDescriptor fd = openOutputFile(mProfileFile);
+ if (fd == null) {
+ return 1;
+ }
+ profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
+ }
+
+ pw.println("Starting: " + intent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ IActivityManager.WaitResult result = null;
+ int res;
+ final long startTime = SystemClock.uptimeMillis();
+ ActivityOptions options = null;
+ if (mStackId != INVALID_STACK_ID) {
+ options = ActivityOptions.makeBasic();
+ options.setLaunchStackId(mStackId);
+ }
+ if (mWaitOption) {
+ result = mInterface.startActivityAndWait(null, null, intent, mimeType,
+ null, null, 0, mStartFlags, profilerInfo,
+ options != null ? options.toBundle() : null, mUserId);
+ res = result.result;
+ } else {
+ res = mInterface.startActivityAsUser(null, null, intent, mimeType,
+ null, null, 0, mStartFlags, profilerInfo,
+ options != null ? options.toBundle() : null, mUserId);
+ }
+ final long endTime = SystemClock.uptimeMillis();
+ PrintWriter out = mWaitOption ? pw : getErrPrintWriter();
+ boolean launched = false;
+ switch (res) {
+ case ActivityManager.START_SUCCESS:
+ launched = true;
+ break;
+ case ActivityManager.START_SWITCHES_CANCELED:
+ launched = true;
+ out.println(
+ "Warning: Activity not started because the "
+ + " current activity is being kept for the user.");
+ break;
+ case ActivityManager.START_DELIVERED_TO_TOP:
+ launched = true;
+ out.println(
+ "Warning: Activity not started, intent has "
+ + "been delivered to currently running "
+ + "top-most instance.");
+ break;
+ case ActivityManager.START_RETURN_INTENT_TO_CALLER:
+ launched = true;
+ out.println(
+ "Warning: Activity not started because intent "
+ + "should be handled by the caller");
+ break;
+ case ActivityManager.START_TASK_TO_FRONT:
+ launched = true;
+ out.println(
+ "Warning: Activity not started, its current "
+ + "task has been brought to the front");
+ break;
+ case ActivityManager.START_INTENT_NOT_RESOLVED:
+ out.println(
+ "Error: Activity not started, unable to "
+ + "resolve " + intent.toString());
+ break;
+ case ActivityManager.START_CLASS_NOT_FOUND:
+ out.println(NO_CLASS_ERROR_CODE);
+ out.println("Error: Activity class " +
+ intent.getComponent().toShortString()
+ + " does not exist.");
+ break;
+ case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
+ out.println(
+ "Error: Activity not started, you requested to "
+ + "both forward and receive its result");
+ break;
+ case ActivityManager.START_PERMISSION_DENIED:
+ out.println(
+ "Error: Activity not started, you do not "
+ + "have permission to access it.");
+ break;
+ case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+ out.println(
+ "Error: Activity not started, voice control not allowed for: "
+ + intent);
+ break;
+ case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
+ out.println(
+ "Error: Not allowed to start background user activity"
+ + " that shouldn't be displayed for all users.");
+ break;
+ default:
+ out.println(
+ "Error: Activity not started, unknown error code " + res);
+ break;
+ }
+ if (mWaitOption && launched) {
+ if (result == null) {
+ result = new IActivityManager.WaitResult();
+ result.who = intent.getComponent();
+ }
+ pw.println("Status: " + (result.timeout ? "timeout" : "ok"));
+ if (result.who != null) {
+ pw.println("Activity: " + result.who.flattenToShortString());
+ }
+ if (result.thisTime >= 0) {
+ pw.println("ThisTime: " + result.thisTime);
+ }
+ if (result.totalTime >= 0) {
+ pw.println("TotalTime: " + result.totalTime);
+ }
+ pw.println("WaitTime: " + (endTime-startTime));
+ pw.println("Complete");
+ }
+ mRepeat--;
+ if (mRepeat > 0) {
+ mInterface.unhandledBack();
+ }
+ } while (mRepeat > 0);
+ return 0;
+ }
+
int runIsUserStopped(PrintWriter pw) {
int userId = UserHandle.parseUserArg(getNextArgRequired());
boolean stopped = mInternal.isUserStopped(userId);
@@ -223,6 +498,24 @@ class ActivityManagerShellCommand extends ShellCommand {
pw.println("Activity manager (activity) commands:");
pw.println(" help");
pw.println(" Print this help text.");
+ pw.println(" start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]");
+ pw.println(" [--sampling INTERVAL] [-R COUNT] [-S]");
+ pw.println(" [--track-allocation] [--user <USER_ID> | current] <INTENT>");
+ pw.println(" Start an Activity. Options are:");
+ pw.println(" -D: enable debugging");
+ pw.println(" -N: enable native debugging");
+ pw.println(" -W: wait for launch to complete");
+ pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
+ pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
+ pw.println(" between samples (use with --start-profiler)");
+ pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
+ pw.println(" -R: repeat the activity launch <COUNT> times. Prior to each repeat,");
+ pw.println(" the top activity will be finished.");
+ pw.println(" -S: force stop the target app before starting the activity");
+ pw.println(" --track-allocation: enable tracking of object allocations");
+ pw.println(" --user <USER_ID> | current: Specify which user to run as; if not");
+ pw.println(" specified then run as the current user.");
+ pw.println(" --stack <STACK_ID>: Specify into which stack should the activity be put.");
pw.println(" force-stop [--user <USER_ID> | all | current] <PACKAGE>");
pw.println(" Completely stop the given application package.");
pw.println(" kill [--user <USER_ID> | all | current] <PACKAGE>");
@@ -241,6 +534,8 @@ class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Optionally controls lenient background check mode, returns current mode.");
pw.println(" get-uid-state <UID>");
pw.println(" Gets the process state of an app given its <UID>.");
+ pw.println();
+ Intent.printIntentArgsHelp(pw, "");
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7708b0205074..6b00c86bf699 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5029,7 +5029,7 @@ final class ActivityStack {
if (top >= 0) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
int activityTop = activities.size() - 1;
- if (activityTop > 0) {
+ if (activityTop >= 0) {
finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
"unhandled-back", true);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 74095acca16a..87f4030a9049 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -18,8 +18,8 @@ package com.android.server.input;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Build;
import android.os.LocaleList;
+import android.os.ShellCallback;
import android.util.Log;
import android.view.Display;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
@@ -91,10 +91,8 @@ import android.view.PointerIcon;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicy;
-import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Toast;
import java.io.File;
import java.io.FileDescriptor;
@@ -103,11 +101,8 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -1739,8 +1734,9 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
- (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
public int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 9d931467d914..fe3a02d193f6 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -44,9 +44,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -62,6 +60,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1718,9 +1717,9 @@ public final class JobSchedulerService extends com.android.server.SystemService
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
};
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 6381aa747022..547cc51e9a05 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -123,7 +123,6 @@ import android.net.LinkProperties;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkPolicy;
-import android.net.NetworkPolicyManager;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
import android.net.NetworkTemplate;
@@ -144,6 +143,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -2386,9 +2386,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new NetworkPolicyManagerShellCommand(mContext, this)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
@Override
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 689917cd670a..f777aaebdd14 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -28,6 +28,7 @@ import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.storage.StorageManager;
import android.util.Log;
import android.util.Slog;
@@ -107,9 +108,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new OtaDexoptShellCommand(this)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c76302c1053c..13428b2e2c8a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -185,6 +185,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
@@ -18397,9 +18398,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
(new PackageManagerShellCommand(this)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
@Override
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 13f558e3dd13..19bf751417ce 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -67,6 +67,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -3366,13 +3367,14 @@ public class ShortcutService extends IShortcutService.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
enforceShell();
final long token = injectClearCallingIdentity();
try {
- final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+ final int status = (new MyShellCommand()).exec(this, in, out, err, args, callback,
+ resultReceiver);
resultReceiver.send(status, null);
} finally {
injectRestoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c1cb032ce97f..c6b09d190bff 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -62,6 +62,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.os.UserManager;
@@ -3209,8 +3210,9 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
- (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 552803f47652..7b7db0e1440b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -28,23 +28,21 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
-import android.view.KeyEvent;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.FastPrintWriter;
import com.android.server.LocalServices;
import com.android.server.notification.NotificationDelegate;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -849,9 +847,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new StatusBarShellCommand(this)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
// ================================================================================
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 846169cbf9c3..43cdf5978068 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -25,10 +25,10 @@ import android.os.Binder;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
-import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -140,9 +140,10 @@ public class WebViewUpdateService extends SystemService {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
(new WebViewUpdateServiceShellCommand(this)).exec(
- this, in, out, err, args, resultReceiver);
+ this, in, out, err, args, callback, resultReceiver);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 3c99174142cb..25f9100d9252 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -68,7 +68,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
/* fdin*/ null,
/* fdout*/ fd.getFileDescriptor(),
/* fderr*/ fd.getFileDescriptor(),
- args, rr);
+ args, null, rr);
}
return readAll(out);
} finally {
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index cbc6c76aa934..5fdf0dd3992c 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.voiceinteraction">
+ <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="25" />
+
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.READ_LOGS" />
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 8d93b7f32100..a0b297701869 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -76,6 +76,7 @@ import android.os.Parcel;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -1180,7 +1181,7 @@ public final class BridgeContext extends Context {
@Override
public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ResultReceiver resultReceiver) {
+ String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver) {
}
};
}