diff options
| -rw-r--r-- | api/current.txt | 7 | ||||
| -rw-r--r-- | api/system-current.txt | 7 | ||||
| -rw-r--r-- | core/java/android/service/chooser/ChooserTarget.java | 128 | ||||
| -rw-r--r-- | core/java/android/service/chooser/ChooserTargetService.java | 5 | ||||
| -rw-r--r-- | core/java/android/service/chooser/IChooserTargetResult.aidl | 2 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ChooserActivity.java | 57 |
6 files changed, 86 insertions, 120 deletions
diff --git a/api/current.txt b/api/current.txt index ea33eaf40fb9..f4f91a72f6fe 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28534,14 +28534,13 @@ package android.service.carrier { package android.service.chooser { public final class ChooserTarget implements android.os.Parcelable { - ctor public ChooserTarget(java.lang.CharSequence, android.graphics.drawable.Icon, float, android.app.PendingIntent); - ctor public ChooserTarget(java.lang.CharSequence, android.graphics.drawable.Icon, float, android.content.IntentSender); + ctor public ChooserTarget(java.lang.CharSequence, android.graphics.drawable.Icon, float, android.content.ComponentName, android.os.Bundle); method public int describeContents(); + method public android.content.ComponentName getComponentName(); method public android.graphics.drawable.Icon getIcon(); - method public android.content.IntentSender getIntentSender(); + method public android.os.Bundle getIntentExtras(); method public float getScore(); method public java.lang.CharSequence getTitle(); - method public boolean sendIntent(android.content.Context, android.content.Intent); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.chooser.ChooserTarget> CREATOR; } diff --git a/api/system-current.txt b/api/system-current.txt index d1e19fbe6b65..4da118a5c9ca 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -30594,14 +30594,13 @@ package android.service.carrier { package android.service.chooser { public final class ChooserTarget implements android.os.Parcelable { - ctor public ChooserTarget(java.lang.CharSequence, android.graphics.drawable.Icon, float, android.app.PendingIntent); - ctor public ChooserTarget(java.lang.CharSequence, android.graphics.drawable.Icon, float, android.content.IntentSender); + ctor public ChooserTarget(java.lang.CharSequence, android.graphics.drawable.Icon, float, android.content.ComponentName, android.os.Bundle); method public int describeContents(); + method public android.content.ComponentName getComponentName(); method public android.graphics.drawable.Icon getIcon(); - method public android.content.IntentSender getIntentSender(); + method public android.os.Bundle getIntentExtras(); method public float getScore(); method public java.lang.CharSequence getTitle(); - method public boolean sendIntent(android.content.Context, android.content.Intent); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.chooser.ChooserTarget> CREATOR; } diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java index 50c435a90d1c..c2f70cc02f05 100644 --- a/core/java/android/service/chooser/ChooserTarget.java +++ b/core/java/android/service/chooser/ChooserTarget.java @@ -17,20 +17,14 @@ package android.service.chooser; -import android.app.Activity; -import android.app.PendingIntent; +import android.annotation.Nullable; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.IntentSender; -import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.os.UserHandle; -import android.util.Log; /** * A ChooserTarget represents a deep-link into an application as returned by a @@ -62,52 +56,21 @@ public final class ChooserTarget implements Parcelable { private Icon mIcon; /** - * The IntentSender that will be used to deliver the intent to the target. - * It will be {@link android.content.Intent#fillIn(android.content.Intent, int)} filled in} - * by the real intent sent by the application. + * The ComponentName of the Activity to be invoked. Must be part of the target creator's + * own package or an Activity exported by its package. */ - private IntentSender mIntentSender; + private ComponentName mComponentName; /** - * The score given to this item. It can be normalized. + * A Bundle to merge with the extras of the intent sent to this target. + * Any extras here will override the extras from the original intent. */ - private float mScore; + private Bundle mIntentExtras; /** - * Construct a deep link target for presentation by a chooser UI. - * - * <p>A target is composed of a title and an icon for presentation to the user. - * The UI presenting this target may truncate the title if it is too long to be presented - * in the available space, as well as crop, resize or overlay the supplied icon.</p> - * - * <p>The creator of a target may supply a ranking score. This score is assumed to be relative - * to the other targets supplied by the same - * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. - * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). - * Scores for a set of targets do not need to sum to 1.</p> - * - * <p>Before being sent, the PendingIntent supplied will be - * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied - * to the chooser. When constructing a PendingIntent for use in a ChooserTarget, make sure - * that you permit the relevant fields to be filled in using the appropriate flags such as - * {@link Intent#FILL_IN_ACTION}, {@link Intent#FILL_IN_CATEGORIES}, - * {@link Intent#FILL_IN_DATA} and {@link Intent#FILL_IN_CLIP_DATA}. Note that - * {@link Intent#FILL_IN_CLIP_DATA} is required to appropriately receive URI permission grants - * for {@link Intent#ACTION_SEND} intents.</p> - * - * <p>Take care not to place custom {@link android.os.Parcelable} types into - * the PendingIntent as extras, as the system will not be able to unparcel it to merge - * additional extras.</p> - * - * @param title title of this target that will be shown to a user - * @param icon icon to represent this target - * @param score ranking score for this target between 0.0f and 1.0f, inclusive - * @param pendingIntent PendingIntent to fill in and send if the user chooses this target + * The score given to this item. It can be normalized. */ - public ChooserTarget(CharSequence title, Icon icon, float score, - PendingIntent pendingIntent) { - this(title, icon, score, pendingIntent.getIntentSender()); - } + private float mScore; /** * Construct a deep link target for presentation by a chooser UI. @@ -122,25 +85,23 @@ public final class ChooserTarget implements Parcelable { * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). * Scores for a set of targets do not need to sum to 1.</p> * - * <p>Before being sent, the IntentSender supplied will be - * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied - * to the chooser. When constructing an IntentSender for use in a ChooserTarget, make sure - * that you permit the relevant fields to be filled in using the appropriate flags such as - * {@link Intent#FILL_IN_ACTION}, {@link Intent#FILL_IN_CATEGORIES}, - * {@link Intent#FILL_IN_DATA} and {@link Intent#FILL_IN_CLIP_DATA}. Note that - * {@link Intent#FILL_IN_CLIP_DATA} is required to appropriately receive URI permission grants - * for {@link Intent#ACTION_SEND} intents.</p> + * <p>The ComponentName must be the name of an Activity component in the creator's own + * package, or an exported component from any other package. You may provide an optional + * Bundle of extras that will be merged into the final intent before it is sent to the + * target Activity; use this to add any additional data about the deep link that the target + * activity will read. e.g. conversation IDs, email addresses, etc.</p> * * <p>Take care not to place custom {@link android.os.Parcelable} types into - * the IntentSender as extras, as the system will not be able to unparcel it to merge - * additional extras.</p> + * the extras bundle, as the system will not be able to unparcel them to merge them.</p> * * @param title title of this target that will be shown to a user * @param icon icon to represent this target * @param score ranking score for this target between 0.0f and 1.0f, inclusive - * @param intentSender IntentSender to fill in and send if the user chooses this target + * @param componentName Name of the component to be launched if this target is chosen + * @param intentExtras Bundle of extras to merge with the extras of the launched intent */ - public ChooserTarget(CharSequence title, Icon icon, float score, IntentSender intentSender) { + public ChooserTarget(CharSequence title, Icon icon, float score, + ComponentName componentName, @Nullable Bundle intentExtras) { mTitle = title; mIcon = icon; if (score > 1.f || score < 0.f) { @@ -148,7 +109,8 @@ public final class ChooserTarget implements Parcelable { + "must be between 0.0f and 1.0f"); } mScore = score; - mIntentSender = intentSender; + mComponentName = componentName; + mIntentExtras = intentExtras; } ChooserTarget(Parcel in) { @@ -159,7 +121,8 @@ public final class ChooserTarget implements Parcelable { mIcon = null; } mScore = in.readFloat(); - mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in); + mComponentName = ComponentName.readFromParcel(in); + mIntentExtras = in.readBundle(); } /** @@ -194,49 +157,29 @@ public final class ChooserTarget implements Parcelable { } /** - * Returns the raw IntentSender supplied by the ChooserTarget's creator. - * This may be null if the creator specified a regular Intent instead. - * - * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> + * Returns the ComponentName of the Activity that should be launched for this ChooserTarget. * - * @return the IntentSender supplied by the ChooserTarget's creator + * @return the name of the target Activity to launch */ - public IntentSender getIntentSender() { - return mIntentSender; + public ComponentName getComponentName() { + return mComponentName; } /** - * Fill in the IntentSender supplied by the ChooserTarget's creator and send it. + * Returns the Bundle of extras to be added to an intent launched to this target. * - * @param context the sending Context; generally the Activity presenting the chooser UI - * @param fillInIntent the Intent provided to the Chooser to be sent to a selected target - * @return true if sending the Intent was successful + * @return the extras to merge with the extras of the intent being launched */ - public boolean sendIntent(Context context, Intent fillInIntent) { - if (fillInIntent != null) { - fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); - } - if (mIntentSender != null) { - try { - mIntentSender.sendIntent(context, 0, fillInIntent, null, null); - return true; - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender to send"); - return false; - } + public Bundle getIntentExtras() { + return mIntentExtras; } @Override public String toString() { return "ChooserTarget{" - + (mIntentSender != null ? mIntentSender.getCreatorPackage() : null) - + ", " - + "'" + mTitle + + mComponentName + + ", " + mIntentExtras + + ", '" + mTitle + "', " + mScore + "}"; } @@ -255,7 +198,8 @@ public final class ChooserTarget implements Parcelable { dest.writeInt(0); } dest.writeFloat(mScore); - IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest); + ComponentName.writeToParcel(mComponentName, dest); + dest.writeBundle(mIntentExtras); } public static final Creator<ChooserTarget> CREATOR diff --git a/core/java/android/service/chooser/ChooserTargetService.java b/core/java/android/service/chooser/ChooserTargetService.java index a3bfeced361c..e0541855bbab 100644 --- a/core/java/android/service/chooser/ChooserTargetService.java +++ b/core/java/android/service/chooser/ChooserTargetService.java @@ -105,9 +105,8 @@ public abstract class ChooserTargetService extends Service { * can handle an intent. * * <p>The returned list should be sorted such that the most relevant targets appear first. - * Any PendingIntents used to construct the resulting ChooserTargets should always be prepared - * to have the relevant data fields filled in by the sender. See - * {@link ChooserTarget#ChooserTarget(CharSequence, android.graphics.drawable.Icon, float, android.app.PendingIntent) ChooserTarget}.</p> + * The score for each ChooserTarget will be combined with the system's score for the original + * target Activity to sort and filter targets presented to the user.</p> * * <p><em>Important:</em> Calls to this method from other applications will occur on * a binder thread, not on your app's main thread. Make sure that access to relevant data diff --git a/core/java/android/service/chooser/IChooserTargetResult.aidl b/core/java/android/service/chooser/IChooserTargetResult.aidl index dbd7cbd23af2..6c648a24ddfc 100644 --- a/core/java/android/service/chooser/IChooserTargetResult.aidl +++ b/core/java/android/service/chooser/IChooserTargetResult.aidl @@ -21,7 +21,7 @@ import android.service.chooser.ChooserTarget; /** * @hide */ -interface IChooserTargetResult +oneway interface IChooserTargetResult { void sendResult(in List<ChooserTarget> targets); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c4f57c7c38bf..6af2e8bf5765 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -471,6 +471,36 @@ public class ChooserActivity extends ResolverActivity { return false; } + void filterServiceTargets(String packageName, List<ChooserTarget> targets) { + if (targets == null) { + return; + } + + final PackageManager pm = getPackageManager(); + for (int i = targets.size() - 1; i >= 0; i--) { + final ChooserTarget target = targets.get(i); + final ComponentName targetName = target.getComponentName(); + if (packageName != null && packageName.equals(targetName.getPackageName())) { + // Anything from the original target's package is fine. + continue; + } + + boolean remove; + try { + final ActivityInfo ai = pm.getActivityInfo(targetName, 0); + remove = !ai.exported || ai.permission != null; + } catch (NameNotFoundException e) { + Log.e(TAG, "Target " + target + " returned by " + packageName + + " component not found"); + remove = true; + } + + if (remove) { + targets.remove(i); + } + } + } + @Override ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, @@ -554,11 +584,11 @@ public class ChooserActivity extends ResolverActivity { return null; } - private Intent getFillInIntent() { + private Intent getBaseIntentToSend() { Intent result = mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent(); if (result == null) { - Log.e(TAG, "ChooserTargetInfo#getFillInIntent: no fillIn intent available"); + Log.e(TAG, "ChooserTargetInfo: no base intent available to send"); } else { result = new Intent(result); if (mFillInIntent != null) { @@ -571,31 +601,24 @@ public class ChooserActivity extends ResolverActivity { @Override public boolean start(Activity activity, Bundle options) { - final Intent intent = getFillInIntent(); - if (intent == null) { - return false; - } - return mChooserTarget.sendIntent(activity, intent); + throw new RuntimeException("ChooserTargets should be started as caller."); } @Override public boolean startAsCaller(Activity activity, Bundle options, int userId) { - final Intent intent = getFillInIntent(); + final Intent intent = getBaseIntentToSend(); if (intent == null) { return false; } - // ChooserTargets will launch with their IntentSender's identity - return mChooserTarget.sendIntent(activity, intent); + intent.setComponent(mChooserTarget.getComponentName()); + intent.putExtras(mChooserTarget.getIntentExtras()); + activity.startActivityAsCaller(intent, options, true, userId); + return true; } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - final Intent intent = getFillInIntent(); - if (intent == null) { - return false; - } - // ChooserTargets will launch with their IntentSender's identity - return mChooserTarget.sendIntent(activity, intent); + throw new RuntimeException("ChooserTargets should be started as caller."); } @Override @@ -998,6 +1021,8 @@ public class ChooserActivity extends ResolverActivity { private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() { @Override public void sendResult(List<ChooserTarget> targets) throws RemoteException { + filterServiceTargets(mOriginalTarget.getResolveInfo().activityInfo.packageName, + targets); final Message msg = Message.obtain(); msg.what = CHOOSER_TARGET_SERVICE_RESULT; msg.obj = new ServiceResultInfo(mOriginalTarget, targets, |