From 3cdb56efea044112bfe1b97b3ed78ee05e0dba46 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 11 Nov 2015 12:45:44 -0800 Subject: [PATCH] Some debugging improvements. - Fix dumping of package manager intent filters so the option to print the filter detail works again. - Extend dump resolvers to allow you to specify the specific types of resolvers you'd like to dump. - Add new package manager commands for querying activities, services, receivers. - Move the code for parsing a command line into an intent to the framework, so it can be used by the new package manager commands and later elsewhere. Change-Id: I56ea2bb8c3dd0e5198ee333be8f41ad9dcdb626f --- cmds/am/src/com/android/commands/am/Am.java | 393 ++-------------- core/java/android/content/Intent.java | 440 +++++++++++++++++- core/java/android/os/ShellCommand.java | 26 +- core/java/android/os/UserHandle.java | 17 + .../com/android/internal/os/BaseCommand.java | 60 +-- .../com/android/server/IntentResolver.java | 2 +- .../am/ActivityManagerShellCommand.java | 20 +- .../server/pm/PackageManagerService.java | 67 ++- .../server/pm/PackageManagerShellCommand.java | 123 ++++- 9 files changed, 695 insertions(+), 453 deletions(-) diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 7f33cb5df1a3f..62e0919a3d845 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -55,6 +55,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SELinux; import android.os.ServiceManager; +import android.os.ShellCommand; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; @@ -72,6 +73,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; +import java.io.PrintWriter; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; @@ -117,7 +119,8 @@ public class Am extends BaseCommand { @Override public void onShowUsage(PrintStream out) { - out.println( + PrintWriter pw = new PrintWriter(out); + pw.println( "usage: am [subcommand] [options]\n" + "usage: am start [-D] [-W] [-P ] [--start-profiler ]\n" + " [--sampling INTERVAL] [-R COUNT] [-S]\n" + @@ -337,52 +340,10 @@ public class Am extends BaseCommand { "am send-trim-memory: send a memory trim event to a .\n" + "\n" + "am get-current-user: returns id of the current foreground user.\n" + - "\n" + - " specifications include these flags and arguments:\n" + - " [-a ] [-d ] [-t ]\n" + - " [-c [-c ] ...]\n" + - " [-e|--es ...]\n" + - " [--esn ...]\n" + - " [--ez ...]\n" + - " [--ei ...]\n" + - " [--el ...]\n" + - " [--ef ...]\n" + - " [--eu ...]\n" + - " [--ecn ]\n" + - " [--eia [, [,)\n" + - " [--ela [, [,)\n" + - " [--efa [, [,)\n" + - " [--esa [, [,; to embed a comma into a string,\n" + - " escape it using \"\\,\")\n" + - " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + - " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]\n" + - " [--debug-log-resolution] [--exclude-stopped-packages]\n" + - " [--include-stopped-packages]\n" + - " [--activity-brought-to-front] [--activity-clear-top]\n" + - " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" + - " [--activity-launched-from-history] [--activity-multiple-task]\n" + - " [--activity-no-animation] [--activity-no-history]\n" + - " [--activity-no-user-action] [--activity-previous-is-top]\n" + - " [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" + - " [--activity-single-top] [--activity-clear-task]\n" + - " [--activity-task-on-home]\n" + - " [--receiver-registered-only] [--receiver-replace-pending]\n" + - " [--selector]\n" + - " [ | | ]\n" - ); + "\n" + ); + Intent.printIntentArgsHelp(pw, ""); + pw.flush(); } @Override @@ -486,10 +447,6 @@ public class Am extends BaseCommand { } private Intent makeIntent(int defUser) throws URISyntaxException { - Intent intent = new Intent(); - Intent baseIntent = intent; - boolean hasIntentInfo = false; - mStartFlags = 0; mWaitOption = false; mStopOption = false; @@ -498,316 +455,38 @@ public class Am extends BaseCommand { mSamplingInterval = 0; mAutoStop = false; mUserId = defUser; - Uri data = null; - String type = null; - String opt; - while ((opt=nextOption()) != null) { - if (opt.equals("-a")) { - intent.setAction(nextArgRequired()); - if (intent == baseIntent) { - hasIntentInfo = true; - } - } else if (opt.equals("-d")) { - data = Uri.parse(nextArgRequired()); - if (intent == baseIntent) { - hasIntentInfo = true; - } - } else if (opt.equals("-t")) { - type = nextArgRequired(); - if (intent == baseIntent) { - hasIntentInfo = true; - } - } else if (opt.equals("-c")) { - intent.addCategory(nextArgRequired()); - if (intent == baseIntent) { - hasIntentInfo = true; - } - } else if (opt.equals("-e") || opt.equals("--es")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - intent.putExtra(key, value); - } else if (opt.equals("--esn")) { - String key = nextArgRequired(); - intent.putExtra(key, (String) null); - } else if (opt.equals("--ei")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - intent.putExtra(key, Integer.decode(value)); - } else if (opt.equals("--eu")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - intent.putExtra(key, Uri.parse(value)); - } else if (opt.equals("--ecn")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - ComponentName cn = ComponentName.unflattenFromString(value); - if (cn == null) throw new IllegalArgumentException("Bad component name: " + value); - intent.putExtra(key, cn); - } else if (opt.equals("--eia")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - String[] strings = value.split(","); - int[] list = new int[strings.length]; - for (int i = 0; i < strings.length; i++) { - list[i] = Integer.decode(strings[i]); - } - intent.putExtra(key, list); - } else if (opt.equals("--eial")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - String[] strings = value.split(","); - ArrayList list = new ArrayList<>(strings.length); - for (int i = 0; i < strings.length; i++) { - list.add(Integer.decode(strings[i])); - } - intent.putExtra(key, list); - } else if (opt.equals("--el")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - intent.putExtra(key, Long.valueOf(value)); - } else if (opt.equals("--ela")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - String[] strings = value.split(","); - long[] list = new long[strings.length]; - for (int i = 0; i < strings.length; i++) { - list[i] = Long.valueOf(strings[i]); - } - intent.putExtra(key, list); - hasIntentInfo = true; - } else if (opt.equals("--elal")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - String[] strings = value.split(","); - ArrayList list = new ArrayList<>(strings.length); - for (int i = 0; i < strings.length; i++) { - list.add(Long.valueOf(strings[i])); - } - intent.putExtra(key, list); - hasIntentInfo = true; - } else if (opt.equals("--ef")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - intent.putExtra(key, Float.valueOf(value)); - hasIntentInfo = true; - } else if (opt.equals("--efa")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - String[] strings = value.split(","); - float[] list = new float[strings.length]; - for (int i = 0; i < strings.length; i++) { - list[i] = Float.valueOf(strings[i]); - } - intent.putExtra(key, list); - hasIntentInfo = true; - } else if (opt.equals("--efal")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - String[] strings = value.split(","); - ArrayList list = new ArrayList<>(strings.length); - for (int i = 0; i < strings.length; i++) { - list.add(Float.valueOf(strings[i])); - } - intent.putExtra(key, list); - hasIntentInfo = true; - } else if (opt.equals("--esa")) { - String key = nextArgRequired(); - String value = nextArgRequired(); - // Split on commas unless they are preceeded by an escape. - // The escape character must be escaped for the string and - // again for the regex, thus four escape characters become one. - String[] strings = value.split("(? list = new ArrayList<>(strings.length); - for (int i = 0; i < strings.length; i++) { - list.add(strings[i]); - } - intent.putExtra(key, list); - hasIntentInfo = true; - } else if (opt.equals("--ez")) { - String key = nextArgRequired(); - String value = nextArgRequired().toLowerCase(); - // Boolean.valueOf() results in false for anything that is not "true", which is - // error-prone in shell commands - boolean arg; - if ("true".equals(value) || "t".equals(value)) { - arg = true; - } else if ("false".equals(value) || "f".equals(value)) { - arg = false; + return Intent.parseCommandArgs(mArgs, new Intent.CommandOptionHandler() { + @Override + public boolean handleOption(String opt, ShellCommand cmd) { + if (opt.equals("-D")) { + mStartFlags |= ActivityManager.START_FLAG_DEBUG; + } else if (opt.equals("-W")) { + mWaitOption = true; + } else if (opt.equals("-P")) { + mProfileFile = nextArgRequired(); + mAutoStop = true; + } else if (opt.equals("--start-profiler")) { + mProfileFile = nextArgRequired(); + mAutoStop = false; + } else if (opt.equals("--sampling")) { + mSamplingInterval = Integer.parseInt(nextArgRequired()); + } else if (opt.equals("-R")) { + mRepeat = Integer.parseInt(nextArgRequired()); + } 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 = parseUserArg(nextArgRequired()); + } else if (opt.equals("--receiver-permission")) { + mReceiverPermission = nextArgRequired(); } else { - try { - arg = Integer.decode(value) != 0; - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Invalid boolean value: " + value); - } + return false; } - - intent.putExtra(key, arg); - } else if (opt.equals("-n")) { - String str = nextArgRequired(); - ComponentName cn = ComponentName.unflattenFromString(str); - if (cn == null) throw new IllegalArgumentException("Bad component name: " + str); - intent.setComponent(cn); - if (intent == baseIntent) { - hasIntentInfo = true; - } - } else if (opt.equals("-p")) { - String str = nextArgRequired(); - intent.setPackage(str); - if (intent == baseIntent) { - hasIntentInfo = true; - } - } else if (opt.equals("-f")) { - String str = nextArgRequired(); - intent.setFlags(Integer.decode(str).intValue()); - } else if (opt.equals("--grant-read-uri-permission")) { - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - } else if (opt.equals("--grant-write-uri-permission")) { - intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - } else if (opt.equals("--grant-persistable-uri-permission")) { - intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); - } else if (opt.equals("--grant-prefix-uri-permission")) { - intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); - } else if (opt.equals("--exclude-stopped-packages")) { - intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); - } else if (opt.equals("--include-stopped-packages")) { - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - } else if (opt.equals("--debug-log-resolution")) { - intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); - } else if (opt.equals("--activity-brought-to-front")) { - intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); - } else if (opt.equals("--activity-clear-top")) { - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - } else if (opt.equals("--activity-clear-when-task-reset")) { - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - } else if (opt.equals("--activity-exclude-from-recents")) { - intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - } else if (opt.equals("--activity-launched-from-history")) { - intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); - } else if (opt.equals("--activity-multiple-task")) { - intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - } else if (opt.equals("--activity-no-animation")) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - } else if (opt.equals("--activity-no-history")) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - } else if (opt.equals("--activity-no-user-action")) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); - } else if (opt.equals("--activity-previous-is-top")) { - intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); - } else if (opt.equals("--activity-reorder-to-front")) { - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - } else if (opt.equals("--activity-reset-task-if-needed")) { - intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - } else if (opt.equals("--activity-single-top")) { - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - } else if (opt.equals("--activity-clear-task")) { - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - } else if (opt.equals("--activity-task-on-home")) { - intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); - } else if (opt.equals("--receiver-registered-only")) { - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - } else if (opt.equals("--receiver-replace-pending")) { - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - } else if (opt.equals("--selector")) { - intent.setDataAndType(data, type); - intent = new Intent(); - } else if (opt.equals("-D")) { - mStartFlags |= ActivityManager.START_FLAG_DEBUG; - } else if (opt.equals("-W")) { - mWaitOption = true; - } else if (opt.equals("-P")) { - mProfileFile = nextArgRequired(); - mAutoStop = true; - } else if (opt.equals("--start-profiler")) { - mProfileFile = nextArgRequired(); - mAutoStop = false; - } else if (opt.equals("--sampling")) { - mSamplingInterval = Integer.parseInt(nextArgRequired()); - } else if (opt.equals("-R")) { - mRepeat = Integer.parseInt(nextArgRequired()); - } 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 = parseUserArg(nextArgRequired()); - } else if (opt.equals("--receiver-permission")) { - mReceiverPermission = nextArgRequired(); - } else { - throw new IllegalArgumentException("Unknown option: " + opt); + return true; } - } - intent.setDataAndType(data, type); - - final boolean hasSelector = intent != baseIntent; - if (hasSelector) { - // A selector was specified; fix up. - baseIntent.setSelector(intent); - intent = baseIntent; - } - - String arg = nextArg(); - baseIntent = null; - if (arg == null) { - if (hasSelector) { - // If a selector has been specified, and no arguments - // have been supplied for the main Intent, then we can - // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't - // need to have a component name specified yet, the - // selector will take care of that. - baseIntent = new Intent(Intent.ACTION_MAIN); - baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); - } - } else if (arg.indexOf(':') >= 0) { - // The argument is a URI. Fully parse it, and use that result - // to fill in any data not specified so far. - baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME - | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE); - } else if (arg.indexOf('/') >= 0) { - // The argument is a component name. Build an Intent to launch - // it. - baseIntent = new Intent(Intent.ACTION_MAIN); - baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); - baseIntent.setComponent(ComponentName.unflattenFromString(arg)); - } else { - // Assume the argument is a package name. - baseIntent = new Intent(Intent.ACTION_MAIN); - baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); - baseIntent.setPackage(arg); - } - if (baseIntent != null) { - Bundle extras = intent.getExtras(); - intent.replaceExtras((Bundle)null); - Bundle uriExtras = baseIntent.getExtras(); - baseIntent.replaceExtras((Bundle)null); - if (intent.getAction() != null && baseIntent.getCategories() != null) { - HashSet cats = new HashSet(baseIntent.getCategories()); - for (String c : cats) { - baseIntent.removeCategory(c); - } - } - intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR); - if (extras == null) { - extras = uriExtras; - } else if (uriExtras != null) { - uriExtras.putAll(extras); - extras = uriExtras; - } - intent.replaceExtras(extras); - hasIntentInfo = true; - } - - if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied"); - return intent; + }); } private void runStartService() throws Exception { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 68b77fe78673b..30fe531c5186f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -18,6 +18,7 @@ package android.content; import android.content.pm.ApplicationInfo; import android.os.ResultReceiver; +import android.os.ShellCommand; import android.provider.MediaStore; import android.util.ArraySet; @@ -57,11 +58,13 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; +import java.io.PrintWriter; import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -3054,21 +3057,21 @@ public class Intent implements Parcelable, Cloneable { /** * Thermal state when the device is normal. This state is sent in the - * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}. + * {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}. * {@hide} */ public static final int EXTRA_THERMAL_STATE_NORMAL = 0; /** * Thermal state where the device is approaching its maximum threshold. This state is sent in - * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}. + * the {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}. * {@hide} */ public static final int EXTRA_THERMAL_STATE_WARNING = 1; /** * Thermal state where the device has reached its maximum threshold. This state is sent in the - * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}. + * {@link #ACTION_THERMAL_EVENT} broadcast as {@link #EXTRA_THERMAL_STATE}. * {@hide} */ public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2; @@ -5083,6 +5086,437 @@ public class Intent implements Parcelable, Cloneable { return intent; } + /** @hide */ + public interface CommandOptionHandler { + boolean handleOption(String opt, ShellCommand cmd); + } + + /** @hide */ + public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler) + throws URISyntaxException { + Intent intent = new Intent(); + Intent baseIntent = intent; + boolean hasIntentInfo = false; + + Uri data = null; + String type = null; + + String opt; + while ((opt=cmd.getNextOption()) != null) { + switch (opt) { + case "-a": + intent.setAction(cmd.getNextArgRequired()); + if (intent == baseIntent) { + hasIntentInfo = true; + } + break; + case "-d": + data = Uri.parse(cmd.getNextArgRequired()); + if (intent == baseIntent) { + hasIntentInfo = true; + } + break; + case "-t": + type = cmd.getNextArgRequired(); + if (intent == baseIntent) { + hasIntentInfo = true; + } + break; + case "-c": + intent.addCategory(cmd.getNextArgRequired()); + if (intent == baseIntent) { + hasIntentInfo = true; + } + break; + case "-e": + case "--es": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + intent.putExtra(key, value); + } + break; + case "--esn": { + String key = cmd.getNextArgRequired(); + intent.putExtra(key, (String) null); + } + break; + case "--ei": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + intent.putExtra(key, Integer.decode(value)); + } + break; + case "--eu": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + intent.putExtra(key, Uri.parse(value)); + } + break; + case "--ecn": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + ComponentName cn = ComponentName.unflattenFromString(value); + if (cn == null) + throw new IllegalArgumentException("Bad component name: " + value); + intent.putExtra(key, cn); + } + break; + case "--eia": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + String[] strings = value.split(","); + int[] list = new int[strings.length]; + for (int i = 0; i < strings.length; i++) { + list[i] = Integer.decode(strings[i]); + } + intent.putExtra(key, list); + } + break; + case "--eial": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + String[] strings = value.split(","); + ArrayList list = new ArrayList<>(strings.length); + for (int i = 0; i < strings.length; i++) { + list.add(Integer.decode(strings[i])); + } + intent.putExtra(key, list); + } + break; + case "--el": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + intent.putExtra(key, Long.valueOf(value)); + } + break; + case "--ela": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + String[] strings = value.split(","); + long[] list = new long[strings.length]; + for (int i = 0; i < strings.length; i++) { + list[i] = Long.valueOf(strings[i]); + } + intent.putExtra(key, list); + hasIntentInfo = true; + } + break; + case "--elal": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + String[] strings = value.split(","); + ArrayList list = new ArrayList<>(strings.length); + for (int i = 0; i < strings.length; i++) { + list.add(Long.valueOf(strings[i])); + } + intent.putExtra(key, list); + hasIntentInfo = true; + } + break; + case "--ef": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + intent.putExtra(key, Float.valueOf(value)); + hasIntentInfo = true; + } + break; + case "--efa": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + String[] strings = value.split(","); + float[] list = new float[strings.length]; + for (int i = 0; i < strings.length; i++) { + list[i] = Float.valueOf(strings[i]); + } + intent.putExtra(key, list); + hasIntentInfo = true; + } + break; + case "--efal": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + String[] strings = value.split(","); + ArrayList list = new ArrayList<>(strings.length); + for (int i = 0; i < strings.length; i++) { + list.add(Float.valueOf(strings[i])); + } + intent.putExtra(key, list); + hasIntentInfo = true; + } + break; + case "--esa": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired(); + // Split on commas unless they are preceeded by an escape. + // The escape character must be escaped for the string and + // again for the regex, thus four escape characters become one. + String[] strings = value.split("(? list = new ArrayList<>(strings.length); + for (int i = 0; i < strings.length; i++) { + list.add(strings[i]); + } + intent.putExtra(key, list); + hasIntentInfo = true; + } + break; + case "--ez": { + String key = cmd.getNextArgRequired(); + String value = cmd.getNextArgRequired().toLowerCase(); + // Boolean.valueOf() results in false for anything that is not "true", which is + // error-prone in shell commands + boolean arg; + if ("true".equals(value) || "t".equals(value)) { + arg = true; + } else if ("false".equals(value) || "f".equals(value)) { + arg = false; + } else { + try { + arg = Integer.decode(value) != 0; + } catch (NumberFormatException ex) { + throw new IllegalArgumentException("Invalid boolean value: " + value); + } + } + + intent.putExtra(key, arg); + } + break; + case "-n": { + String str = cmd.getNextArgRequired(); + ComponentName cn = ComponentName.unflattenFromString(str); + if (cn == null) + throw new IllegalArgumentException("Bad component name: " + str); + intent.setComponent(cn); + if (intent == baseIntent) { + hasIntentInfo = true; + } + } + break; + case "-p": { + String str = cmd.getNextArgRequired(); + intent.setPackage(str); + if (intent == baseIntent) { + hasIntentInfo = true; + } + } + break; + case "-f": + String str = cmd.getNextArgRequired(); + intent.setFlags(Integer.decode(str).intValue()); + break; + case "--grant-read-uri-permission": + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + break; + case "--grant-write-uri-permission": + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + break; + case "--grant-persistable-uri-permission": + intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + break; + case "--grant-prefix-uri-permission": + intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); + break; + case "--exclude-stopped-packages": + intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); + break; + case "--include-stopped-packages": + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + break; + case "--debug-log-resolution": + intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); + break; + case "--activity-brought-to-front": + intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); + break; + case "--activity-clear-top": + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + break; + case "--activity-clear-when-task-reset": + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + break; + case "--activity-exclude-from-recents": + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + break; + case "--activity-launched-from-history": + intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); + break; + case "--activity-multiple-task": + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + break; + case "--activity-no-animation": + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + break; + case "--activity-no-history": + intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + break; + case "--activity-no-user-action": + intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); + break; + case "--activity-previous-is-top": + intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + break; + case "--activity-reorder-to-front": + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + break; + case "--activity-reset-task-if-needed": + intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + break; + case "--activity-single-top": + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + break; + case "--activity-clear-task": + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + break; + case "--activity-task-on-home": + intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME); + break; + case "--receiver-registered-only": + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + break; + case "--receiver-replace-pending": + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + break; + case "--selector": + intent.setDataAndType(data, type); + intent = new Intent(); + break; + default: + if (optionHandler != null && optionHandler.handleOption(opt, cmd)) { + // Okay, caller handled this option. + } else { + throw new IllegalArgumentException("Unknown option: " + opt); + } + break; + } + } + intent.setDataAndType(data, type); + + final boolean hasSelector = intent != baseIntent; + if (hasSelector) { + // A selector was specified; fix up. + baseIntent.setSelector(intent); + intent = baseIntent; + } + + String arg = cmd.getNextArg(); + baseIntent = null; + if (arg == null) { + if (hasSelector) { + // If a selector has been specified, and no arguments + // have been supplied for the main Intent, then we can + // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't + // need to have a component name specified yet, the + // selector will take care of that. + baseIntent = new Intent(Intent.ACTION_MAIN); + baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); + } + } else if (arg.indexOf(':') >= 0) { + // The argument is a URI. Fully parse it, and use that result + // to fill in any data not specified so far. + baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME + | Intent.URI_ANDROID_APP_SCHEME | Intent.URI_ALLOW_UNSAFE); + } else if (arg.indexOf('/') >= 0) { + // The argument is a component name. Build an Intent to launch + // it. + baseIntent = new Intent(Intent.ACTION_MAIN); + baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); + baseIntent.setComponent(ComponentName.unflattenFromString(arg)); + } else { + // Assume the argument is a package name. + baseIntent = new Intent(Intent.ACTION_MAIN); + baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); + baseIntent.setPackage(arg); + } + if (baseIntent != null) { + Bundle extras = intent.getExtras(); + intent.replaceExtras((Bundle)null); + Bundle uriExtras = baseIntent.getExtras(); + baseIntent.replaceExtras((Bundle)null); + if (intent.getAction() != null && baseIntent.getCategories() != null) { + HashSet cats = new HashSet(baseIntent.getCategories()); + for (String c : cats) { + baseIntent.removeCategory(c); + } + } + intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR); + if (extras == null) { + extras = uriExtras; + } else if (uriExtras != null) { + uriExtras.putAll(extras); + extras = uriExtras; + } + intent.replaceExtras(extras); + hasIntentInfo = true; + } + + if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied"); + return intent; + } + + /** @hide */ + public static void printIntentArgsHelp(PrintWriter pw, String prefix) { + final String[] lines = new String[] { + " specifications include these flags and arguments:", + " [-a ] [-d ] [-t ]", + " [-c [-c ] ...]", + " [-e|--es ...]", + " [--esn ...]", + " [--ez ...]", + " [--ei ...]", + " [--el ...]", + " [--ef ...]", + " [--eu ...]", + " [--ecn ]", + " [--eia [, [,)", + " [--ela [, [,)", + " [--efa [, [,)", + " [--esa [, [,; to embed a comma into a string,", + " escape it using \"\\,\")", + " [--grant-read-uri-permission] [--grant-write-uri-permission]", + " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]", + " [--debug-log-resolution] [--exclude-stopped-packages]", + " [--include-stopped-packages]", + " [--activity-brought-to-front] [--activity-clear-top]", + " [--activity-clear-when-task-reset] [--activity-exclude-from-recents]", + " [--activity-launched-from-history] [--activity-multiple-task]", + " [--activity-no-animation] [--activity-no-history]", + " [--activity-no-user-action] [--activity-previous-is-top]", + " [--activity-reorder-to-front] [--activity-reset-task-if-needed]", + " [--activity-single-top] [--activity-clear-task]", + " [--activity-task-on-home]", + " [--receiver-registered-only] [--receiver-replace-pending]", + " [--selector]", + " [ | | ]" + }; + for (String line : lines) { + pw.print(prefix); + pw.println(line); + } + } + /** * Retrieve the general action to be performed, such as * {@link #ACTION_VIEW}. The action describes the general way the rest of diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index cad482b6bab98..6f12b625318ff 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -48,19 +48,35 @@ public abstract class ShellCommand { private FastPrintWriter mErrPrintWriter; private InputStream mInputStream; - public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args, ResultReceiver resultReceiver) { + public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, int firstArgPos) { mTarget = target; mIn = in; mOut = out; mErr = err; mArgs = args; - mResultReceiver = resultReceiver; - mCmd = args != null && args.length > 0 ? args[0] : null; - mArgPos = 1; + mResultReceiver = null; + mCmd = null; + mArgPos = firstArgPos; mCurArgData = null; mOutPrintWriter = null; mErrPrintWriter = null; + } + + public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ResultReceiver resultReceiver) { + String cmd; + int start; + if (args != null && args.length > 0) { + cmd = args[0]; + start = 1; + } else { + cmd = null; + start = 0; + } + init(target, in, out, err, args, start); + mCmd = cmd; + mResultReceiver = resultReceiver; if (DEBUG) Slog.d(TAG, "Starting command " + mCmd + " on " + mTarget); int res = -1; diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 95da438bc869d..f946ca7ed4159 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -256,6 +256,23 @@ public final class UserHandle implements Parcelable { } } + /** @hide */ + public static int parseUserArg(String arg) { + int userId; + if ("all".equals(arg)) { + userId = UserHandle.USER_ALL; + } else if ("current".equals(arg) || "cur".equals(arg)) { + userId = UserHandle.USER_CURRENT; + } else { + try { + userId = Integer.parseInt(arg); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Bad user number: " + arg); + } + } + return userId; + } + /** * Returns the user id of the current process * @return user id of the current process diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java index e26b27d5820c6..c067da7e1decd 100644 --- a/core/java/com/android/internal/os/BaseCommand.java +++ b/core/java/com/android/internal/os/BaseCommand.java @@ -17,13 +17,19 @@ package com.android.internal.os; +import android.os.ShellCommand; + import java.io.PrintStream; public abstract class BaseCommand { - protected String[] mArgs; - private int mNextArg; - private String mCurArgData; + final protected ShellCommand mArgs = new ShellCommand() { + @Override public int onCommand(String cmd) { + return 0; + } + @Override public void onHelp() { + } + }; // These are magic strings understood by the Eclipse plugin. public static final String FATAL_ERROR_CODE = "Error type 1"; @@ -39,9 +45,7 @@ public abstract class BaseCommand { return; } - mArgs = args; - mNextArg = 0; - mCurArgData = null; + mArgs.init(null, null, null, null, args, 0); try { onRun(); @@ -87,32 +91,7 @@ public abstract class BaseCommand { * starts with '-'. If the next argument is not an option, null is returned. */ public String nextOption() { - if (mCurArgData != null) { - String prev = mArgs[mNextArg - 1]; - throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); - } - if (mNextArg >= mArgs.length) { - return null; - } - String arg = mArgs[mNextArg]; - if (!arg.startsWith("-")) { - return null; - } - mNextArg++; - if (arg.equals("--")) { - return null; - } - if (arg.length() > 1 && arg.charAt(1) != '-') { - if (arg.length() > 2) { - mCurArgData = arg.substring(2); - return arg.substring(0, 2); - } else { - mCurArgData = null; - return arg; - } - } - mCurArgData = null; - return arg; + return mArgs.getNextOption(); } /** @@ -120,15 +99,7 @@ public abstract class BaseCommand { * no arguments left, return null. */ public String nextArg() { - if (mCurArgData != null) { - String arg = mCurArgData; - mCurArgData = null; - return arg; - } else if (mNextArg < mArgs.length) { - return mArgs[mNextArg++]; - } else { - return null; - } + return mArgs.getNextArg(); } /** @@ -136,11 +107,6 @@ public abstract class BaseCommand { * no arguments left, throws an IllegalArgumentException to report this to the user. */ public String nextArgRequired() { - String arg = nextArg(); - if (arg == null) { - String prev = mArgs[mNextArg - 1]; - throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); - } - return arg; + return mArgs.getNextArgRequired(); } } diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 3359060d3fe7c..43d10c74478c5 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -226,7 +226,7 @@ public abstract class IntentResolver { final int N = a.length; boolean printedHeader = false; F filter; - if (collapseDuplicates) { + if (collapseDuplicates && !printFilter) { found.clear(); for (int i=0; i= args.length) { + dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS + | DumpState.DUMP_SERVICE_RESOLVERS + | DumpState.DUMP_RECEIVER_RESOLVERS + | DumpState.DUMP_CONTENT_RESOLVERS); + } else { + while (opti < args.length) { + String name = args[opti]; + if ("a".equals(name) || "activity".equals(name)) { + dumpState.setDump(DumpState.DUMP_ACTIVITY_RESOLVERS); + } else if ("s".equals(name) || "service".equals(name)) { + dumpState.setDump(DumpState.DUMP_SERVICE_RESOLVERS); + } else if ("r".equals(name) || "receiver".equals(name)) { + dumpState.setDump(DumpState.DUMP_RECEIVER_RESOLVERS); + } else if ("c".equals(name) || "content".equals(name)) { + dumpState.setDump(DumpState.DUMP_CONTENT_RESOLVERS); + } else { + pw.println("Error: unknown resolver table type: " + name); + return; + } + opti++; + } + } } else if ("perm".equals(cmd) || "permissions".equals(cmd)) { dumpState.setDump(DumpState.DUMP_PERMISSIONS); } else if ("permission".equals(cmd)) { @@ -15399,22 +15424,28 @@ public class PackageManagerService extends IPackageManager.Stub { } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:" : "Activity Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:" : "Service Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { dumpState.setTitlePrinted(true); } + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:" : "Provider Resolver Table:", " ", packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index d7176fd0776bc..2cedc9ccd7c12 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -35,6 +35,7 @@ import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionParams; +import android.content.pm.ResolveInfo; import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; @@ -46,6 +47,7 @@ import android.os.ShellCommand; import android.os.UserHandle; import android.text.TextUtils; +import android.util.PrintWriterPrinter; import com.android.internal.util.SizedInputStream; import libcore.io.IoUtils; @@ -56,6 +58,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -68,6 +71,7 @@ class PackageManagerShellCommand extends ShellCommand { final IPackageManager mInterface; final private WeakHashMap mResourceCache = new WeakHashMap(); + int mTargetUser; PackageManagerShellCommand(PackageManagerService service) { mInterface = service; @@ -97,6 +101,12 @@ class PackageManagerShellCommand extends ShellCommand { return runList(); case "uninstall": return runUninstall(); + case "query-intent-activities": + return runQueryIntentActivities(); + case "query-intent-services": + return runQueryIntentServices(); + case "query-intent-receivers": + return runQueryIntentReceivers(); default: return handleDefaultCommands(cmd); } @@ -340,7 +350,7 @@ class PackageManagerShellCommand extends ShellCommand { listThirdParty = true; break; case "--user": - userId = Integer.parseInt(getNextArg()); + userId = UserHandle.parseUserArg(getNextArgRequired()); break; default: pw.println("Error: Unknown option: " + opt); @@ -487,7 +497,7 @@ class PackageManagerShellCommand extends ShellCommand { flags |= PackageManager.DELETE_KEEP_DATA; break; case "--user": - userId = Integer.parseInt(getNextArg()); + userId = UserHandle.parseUserArg(getNextArgRequired()); break; default: pw.println("Error: Unknown option: " + opt); @@ -538,6 +548,104 @@ class PackageManagerShellCommand extends ShellCommand { } } + private Intent parseIntentAndUser() throws URISyntaxException { + mTargetUser = UserHandle.USER_CURRENT; + Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() { + @Override + public boolean handleOption(String opt, ShellCommand cmd) { + if ("--user".equals(opt)) { + mTargetUser = UserHandle.parseUserArg(cmd.getNextArgRequired()); + return true; + } + return false; + } + }); + mTargetUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), mTargetUser, false, false, null, null); + return intent; + } + + private int runQueryIntentActivities() { + Intent intent; + try { + intent = parseIntentAndUser(); + } catch (URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + try { + List result = mInterface.queryIntentActivities(intent, null, 0, + mTargetUser); + PrintWriter pw = getOutPrintWriter(); + if (result == null || result.size() <= 0) { + pw.println("No activities found"); + } else { + pw.print(result.size()); pw.println(" activities found:"); + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i=0; i result = mInterface.queryIntentServices(intent, null, 0, + mTargetUser); + PrintWriter pw = getOutPrintWriter(); + if (result == null || result.size() <= 0) { + pw.println("No services found"); + } else { + pw.print(result.size()); pw.println(" services found:"); + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i=0; i result = mInterface.queryIntentReceivers(intent, null, 0, + mTargetUser); + PrintWriter pw = getOutPrintWriter(); + if (result == null || result.size() <= 0) { + pw.println("No receivers found"); + } else { + pw.print(result.size()); pw.println(" receivers found:"); + PrintWriterPrinter pr = new PrintWriterPrinter(pw); + for (int i=0; i