From f5b8671c340f189c50b41c53622f979b6d5e0a57 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 5 Dec 2011 17:42:41 -0800 Subject: [PATCH] Fix issue #5714517: App shortcuts can result in bad task intents New API to let you build an Intent whose base configuration is correct, but has an additional "selector" to pick out the specific app that you would like launched. Change-Id: Ide9db6dc60e2844b7696cfe09b28337fe7dd63db --- api/current.txt | 4 + cmds/am/src/com/android/commands/am/Am.java | 97 +++++--- core/java/android/content/Intent.java | 229 ++++++++++++++++-- .../providers/settings/DatabaseHelper.java | 3 +- .../policy/impl/PhoneWindowManager.java | 3 +- .../com/android/server/am/TaskRecord.java | 12 +- .../server/pm/PackageManagerService.java | 25 +- 7 files changed, 323 insertions(+), 50 deletions(-) diff --git a/api/current.txt b/api/current.txt index c62d82b2b4..38cf96632f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5334,6 +5334,7 @@ package android.content { method public java.util.ArrayList getParcelableArrayListExtra(java.lang.String); method public T getParcelableExtra(java.lang.String); method public java.lang.String getScheme(); + method public android.content.Intent getSelector(); method public java.io.Serializable getSerializableExtra(java.lang.String); method public short[] getShortArrayExtra(java.lang.String); method public short getShortExtra(java.lang.String, short); @@ -5346,6 +5347,7 @@ package android.content { method public boolean hasExtra(java.lang.String); method public boolean hasFileDescriptors(); method public static android.content.Intent makeMainActivity(android.content.ComponentName); + method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String); method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName); method public static android.content.Intent parseIntent(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.content.Intent parseUri(java.lang.String, int) throws java.net.URISyntaxException; @@ -5399,6 +5401,7 @@ package android.content { method public void setExtrasClassLoader(java.lang.ClassLoader); method public android.content.Intent setFlags(int); method public android.content.Intent setPackage(java.lang.String); + method public void setSelector(android.content.Intent); method public void setSourceBounds(android.graphics.Rect); method public android.content.Intent setType(java.lang.String); method public deprecated java.lang.String toURI(); @@ -5577,6 +5580,7 @@ package android.content { field public static final int FILL_IN_COMPONENT = 8; // 0x8 field public static final int FILL_IN_DATA = 2; // 0x2 field public static final int FILL_IN_PACKAGE = 16; // 0x10 + field public static final int FILL_IN_SELECTOR = 64; // 0x40 field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20 field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000 field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000 diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 140222ee71..fddb429d5a 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -131,6 +131,10 @@ private void run(String[] args) throws Exception { runScreenCompat(); } else if (op.equals("display-size")) { runDisplaySize(); + } else if (op.equals("to-uri")) { + runToUri(false); + } else if (op.equals("to-intent-uri")) { + runToUri(true); } else { throw new IllegalArgumentException("Unknown command: " + op); } @@ -138,6 +142,7 @@ private void run(String[] args) throws Exception { private Intent makeIntent() throws URISyntaxException { Intent intent = new Intent(); + Intent baseIntent = intent; boolean hasIntentInfo = false; mDebugOption = false; @@ -152,35 +157,39 @@ private Intent makeIntent() throws URISyntaxException { while ((opt=nextOption()) != null) { if (opt.equals("-a")) { intent.setAction(nextArgRequired()); - hasIntentInfo = true; + if (intent == baseIntent) { + hasIntentInfo = true; + } } else if (opt.equals("-d")) { data = Uri.parse(nextArgRequired()); - hasIntentInfo = true; + if (intent == baseIntent) { + hasIntentInfo = true; + } } else if (opt.equals("-t")) { type = nextArgRequired(); - hasIntentInfo = true; + if (intent == baseIntent) { + hasIntentInfo = true; + } } else if (opt.equals("-c")) { intent.addCategory(nextArgRequired()); - hasIntentInfo = true; + if (intent == baseIntent) { + hasIntentInfo = true; + } } else if (opt.equals("-e") || opt.equals("--es")) { String key = nextArgRequired(); String value = nextArgRequired(); intent.putExtra(key, value); - hasIntentInfo = true; } else if (opt.equals("--esn")) { String key = nextArgRequired(); intent.putExtra(key, (String) null); - hasIntentInfo = true; } else if (opt.equals("--ei")) { String key = nextArgRequired(); String value = nextArgRequired(); intent.putExtra(key, Integer.valueOf(value)); - hasIntentInfo = true; } else if (opt.equals("--eu")) { String key = nextArgRequired(); String value = nextArgRequired(); intent.putExtra(key, Uri.parse(value)); - hasIntentInfo = true; } else if (opt.equals("--eia")) { String key = nextArgRequired(); String value = nextArgRequired(); @@ -190,12 +199,10 @@ private Intent makeIntent() throws URISyntaxException { list[i] = Integer.valueOf(strings[i]); } intent.putExtra(key, list); - hasIntentInfo = true; } else if (opt.equals("--el")) { String key = nextArgRequired(); String value = nextArgRequired(); intent.putExtra(key, Long.valueOf(value)); - hasIntentInfo = true; } else if (opt.equals("--ela")) { String key = nextArgRequired(); String value = nextArgRequired(); @@ -205,18 +212,18 @@ private Intent makeIntent() throws URISyntaxException { list[i] = Long.valueOf(strings[i]); } intent.putExtra(key, list); - hasIntentInfo = true; } else if (opt.equals("--ez")) { String key = nextArgRequired(); String value = nextArgRequired(); intent.putExtra(key, Boolean.valueOf(value)); - hasIntentInfo = true; } 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); - hasIntentInfo = true; + if (intent == baseIntent) { + hasIntentInfo = true; + } } else if (opt.equals("-f")) { String str = nextArgRequired(); intent.setFlags(Integer.decode(str).intValue()); @@ -264,6 +271,9 @@ private Intent makeIntent() throws URISyntaxException { 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")) { mDebugOption = true; } else if (opt.equals("-W")) { @@ -286,25 +296,42 @@ private Intent makeIntent() throws URISyntaxException { } 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(); - if (arg != null) { - Intent baseIntent; - 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); - } 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 = 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); - baseIntent.setPackage(arg); } + } 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); + } 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(); @@ -315,7 +342,7 @@ private Intent makeIntent() throws URISyntaxException { baseIntent.removeCategory(c); } } - intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT); + intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR); if (extras == null) { extras = uriExtras; } else if (uriExtras != null) { @@ -1064,6 +1091,11 @@ private void runDisplaySize() throws Exception { } } + private void runToUri(boolean intentScheme) throws Exception { + Intent intent = makeIntent(); + System.out.println(intent.toUri(intentScheme ? Intent.URI_INTENT_SCHEME : 0)); + } + private class IntentReceiver extends IIntentReceiver.Stub { private boolean mFinished = false; @@ -1233,6 +1265,8 @@ private static void showUsage() { " am monitor [--gdb ]\n" + " am screen-compat [on|off] \n" + " am display-size [reset|MxN]\n" + + " am to-uri [INTENT]\n" + + " am to-intent-uri [INTENT]\n" + "\n" + "am start: start an Activity. Options are:\n" + " -D: enable debugging\n" + @@ -1284,6 +1318,10 @@ private static void showUsage() { "\n" + "am display-size: override display size.\n" + "\n" + + "am to-uri: print the given Intent specification as a URI.\n" + + "\n" + + "am to-intent-uri: print the given Intent specification as an intent: URI.\n" + + "\n" + " specifications include these flags and arguments:\n" + " [-a ] [-d ] [-t ]\n" + " [-c [-c ] ...]\n" + @@ -1308,6 +1346,7 @@ private static void showUsage() { " [--activity-single-top] [--activity-clear-task]\n" + " [--activity-task-on-home]\n" + " [--receiver-registered-only] [--receiver-replace-pending]\n" + + " [--selector]\n" + " [ | | ]\n" ); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9948985ce1..4e5598b3a0 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2315,6 +2315,11 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the browser application. * The activity should be able to browse the Internet. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER"; @@ -2322,6 +2327,11 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the calculator application. * The activity should be able to perform standard arithmetic operations. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR"; @@ -2329,6 +2339,11 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the calendar application. * The activity should be able to view and manipulate calendar entries. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; @@ -2336,6 +2351,11 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the contacts application. * The activity should be able to view and manipulate address book entries. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; @@ -2343,6 +2363,11 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the email application. * The activity should be able to send and receive email. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; @@ -2351,6 +2376,11 @@ public static Intent createChooser(Intent target, CharSequence title) { * Used with {@link #ACTION_MAIN} to launch the gallery application. * The activity should be able to view and manipulate image and video files * stored on the device. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; @@ -2358,6 +2388,11 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the maps application. * The activity should be able to show the user's current location and surroundings. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; @@ -2365,13 +2400,24 @@ public static Intent createChooser(Intent target, CharSequence title) { /** * Used with {@link #ACTION_MAIN} to launch the messaging application. * The activity should be able to send and receive text messages. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING"; /** * Used with {@link #ACTION_MAIN} to launch the music application. - * The activity should be able to play, browse, or manipulate music files stored on the device. + * The activity should be able to play, browse, or manipulate music files + * stored on the device. + *

NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.

*/ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; @@ -2963,6 +3009,7 @@ public static Intent createChooser(Intent target, CharSequence title) { private HashSet mCategories; private Bundle mExtras; private Rect mSourceBounds; + private Intent mSelector; // --------------------------------------------------------------------- @@ -2991,6 +3038,9 @@ public Intent(Intent o) { if (o.mSourceBounds != null) { this.mSourceBounds = new Rect(o.mSourceBounds); } + if (o.mSelector != null) { + this.mSelector = new Intent(o.mSelector); + } } @Override @@ -3130,6 +3180,39 @@ public static Intent makeMainActivity(ComponentName mainActivity) { return intent; } + /** + * Make an Intent for the main activity of an application, without + * specifying a specific activity to run but giving a selector to find + * the activity. This results in a final Intent that is structured + * the same as when the application is launched from + * Home. For anything else that wants to launch an application in the + * same way, it is important that they use an Intent structured the same + * way, and can use this function to ensure this is the case. + * + *

The returned Intent has {@link #ACTION_MAIN} as its action, and includes the + * category {@link #CATEGORY_LAUNCHER}. This does not have + * {@link #FLAG_ACTIVITY_NEW_TASK} set, though typically you will want + * to do that through {@link #addFlags(int)} on the returned Intent. + * + * @param selectorAction The action name of the Intent's selector. + * @param selectorCategory The name of a category to add to the Intent's + * selector. + * @return Returns a newly created Intent that can be used to launch the + * activity as a main application entry. + * + * @see #setSelector(Intent) + */ + public static Intent makeMainSelectorActivity(String selectorAction, + String selectorCategory) { + Intent intent = new Intent(ACTION_MAIN); + intent.addCategory(CATEGORY_LAUNCHER); + Intent selector = new Intent(); + selector.setAction(selectorAction); + selector.addCategory(selectorCategory); + intent.setSelector(selector); + return intent; + } + /** * Make an Intent that can be used to re-launch an application's task * in its base state. This is like {@link #makeMainActivity(ComponentName)}, @@ -3205,6 +3288,7 @@ public static Intent parseUri(String uri, int flags) throws URISyntaxException { // new format Intent intent = new Intent(ACTION_VIEW); + Intent baseIntent = intent; // fetch data part, if present String data = i >= 0 ? uri.substring(0, i) : null; @@ -3214,8 +3298,9 @@ public static Intent parseUri(String uri, int flags) throws URISyntaxException { // loop over contents of Intent, all name=value; while (!uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); - int semi = uri.indexOf(';', eq); - String value = Uri.decode(uri.substring(eq + 1, semi)); + if (eq < 0) eq = i-1; + int semi = uri.indexOf(';', i); + String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : ""; // action if (uri.startsWith("action=", i)) { @@ -3257,6 +3342,11 @@ else if (uri.startsWith("sourceBounds=", i)) { intent.mSourceBounds = Rect.unflattenFromString(value); } + // selector + else if (semi == (i+3) && uri.startsWith("SEL", i)) { + intent = new Intent(); + } + // extra else { String key = Uri.decode(uri.substring(i + 2, eq)); @@ -3280,6 +3370,12 @@ else if (uri.startsWith("sourceBounds=", i)) { i = semi + 1; } + if (intent != baseIntent) { + // The Intent had a selector; fix it up. + baseIntent.setSelector(intent); + intent = baseIntent; + } + if (data != null) { if (data.startsWith("intent:")) { data = data.substring(7); @@ -3605,7 +3701,7 @@ public boolean hasCategory(String category) { * Return the set of all categories in the intent. If there are no categories, * returns NULL. * - * @return Set The set of categories you can examine. Do not modify! + * @return The set of categories you can examine. Do not modify! * * @see #hasCategory * @see #addCategory @@ -3614,6 +3710,16 @@ public Set getCategories() { return mCategories; } + /** + * Return the specific selector associated with this Intent. If there is + * none, returns null. See {@link #setSelector} for more information. + * + * @see #setSelector + */ + public Intent getSelector() { + return mSelector; + } + /** * Sets the ClassLoader that will be used when unmarshalling * any Parcelable values from the extras of this Intent. @@ -4432,6 +4538,49 @@ public void removeCategory(String category) { } } + /** + * Set a selector for this Intent. This is a modification to the kinds of + * things the Intent will match. If the selector is set, it will be used + * when trying to find entities that can handle the Intent, instead of the + * main contents of the Intent. This allows you build an Intent containing + * a generic protocol while targeting it more specifically. + * + *

An example of where this may be used is with things like + * {@link #CATEGORY_APP_BROWSER}. This category allows you to build an + * Intent that will launch the Browser application. However, the correct + * main entry point of an application is actually {@link #ACTION_MAIN} + * {@link #CATEGORY_LAUNCHER} with {@link #setComponent(ComponentName)} + * used to specify the actual Activity to launch. If you launch the browser + * with something different, undesired behavior may happen if the user has + * previously or later launches it the normal way, since they do not match. + * Instead, you can build an Intent with the MAIN action (but no ComponentName + * yet specified) and set a selector with {@link #ACTION_MAIN} and + * {@link #CATEGORY_APP_BROWSER} to point it specifically to the browser activity. + * + *

Setting a selector does not impact the behavior of + * {@link #filterEquals(Intent)} and {@link #filterHashCode()}. This is part of the + * desired behavior of a selector -- it does not impact the base meaning + * of the Intent, just what kinds of things will be matched against it + * when determining who can handle it.

+ * + *

You can not use both a selector and {@link #setPackage(String)} on + * the same base Intent.

+ * + * @param selector The desired selector Intent; set to null to not use + * a special selector. + */ + public void setSelector(Intent selector) { + if (selector == this) { + throw new IllegalArgumentException( + "Intent being set as a selector of itself"); + } + if (selector != null && mPackage != null) { + throw new IllegalArgumentException( + "Can't set selector when package name is already set"); + } + mSelector = selector; + } + /** * Add extended data to the intent. The name must include a package * prefix, for example the app com.android.contacts would use names @@ -5259,6 +5408,10 @@ public Intent addFlags(int flags) { * @see #resolveActivity */ public Intent setPackage(String packageName) { + if (packageName != null && mSelector != null) { + throw new IllegalArgumentException( + "Can't set package name when selector is already set"); + } mPackage = packageName; return this; } @@ -5394,11 +5547,17 @@ public void setSourceBounds(Rect r) { public static final int FILL_IN_PACKAGE = 1<<4; /** - * Use with {@link #fillIn} to allow the current package value to be + * Use with {@link #fillIn} to allow the current bounds rectangle to be * overwritten, even if it is already set. */ public static final int FILL_IN_SOURCE_BOUNDS = 1<<5; + /** + * Use with {@link #fillIn} to allow the current selector to be + * overwritten, even if it is already set. + */ + public static final int FILL_IN_SELECTOR = 1<<6; + /** * Copy the contents of other in to this object, but only * where fields are not defined by this object. For purposes of a field @@ -5419,11 +5578,13 @@ public void setSourceBounds(Rect r) { * *

In addition, you can use the {@link #FILL_IN_ACTION}, * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, - * and {@link #FILL_IN_COMPONENT} to override the restriction where the + * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS}, and + * {@link #FILL_IN_SELECTOR} to override the restriction where the * corresponding field will not be replaced if it is already set. * *

Note: The component field will only be copied if {@link #FILL_IN_COMPONENT} is explicitly - * specified. + * specified. The selector will only be copied if {@link #FILL_IN_SELECTOR} is + * explicitly specified. * *

For example, consider Intent A with {data="foo", categories="bar"} * and Intent B with {action="gotit", data-type="some/thing", @@ -5439,7 +5600,8 @@ public void setSourceBounds(Rect r) { * * @return Returns a bit mask of {@link #FILL_IN_ACTION}, * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, - * and {@link #FILL_IN_COMPONENT} indicating which fields were changed. + * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS}, and + * {@link #FILL_IN_SELECTOR} indicating which fields were changed. */ public int fillIn(Intent other, int flags) { int changes = 0; @@ -5464,8 +5626,20 @@ public int fillIn(Intent other, int flags) { } if (other.mPackage != null && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) { - mPackage = other.mPackage; - changes |= FILL_IN_PACKAGE; + // Only do this if mSelector is not set. + if (mSelector == null) { + mPackage = other.mPackage; + changes |= FILL_IN_PACKAGE; + } + } + // Selector is special: it can only be set if explicitly allowed, + // for the same reason as the component name. + if (other.mSelector != null && (flags&FILL_IN_SELECTOR) != 0) { + if (mPackage == null) { + mSelector = new Intent(other.mSelector); + mPackage = null; + changes |= FILL_IN_SELECTOR; + } } // Component is special: it can -only- be set if explicitly allowed, // since otherwise the sender could force the intent somewhere the @@ -5763,6 +5937,11 @@ public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean first = false; b.append("(has extras)"); } + if (mSelector != null) { + b.append(" sel={"); + mSelector.toShortString(b, secure, comp, extras); + b.append("}"); + } } /** @@ -5823,6 +6002,21 @@ public String toUri(int flags) { uri.append("#Intent;"); + toUriInner(uri, scheme, flags); + if (mSelector != null) { + uri.append("SEL;"); + // Note that for now we are not going to try to handle the + // data part; not clear how to represent this as a URI, and + // not much utility in it. + mSelector.toUriInner(uri, null, flags); + } + + uri.append("end"); + + return uri.toString(); + } + + private void toUriInner(StringBuilder uri, String scheme, int flags) { if (scheme != null) { uri.append("scheme=").append(scheme).append(';'); } @@ -5877,10 +6071,6 @@ public String toUri(int flags) { } } } - - uri.append("end"); - - return uri.toString(); } public int describeContents() { @@ -5911,6 +6101,13 @@ public void writeToParcel(Parcel out, int flags) { out.writeInt(0); } + if (mSelector != null) { + out.writeInt(1); + mSelector.writeToParcel(out, flags); + } else { + out.writeInt(0); + } + out.writeBundle(mExtras); } @@ -5952,6 +6149,10 @@ public void readFromParcel(Parcel in) { mCategories = null; } + if (in.readInt() != 0) { + mSelector = new Intent(in); + } + mExtras = in.readBundle(); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 080d345e51..ac2369ab52 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1151,8 +1151,7 @@ private void loadBookmarks(SQLiteDatabase db) { intent.setComponent(cn); title = info.loadLabel(packageManager).toString(); } else if (category != null) { - intent = new Intent(Intent.ACTION_MAIN, null); - intent.addCategory(category); + intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category); title = ""; } else { Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutStr diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index f6bf213df4..46463ab20c 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -1638,8 +1638,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p if (down && repeatCount == 0) { String category = sApplicationLaunchKeyCategories.get(keyCode); if (category != null) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(category); + Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { mContext.startActivity(intent); diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index a86076344e..de3129bda3 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -54,8 +54,17 @@ long getInactiveDuration() { void setIntent(Intent _intent, ActivityInfo info) { stringName = null; - + if (info.targetActivity == null) { + if (_intent != null) { + // If this Intent has a selector, we want to clear it for the + // recent task since it is not relevant if the user later wants + // to re-launch the app. + if (_intent.getSelector() != null) { + _intent = new Intent(_intent); + _intent.setSelector(null); + } + } intent = _intent; realActivity = _intent != null ? _intent.getComponent() : null; origActivity = null; @@ -65,6 +74,7 @@ void setIntent(Intent _intent, ActivityInfo info) { if (_intent != null) { Intent targetIntent = new Intent(_intent); targetIntent.setComponent(targetComponent); + targetIntent.setSelector(null); intent = targetIntent; realActivity = targetComponent; origActivity = _intent.getComponent(); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 700554157e..6b61c47db6 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -2162,6 +2162,9 @@ ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags, List query, int priority) { // writer synchronized (mPackages) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + } if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); List prefs = mSettings.mPreferredActivities.queryIntent(intent, resolvedType, @@ -2242,7 +2245,13 @@ ResolveInfo findPreferredActivity(Intent intent, String resolvedType, public List queryIntentActivities(Intent intent, String resolvedType, int flags) { - final ComponentName comp = intent.getComponent(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } if (comp != null) { final List list = new ArrayList(1); final ActivityInfo ai = getActivityInfo(comp, flags); @@ -2440,6 +2449,12 @@ public List queryIntentActivityOptions(ComponentName caller, public List queryIntentReceivers(Intent intent, String resolvedType, int flags) { ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } if (comp != null) { List list = new ArrayList(1); ActivityInfo ai = getReceiverInfo(comp, flags); @@ -2478,7 +2493,13 @@ public ResolveInfo resolveService(Intent intent, String resolvedType, int flags) } public List queryIntentServices(Intent intent, String resolvedType, int flags) { - final ComponentName comp = intent.getComponent(); + ComponentName comp = intent.getComponent(); + if (comp == null) { + if (intent.getSelector() != null) { + intent = intent.getSelector(); + comp = intent.getComponent(); + } + } if (comp != null) { final List list = new ArrayList(1); final ServiceInfo si = getServiceInfo(comp, flags);