From f5b8671c340f189c50b41c53622f979b6d5e0a57 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 5 Dec 2011 17:42:41 -0800 Subject: 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 +++++++++++++++++++-- .../android/providers/settings/DatabaseHelper.java | 3 +- .../internal/policy/impl/PhoneWindowManager.java | 3 +- .../java/com/android/server/am/TaskRecord.java | 12 +- .../android/server/pm/PackageManagerService.java | 25 ++- 7 files changed, 323 insertions(+), 50 deletions(-) diff --git a/api/current.txt b/api/current.txt index c62d82b2b4c6..38cf96632f3f 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 140222ee7130..fddb429d5ae5 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 @@ public class Am { 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 @@ public class Am { private Intent makeIntent() throws URISyntaxException { Intent intent = new Intent(); + Intent baseIntent = intent; boolean hasIntentInfo = false; mDebugOption = false; @@ -152,35 +157,39 @@ public class Am { 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 @@ public class Am { 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 @@ public class Am { 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 @@ public class Am { 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 @@ public class Am { } 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 @@ public class Am { 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 @@ public class Am { } } + 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 @@ public class Am { " 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 @@ public class Am { "\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 @@ public class Am { " [--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 9948985ce14d..4e5598b3a0a1 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2315,6 +2315,11 @@ public class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { * 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 class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { /** * 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 class Intent implements Parcelable, Cloneable { private HashSet mCategories; private Bundle mExtras; private Rect mSourceBounds; + private Intent mSelector; // --------------------------------------------------------------------- @@ -2991,6 +3038,9 @@ public class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { // 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 class Intent implements Parcelable, Cloneable { // 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 @@ public class Intent implements Parcelable, Cloneable { 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 @@ public class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { * 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 class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { } } + /** + * 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 class Intent implements Parcelable, Cloneable { * @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 class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { * *

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 class Intent implements Parcelable, Cloneable { * * @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 class Intent implements Parcelable, Cloneable { } 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 class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { } } } - - uri.append("end"); - - return uri.toString(); } public int describeContents() { @@ -5911,6 +6101,13 @@ public class Intent implements Parcelable, Cloneable { 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 class Intent implements Parcelable, Cloneable { 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 080d345e51d9..ac2369ab52a9 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { 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 f6bf213df453..46463ab20c1c 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 class PhoneWindowManager implements WindowManagerPolicy { 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 a86076344e8a..de3129bda384 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -54,8 +54,17 @@ class TaskRecord extends ThumbnailHolder { 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 @@ class TaskRecord extends ThumbnailHolder { 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 700554157ea1..6b61c47db696 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -2162,6 +2162,9 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 class PackageManagerService extends IPackageManager.Stub { 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 class PackageManagerService extends IPackageManager.Stub { } 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); -- cgit v1.2.3-59-g8ed1b