diff options
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/app/Activity.java | 37 | ||||
-rw-r--r-- | core/java/android/app/ActivityThread.java | 25 | ||||
-rw-r--r-- | core/java/android/app/ApplicationThreadNative.java | 22 | ||||
-rw-r--r-- | core/java/android/app/IApplicationThread.java | 9 | ||||
-rw-r--r-- | core/java/android/app/Instrumentation.java | 15 | ||||
-rw-r--r-- | core/java/android/app/LocalActivityManager.java | 5 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 266 | ||||
-rw-r--r-- | core/java/android/content/pm/LabeledIntent.aidl | 19 | ||||
-rw-r--r-- | core/java/com/android/internal/content/ReferrerIntent.aidl | 19 | ||||
-rw-r--r-- | core/java/com/android/internal/content/ReferrerIntent.java | 51 |
10 files changed, 410 insertions, 58 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4b705dd..148527f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -693,6 +693,7 @@ public class Activity extends ContextThemeWrapper /*package*/ String mEmbeddedID; private Application mApplication; /*package*/ Intent mIntent; + /*package*/ String mReferrer; private ComponentName mComponent; /*package*/ ActivityInfo mActivityInfo; /*package*/ ActivityThread mMainThread; @@ -4448,6 +4449,39 @@ public class Activity extends ContextThemeWrapper } /** + * Return information about who launched this activity. If the launching Intent + * contains an {@link android.content.Intent#EXTRA_REFERRER Intent.EXTRA_REFERRER}, + * that will be returned as-is; otherwise, if known, an + * {@link Intent#URI_ANDROID_APP_SCHEME android-app:} referrer URI containing the + * package name that started the Intent will be returned. This may return null if no + * referrer can be identified -- it is neither explicitly specified, nor is it known which + * application package was involved. + * + * <p>If called while inside the handling of {@link #onNewIntent}, this function will + * return the referrer that submitted that new intent to the activity. Otherwise, it + * always returns the referrer of the original Intent.</p> + * + * <p>Note that this is <em>not</em> a security feature -- you can not trust the + * referrer information, applications can spoof it.</p> + */ + @Nullable + public Uri getReferrer() { + Intent intent = getIntent(); + Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER); + if (referrer != null) { + return referrer; + } + String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME); + if (referrerName != null) { + return Uri.parse(referrerName); + } + if (mReferrer != null) { + return new Uri.Builder().scheme("android-app").authority(mReferrer).build(); + } + return null; + } + + /** * Return the name of the package that invoked this activity. This is who * the data in {@link #setResult setResult()} will be sent to. You can * use this information to validate that the recipient is allowed to @@ -5868,7 +5902,7 @@ public class Activity extends ContextThemeWrapper Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, - Configuration config, IVoiceInteractor voiceInteractor) { + Configuration config, String referrer, IVoiceInteractor voiceInteractor) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); @@ -5891,6 +5925,7 @@ public class Activity extends ContextThemeWrapper mIdent = ident; mApplication = application; mIntent = intent; + mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cf6c049..5f21d75 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -94,6 +94,7 @@ import android.renderscript.RenderScript; import android.security.AndroidKeyStoreProvider; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; import com.android.internal.os.SamplingProfilerIntegration; @@ -268,6 +269,7 @@ public final class ActivityThread { IBinder token; int ident; Intent intent; + String referrer; IVoiceInteractor voiceInteractor; Bundle state; PersistableBundle persistentState; @@ -290,7 +292,7 @@ public final class ActivityThread { LoadedApk packageInfo; List<ResultInfo> pendingResults; - List<Intent> pendingIntents; + List<ReferrerIntent> pendingIntents; boolean startsNotResumed; boolean isForward; @@ -348,7 +350,7 @@ public final class ActivityThread { } static final class NewIntentData { - List<Intent> intents; + List<ReferrerIntent> intents; IBinder token; public String toString() { return "NewIntentData{intents=" + intents + " token=" + token + "}"; @@ -605,9 +607,9 @@ public final class ActivityThread { // activity itself back to the activity manager. (matters more with ipc) public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, int procState, Bundle state, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); @@ -617,6 +619,7 @@ public final class ActivityThread { r.token = token; r.ident = ident; r.intent = intent; + r.referrer = referrer; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; @@ -637,13 +640,13 @@ public final class ActivityThread { } public final void scheduleRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) { requestRelaunchActivity(token, pendingResults, pendingNewIntents, configChanges, notResumed, config, true); } - public final void scheduleNewIntent(List<Intent> intents, IBinder token) { + public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) { NewIntentData data = new NewIntentData(); data.intents = intents; data.token = token; @@ -2234,7 +2237,7 @@ public final class ActivityThread { activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, - r.voiceInteractor); + r.referrer, r.voiceInteractor); if (customIntent != null) { activity.mIntent = customIntent; @@ -2421,8 +2424,7 @@ public final class ActivityThread { } } - private void deliverNewIntents(ActivityClientRecord r, - List<Intent> intents) { + private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) { final int N = intents.size(); for (int i=0; i<N; i++) { Intent intent = intents.get(i); @@ -2433,8 +2435,7 @@ public final class ActivityThread { } } - public final void performNewIntents(IBinder token, - List<Intent> intents) { + public final void performNewIntents(IBinder token, List<ReferrerIntent> intents) { ActivityClientRecord r = mActivities.get(token); if (r != null) { final boolean resumed = !r.paused; @@ -3752,7 +3753,7 @@ public final class ActivityThread { } public final void requestRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config, boolean fromServer) { ActivityClientRecord target = null; diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 0123e16..d1b77b9 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import java.io.FileDescriptor; import java.io.IOException; @@ -140,19 +141,21 @@ public abstract class ApplicationThreadNative extends Binder ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data); Configuration curConfig = Configuration.CREATOR.createFromParcel(data); CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data); + String referrer = data.readString(); IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface( data.readStrongBinder()); int procState = data.readInt(); Bundle state = data.readBundle(); PersistableBundle persistentState = data.readPersistableBundle(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); - List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); + List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); boolean notResumed = data.readInt() != 0; boolean isForward = data.readInt() != 0; ProfilerInfo profilerInfo = data.readInt() != 0 ? ProfilerInfo.CREATOR.createFromParcel(data) : null; - scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, voiceInteractor, - procState, state, persistentState, ri, pi, notResumed, isForward, profilerInfo); + scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, referrer, + voiceInteractor, procState, state, persistentState, ri, pi, + notResumed, isForward, profilerInfo); return true; } @@ -161,7 +164,7 @@ public abstract class ApplicationThreadNative extends Binder data.enforceInterface(IApplicationThread.descriptor); IBinder b = data.readStrongBinder(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); - List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); + List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); int configChanges = data.readInt(); boolean notResumed = data.readInt() != 0; Configuration config = null; @@ -175,7 +178,7 @@ public abstract class ApplicationThreadNative extends Binder case SCHEDULE_NEW_INTENT_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); - List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); + List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); IBinder b = data.readStrongBinder(); scheduleNewIntent(pi, b); return true; @@ -764,9 +767,9 @@ class ApplicationThreadProxy implements IApplicationThread { public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, int procState, Bundle state, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -776,6 +779,7 @@ class ApplicationThreadProxy implements IApplicationThread { info.writeToParcel(data, 0); curConfig.writeToParcel(data, 0); compatInfo.writeToParcel(data, 0); + data.writeString(referrer); data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null); data.writeInt(procState); data.writeBundle(state); @@ -796,7 +800,7 @@ class ApplicationThreadProxy implements IApplicationThread { } public final void scheduleRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) throws RemoteException { Parcel data = Parcel.obtain(); @@ -817,7 +821,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public void scheduleNewIntent(List<Intent> intents, IBinder token) + public void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index f53075c..42acbc6 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -35,6 +35,7 @@ import android.os.IBinder; import android.os.IInterface; import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import java.io.FileDescriptor; import java.util.List; @@ -59,14 +60,14 @@ public interface IApplicationThread extends IInterface { void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException; void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, int procState, Bundle state, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException; void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, int configChanges, + List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) throws RemoteException; - void scheduleNewIntent(List<Intent> intent, IBinder token) throws RemoteException; + void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException; void scheduleDestroyActivity(IBinder token, boolean finished, int configChanges) throws RemoteException; void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo, diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 60a013e..d96153a 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -45,6 +45,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.Window; +import com.android.internal.content.ReferrerIntent; import java.io.File; import java.util.ArrayList; @@ -1042,7 +1043,7 @@ public class Instrumentation { activity.attach(context, aThread, this, token, 0, application, intent, info, title, parent, id, (Activity.NonConfigurationInstances)lastNonConfigurationInstance, - new Configuration(), null); + new Configuration(), null, null); return activity; } @@ -1207,7 +1208,17 @@ public class Instrumentation { * @param intent The new intent being received. */ public void callActivityOnNewIntent(Activity activity, Intent intent) { - activity.onNewIntent(intent); + final String oldReferrer = activity.mReferrer; + try { + try { + activity.mReferrer = ((ReferrerIntent)intent).mReferrer; + } catch (ClassCastException e) { + activity.mReferrer = null; + } + activity.onNewIntent(intent); + } finally { + activity.mReferrer = oldReferrer; + } } /** diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java index b654a6a..873e337 100644 --- a/core/java/android/app/LocalActivityManager.java +++ b/core/java/android/app/LocalActivityManager.java @@ -22,6 +22,7 @@ import android.os.Binder; import android.os.Bundle; import android.util.Log; import android.view.Window; +import com.android.internal.content.ReferrerIntent; import java.util.ArrayList; import java.util.HashMap; @@ -310,8 +311,8 @@ public class LocalActivityManager { if (aInfo.launchMode != ActivityInfo.LAUNCH_MULTIPLE || (intent.getFlags()&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) { // The activity wants onNewIntent() called. - ArrayList<Intent> intents = new ArrayList<Intent>(1); - intents.add(intent); + ArrayList<ReferrerIntent> intents = new ArrayList<>(1); + intents.add(new ReferrerIntent(intent, mParent.getPackageName())); if (localLOGV) Log.v(TAG, r.id + ": new intent"); mActivityThread.performNewIntents(r, intents); r.intent = intent; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e06f034..57f6028 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1401,14 +1401,36 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.ORIGINATING_URI"; /** - * Used as a URI extra field with {@link #ACTION_INSTALL_PACKAGE} and - * {@link #ACTION_VIEW} to indicate the HTTP referrer URI associated with the Intent - * data field or {@link #EXTRA_ORIGINATING_URI}. + * This extra can be used with any Intent used to launch an activity, supplying information + * about who is launching that activity. This field contains a {@link android.net.Uri} + * object, typically an http: or https: URI of the web site that the referral came from; + * it can also use the {@link #URI_ANDROID_APP_SCHEME android-app:} scheme to identify + * a native application that it came from. + * + * <p>To retrieve this value in a client, use {@link android.app.Activity#getReferrer} + * instead of directly retrieving the extra. It is also valid for applications to + * instead supply {@link #EXTRA_REFERRER_NAME} for cases where they can only create + * a string, not a Uri; the field here, if supplied, will always take precedence, + * however.</p> + * + * @see #EXTRA_REFERRER_NAME */ public static final String EXTRA_REFERRER = "android.intent.extra.REFERRER"; /** + * Alternate version of {@link #EXTRA_REFERRER} that supplies the URI as a String rather + * than a {@link android.net.Uri} object. Only for use in cases where Uri objects can + * not be created, in particular when Intent extras are supplied through the + * {@link #URI_INTENT_SCHEME intent:} or {@link #URI_ANDROID_APP_SCHEME android-app:} + * schemes. + * + * @see #EXTRA_REFERRER + */ + public static final String EXTRA_REFERRER_NAME + = "android.intent.extra.REFERRER_NAME"; + + /** * Used as an int extra field with {@link #ACTION_INSTALL_PACKAGE} and * {@link} #ACTION_VIEW} to indicate the uid of the package that initiated the install * @hide @@ -3919,6 +3941,75 @@ public class Intent implements Parcelable, Cloneable { */ public static final int URI_INTENT_SCHEME = 1<<0; + /** + * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string + * always has the "android-app:" scheme. This is a variation of + * {@link #URI_INTENT_SCHEME} whose format is simpler for the case of an + * http/https URI being delivered to a specific package name. The format + * is: + * + * <pre class="prettyprint"> + * android-app://{package_id}/{scheme}/{host}/{path}{#Intent;...}</pre> + * + * <p>In this scheme, only the <code>pacakge_id</code> is required, and all + * other components can be included as desired. Note that this can not be + * used with intents that have a {@link #setSelector}, since the base intent + * will always have an explicit package name.</p> + * + * <p>Some examples of how this scheme maps to Intent objects:</p> + * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> + * <colgroup align="left" /> + * <colgroup align="left" /> + * <thead> + * <tr><th>URI</th> <th>Intent</th></tr> + * </thead> + * + * <tbody> + * <tr><td><code>android-app://com.example.app</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td>{@link #ACTION_MAIN}</td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/http/example.com</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td>{@link #ACTION_VIEW}</td></tr> + * <tr><td>Data: </td><td><code>http://example.com/</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/http/example.com/foo?1234</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td>{@link #ACTION_VIEW}</td></tr> + * <tr><td>Data: </td><td><code>http://example.com/foo?1234</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;end</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/http/example.com/foo?1234<br />#Intent;action=com.example.MY_ACTION;end</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr> + * <tr><td>Data: </td><td><code>http://example.com/foo?1234</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;<br />i.some_int=100;S.some_str=hello;end</code></td> + * <td><table border="" style="margin:0" > + * <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * <tr><td>Extras: </td><td><code>some_int=(int)100<br />some_str=(String)hello</code></td></tr> + * </table></td> + * </tr> + * </tbody> + * </table> + */ + public static final int URI_ANDROID_APP_SCHEME = 1<<1; + // --------------------------------------------------------------------- private String mAction; @@ -4179,8 +4270,8 @@ public class Intent implements Parcelable, Cloneable { * the scheme and full path. * * @param uri The URI to turn into an Intent. - * @param flags Additional processing flags. Either 0 or - * {@link #URI_INTENT_SCHEME}. + * @param flags Additional processing flags. Either 0, + * {@link #URI_INTENT_SCHEME}, or {@link #URI_ANDROID_APP_SCHEME}. * * @return Intent The newly created Intent object. * @@ -4193,9 +4284,11 @@ public class Intent implements Parcelable, Cloneable { public static Intent parseUri(String uri, int flags) throws URISyntaxException { int i = 0; try { - // Validate intent scheme for if requested. - if ((flags&URI_INTENT_SCHEME) != 0) { - if (!uri.startsWith("intent:")) { + final boolean androidApp = uri.startsWith("android-app:"); + + // Validate intent scheme if requested. + if ((flags&(URI_INTENT_SCHEME|URI_ANDROID_APP_SCHEME)) != 0) { + if (!uri.startsWith("intent:") && !androidApp) { Intent intent = new Intent(ACTION_VIEW); try { intent.setData(Uri.parse(uri)); @@ -4206,24 +4299,40 @@ public class Intent implements Parcelable, Cloneable { } } - // simple case i = uri.lastIndexOf("#"); - if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri)); + // simple case + if (i == -1) { + if (!androidApp) { + return new Intent(ACTION_VIEW, Uri.parse(uri)); + } // old format Intent URI - if (!uri.startsWith("#Intent;", i)) return getIntentOld(uri); + } else if (!uri.startsWith("#Intent;", i)) { + if (!androidApp) { + return getIntentOld(uri); + } else { + i = -1; + } + } // new format Intent intent = new Intent(ACTION_VIEW); Intent baseIntent = intent; + boolean explicitAction = false; + boolean inSelector = false; // fetch data part, if present - String data = i >= 0 ? uri.substring(0, i) : null; String scheme = null; - i += "#Intent;".length(); + String data; + if (i >= 0) { + data = uri.substring(0, i); + i += 8; // length of "#Intent;" + } else { + data = uri; + } // loop over contents of Intent, all name=value; - while (!uri.startsWith("end", i)) { + while (i >= 0 && !uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); if (eq < 0) eq = i-1; int semi = uri.indexOf(';', i); @@ -4232,6 +4341,9 @@ public class Intent implements Parcelable, Cloneable { // action if (uri.startsWith("action=", i)) { intent.setAction(value); + if (!inSelector) { + explicitAction = true; + } } // categories @@ -4261,7 +4373,11 @@ public class Intent implements Parcelable, Cloneable { // scheme else if (uri.startsWith("scheme=", i)) { - scheme = value; + if (inSelector) { + intent.mData = Uri.parse(value); + } else { + scheme = value; + } } // source bounds @@ -4272,6 +4388,7 @@ public class Intent implements Parcelable, Cloneable { // selector else if (semi == (i+3) && uri.startsWith("SEL", i)) { intent = new Intent(); + inSelector = true; } // extra @@ -4297,9 +4414,11 @@ public class Intent implements Parcelable, Cloneable { i = semi + 1; } - if (intent != baseIntent) { + if (inSelector) { // The Intent had a selector; fix it up. - baseIntent.setSelector(intent); + if (baseIntent.mPackage == null) { + baseIntent.setSelector(intent); + } intent = baseIntent; } @@ -4309,6 +4428,47 @@ public class Intent implements Parcelable, Cloneable { if (scheme != null) { data = scheme + ':' + data; } + } else if (data.startsWith("android-app:")) { + if (data.charAt(12) == '/' && data.charAt(13) == '/') { + // Correctly formed android-app, first part is package name. + int end = data.indexOf('/', 14); + if (end < 0) { + // All we have is a package name. + intent.mPackage = data.substring(14); + if (!explicitAction) { + intent.setAction(ACTION_MAIN); + } + data = ""; + } else { + // Target the Intent at the given package name always. + String authority = null; + intent.mPackage = data.substring(14, end); + int newEnd; + if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) { + // Found a scheme, remember it. + scheme = data.substring(end+1, newEnd); + end = newEnd; + if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) { + // Found a authority, remember it. + authority = data.substring(end+1, newEnd); + end = newEnd; + } + } + if (scheme == null) { + // If there was no scheme, then this just targets the package. + if (!explicitAction) { + intent.setAction(ACTION_MAIN); + } + data = ""; + } else if (authority == null) { + data = scheme + ":"; + } else { + data = scheme + "://" + authority + data.substring(end); + } + } + } else { + data = ""; + } } if (data.length() > 0) { @@ -7084,14 +7244,53 @@ public class Intent implements Parcelable, Cloneable { * <p>You can convert the returned string back to an Intent with * {@link #getIntent}. * - * @param flags Additional operating flags. Either 0 or - * {@link #URI_INTENT_SCHEME}. + * @param flags Additional operating flags. Either 0, + * {@link #URI_INTENT_SCHEME}, or {@link #URI_ANDROID_APP_SCHEME}. * * @return Returns a URI encoding URI string describing the entire contents * of the Intent. */ public String toUri(int flags) { StringBuilder uri = new StringBuilder(128); + if ((flags&URI_ANDROID_APP_SCHEME) != 0) { + if (mPackage == null) { + throw new IllegalArgumentException( + "Intent must include an explicit package name to build an android-app: " + + this); + } + uri.append("android-app://"); + uri.append(mPackage); + String scheme = null; + if (mData != null) { + scheme = mData.getScheme(); + if (scheme != null) { + uri.append('/'); + uri.append(scheme); + String authority = mData.getEncodedAuthority(); + if (authority != null) { + uri.append('/'); + uri.append(authority); + String path = mData.getEncodedPath(); + if (path != null) { + uri.append(path); + } + String queryParams = mData.getEncodedQuery(); + if (queryParams != null) { + uri.append('?'); + uri.append(queryParams); + } + String fragment = mData.getEncodedFragment(); + if (fragment != null) { + uri.append('#'); + uri.append(fragment); + } + } + } + } + toUriFragment(uri, null, scheme == null ? Intent.ACTION_MAIN : Intent.ACTION_VIEW, + mPackage, flags); + return uri.toString(); + } String scheme = null; if (mData != null) { String data = mData.toString(); @@ -7121,27 +7320,38 @@ public class Intent implements Parcelable, Cloneable { uri.append("intent:"); } - uri.append("#Intent;"); + toUriFragment(uri, scheme, Intent.ACTION_VIEW, null, flags); + + return uri.toString(); + } + + private void toUriFragment(StringBuilder uri, String scheme, String defAction, + String defPackage, int flags) { + StringBuilder frag = new StringBuilder(128); - toUriInner(uri, scheme, flags); + toUriInner(frag, scheme, defAction, defPackage, 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); + mSelector.toUriInner(frag, mSelector.mData != null ? mSelector.mData.getScheme() : null, + null, null, flags); } - uri.append("end"); - - return uri.toString(); + if (frag.length() > 0) { + uri.append("#Intent;"); + uri.append(frag); + uri.append("end"); + } } - private void toUriInner(StringBuilder uri, String scheme, int flags) { + private void toUriInner(StringBuilder uri, String scheme, String defAction, + String defPackage, int flags) { if (scheme != null) { uri.append("scheme=").append(scheme).append(';'); } - if (mAction != null) { + if (mAction != null && !mAction.equals(defAction)) { uri.append("action=").append(Uri.encode(mAction)).append(';'); } if (mCategories != null) { @@ -7155,7 +7365,7 @@ public class Intent implements Parcelable, Cloneable { if (mFlags != 0) { uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';'); } - if (mPackage != null) { + if (mPackage != null && !mPackage.equals(defPackage)) { uri.append("package=").append(Uri.encode(mPackage)).append(';'); } if (mComponent != null) { diff --git a/core/java/android/content/pm/LabeledIntent.aidl b/core/java/android/content/pm/LabeledIntent.aidl new file mode 100644 index 0000000..ad96759 --- /dev/null +++ b/core/java/android/content/pm/LabeledIntent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +parcelable LabeledIntent; diff --git a/core/java/com/android/internal/content/ReferrerIntent.aidl b/core/java/com/android/internal/content/ReferrerIntent.aidl new file mode 100644 index 0000000..7cf6774 --- /dev/null +++ b/core/java/com/android/internal/content/ReferrerIntent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +parcelable ReferrerIntent; diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java new file mode 100644 index 0000000..8d9a1cf --- /dev/null +++ b/core/java/com/android/internal/content/ReferrerIntent.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +import android.content.Intent; +import android.os.Parcel; + +/** + * Subclass of Intent that also contains referrer (as a package name) information. + */ +public class ReferrerIntent extends Intent { + public final String mReferrer; + + public ReferrerIntent(Intent baseIntent, String referrer) { + super(baseIntent); + mReferrer = referrer; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + super.writeToParcel(dest, parcelableFlags); + dest.writeString(mReferrer); + } + + ReferrerIntent(Parcel in) { + readFromParcel(in); + mReferrer = in.readString(); + } + + public static final Creator<ReferrerIntent> CREATOR = new Creator<ReferrerIntent>() { + public ReferrerIntent createFromParcel(Parcel source) { + return new ReferrerIntent(source); + } + public ReferrerIntent[] newArray(int size) { + return new ReferrerIntent[size]; + } + }; +} |