diff options
17 files changed, 459 insertions, 84 deletions
diff --git a/Android.mk b/Android.mk index b77c2edc15b9..960bf2620e7e 100644 --- a/Android.mk +++ b/Android.mk @@ -559,6 +559,7 @@ aidl_files := \ frameworks/base/core/java/android/content/pm/ProviderInfo.aidl \ frameworks/base/core/java/android/content/pm/PackageStats.aidl \ frameworks/base/core/java/android/content/pm/PermissionGroupInfo.aidl \ + frameworks/base/core/java/android/content/pm/LabeledIntent.aidl \ frameworks/base/core/java/android/content/ComponentName.aidl \ frameworks/base/core/java/android/content/SyncStats.aidl \ frameworks/base/core/java/android/content/ContentValues.aidl \ diff --git a/api/current.txt b/api/current.txt index 7df6e6e2d456..a832ef02269b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3303,6 +3303,7 @@ package android.app { method public final android.app.Activity getParent(); method public android.content.Intent getParentActivityIntent(); method public android.content.SharedPreferences getPreferences(int); + method public android.net.Uri getReferrer(); method public int getRequestedOrientation(); method public int getTaskId(); method public final java.lang.CharSequence getTitle(); @@ -7750,6 +7751,7 @@ package android.content { field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI"; field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER"; field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER"; + field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME"; field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token"; field public static final java.lang.String EXTRA_REPLACEMENT_EXTRAS = "android.intent.extra.REPLACEMENT_EXTRAS"; field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING"; @@ -7809,6 +7811,7 @@ package android.content { field public static final int FLAG_RECEIVER_REGISTERED_ONLY = 1073741824; // 0x40000000 field public static final int FLAG_RECEIVER_REPLACE_PENDING = 536870912; // 0x20000000 field public static final java.lang.String METADATA_DOCK_HOME = "android.dock_home"; + field public static final int URI_ANDROID_APP_SCHEME = 2; // 0x2 field public static final int URI_INTENT_SCHEME = 1; // 0x1 } diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index bc57030e3da0..0cad17d6be5b 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -127,6 +127,7 @@ public class Am extends BaseCommand { " am screen-compat [on|off] <PACKAGE>\n" + " am to-uri [INTENT]\n" + " am to-intent-uri [INTENT]\n" + + " am to-app-uri [INTENT]\n" + " am switch-user <USER_ID>\n" + " am start-user <USER_ID>\n" + " am stop-user <USER_ID>\n" + @@ -229,6 +230,8 @@ public class Am extends BaseCommand { "\n" + "am to-intent-uri: print the given Intent specification as an intent: URI.\n" + "\n" + + "am to-app-uri: print the given Intent specification as an android-app: URI.\n" + + "\n" + "am switch-user: switch to put USER_ID in the foreground, starting\n" + " execution of that user if it is currently stopped.\n" + "\n" + @@ -270,7 +273,7 @@ public class Am extends BaseCommand { " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" + " [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]\n" + " (to embed a comma into a string escape it using \"\\,\")\n" + - " [-n <COMPONENT>] [-f <FLAGS>]\n" + + " [-n <COMPONENT>] [-p <PACKAGE>] [-f <FLAGS>]\n" + " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]\n" + " [--debug-log-resolution] [--exclude-stopped-packages]\n" + @@ -337,9 +340,11 @@ public class Am extends BaseCommand { } else if (op.equals("screen-compat")) { runScreenCompat(); } else if (op.equals("to-uri")) { - runToUri(false); + runToUri(0); } else if (op.equals("to-intent-uri")) { - runToUri(true); + runToUri(Intent.URI_INTENT_SCHEME); + } else if (op.equals("to-app-uri")) { + runToUri(Intent.URI_ANDROID_APP_SCHEME); } else if (op.equals("switch-user")) { runSwitchUser(); } else if (op.equals("start-user")) { @@ -502,6 +507,12 @@ public class Am extends BaseCommand { if (intent == baseIntent) { hasIntentInfo = true; } + } else if (opt.equals("-p")) { + String str = nextArgRequired(); + intent.setPackage(str); + if (intent == baseIntent) { + hasIntentInfo = true; + } } else if (opt.equals("-f")) { String str = nextArgRequired(); intent.setFlags(Integer.decode(str).intValue()); @@ -607,7 +618,8 @@ public class Am extends BaseCommand { } 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); + baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME + | Intent.URI_ANDROID_APP_SCHEME); } else if (arg.indexOf('/') >= 0) { // The argument is a component name. Build an Intent to launch // it. @@ -1549,9 +1561,9 @@ public class Am extends BaseCommand { } while (packageName != null); } - private void runToUri(boolean intentScheme) throws Exception { + private void runToUri(int flags) throws Exception { Intent intent = makeIntent(UserHandle.USER_CURRENT); - System.out.println(intent.toUri(intentScheme ? Intent.URI_INTENT_SCHEME : 0)); + System.out.println(intent.toUri(flags)); } private class IntentReceiver extends IIntentReceiver.Stub { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4b705dde1344..148527fc47df 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 cf6c04959de8..5f21d754558b 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 0123e16739d3..d1b77b9f99de 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 f53075cf5e03..42acbc66ac8a 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 60a013e47c94..d96153a33e1e 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 b654a6aae8af..873e3373d80d 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 e06f034c1570..57f6028cb7b5 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 000000000000..ad9675924ab5 --- /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 000000000000..7cf6774b631d --- /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 000000000000..8d9a1cf9eee5 --- /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]; + } + }; +} diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 1a0e45ee4177..c12cadb01c8a 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -21,6 +21,7 @@ import android.os.PersistableBundle; import android.os.Trace; import com.android.internal.app.ResolverActivity; +import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; @@ -128,7 +129,7 @@ final class ActivityRecord { final int requestCode; // code given by requester (resultTo) ArrayList<ResultInfo> results; // pending ActivityResult objs we have received HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act - ArrayList<Intent> newIntents; // any pending new intents for single-top mode + ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode ActivityOptions pendingOptions; // most recently given options ActivityOptions returningOptions; // options that are coming back via convertToTranslucent HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold @@ -629,9 +630,9 @@ final class ActivityRecord { } } - void addNewIntentLocked(Intent intent) { + void addNewIntentLocked(ReferrerIntent intent) { if (newIntents == null) { - newIntents = new ArrayList<Intent>(); + newIntents = new ArrayList<>(); } newIntents.add(intent); } @@ -640,7 +641,7 @@ final class ActivityRecord { * Deliver a new Intent to an existing activity, so that its onNewIntent() * method will be called at the proper time. */ - final void deliverNewIntentLocked(int callingUid, Intent intent) { + final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) { // The activity now gets access to the data associated with this Intent. service.grantUriPermissionFromIntentLocked(callingUid, packageName, intent, getUriPermissionsLocked(), userId); @@ -649,14 +650,14 @@ final class ActivityRecord { // device is sleeping, then all activities are stopped, so in that // case we will deliver it if this is the current top activity on its // stack. + final ReferrerIntent rintent = new ReferrerIntent(intent, referrer); boolean unsent = true; if ((state == ActivityState.RESUMED || (service.isSleeping() && task.stack.topRunningActivityLocked(null) == this)) && app != null && app.thread != null) { try { - ArrayList<Intent> ar = new ArrayList<Intent>(); - intent = new Intent(intent); - ar.add(intent); + ArrayList<ReferrerIntent> ar = new ArrayList<>(1); + ar.add(rintent); app.thread.scheduleNewIntent(ar, appToken); unsent = false; } catch (RemoteException e) { @@ -668,7 +669,7 @@ final class ActivityRecord { } } if (unsent) { - addNewIntentLocked(new Intent(intent)); + addNewIntentLocked(rintent); } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index b955011bc3b2..cfcab6818542 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -44,6 +44,7 @@ import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; import android.util.ArraySet; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; @@ -2953,7 +2954,8 @@ final class ActivityStack { parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK || parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP || (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { - parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent); + parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent, + srec.packageName); } else { try { ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( @@ -3773,7 +3775,7 @@ final class ActivityStack { private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) { List<ResultInfo> results = null; - List<Intent> newIntents = null; + List<ReferrerIntent> newIntents = null; if (andResume) { results = r.results; newIntents = r.newIntents; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 079df7d14e4b..099151fb20ca 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -97,6 +97,7 @@ import android.view.InputEvent; import android.view.Surface; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import com.android.internal.os.TransferPipe; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; @@ -1101,7 +1102,7 @@ public final class ActivityStackSupervisor implements DisplayListener { throw new RemoteException(); } List<ResultInfo> results = null; - List<Intent> newIntents = null; + List<ReferrerIntent> newIntents = null; if (andResume) { results = r.results; newIntents = r.newIntents; @@ -1155,9 +1156,9 @@ public final class ActivityStackSupervisor implements DisplayListener { app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP); app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), - r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, - results, newIntents, !andResume, mService.isNextTransitionForward(), - profilerInfo); + r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState, + r.icicle, r.persistentState, results, newIntents, !andResume, + mService.isNextTransitionForward(), profilerInfo); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { // This may be a heavy-weight process! Note that the package @@ -1896,7 +1897,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - top.deliverNewIntentLocked(callingUid, r.intent); + top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); } else { // A special case: we need to // start the activity because it is not currently @@ -1922,7 +1923,8 @@ public final class ActivityStackSupervisor implements DisplayListener { if (intentActivity.frontOfTask) { intentActivity.task.setIntent(r); } - intentActivity.deliverNewIntentLocked(callingUid, r.intent); + intentActivity.deliverNewIntentLocked(callingUid, r.intent, + r.launchedFromPackage); } else if (!r.intent.filterEquals(intentActivity.task.intent)) { // In this case we are launching the root activity // of the task, but with a different intent. We @@ -1995,7 +1997,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // is the case, so this is it! return ActivityManager.START_RETURN_INTENT_TO_CALLER; } - top.deliverNewIntentLocked(callingUid, r.intent); + top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); return ActivityManager.START_DELIVERED_TO_TOP; } } @@ -2071,7 +2073,7 @@ public final class ActivityStackSupervisor implements DisplayListener { keepCurTransition = true; if (top != null) { ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - top.deliverNewIntentLocked(callingUid, r.intent); + top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); // For paranoia, make sure we have correctly // resumed the top activity. targetStack.mLastPausedActivity = null; @@ -2092,7 +2094,7 @@ public final class ActivityStackSupervisor implements DisplayListener { task.moveActivityToFrontLocked(top); ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task); top.updateOptionsLocked(options); - top.deliverNewIntentLocked(callingUid, r.intent); + top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); targetStack.mLastPausedActivity = null; if (doResume) { targetStack.resumeTopActivityLocked(null); @@ -2132,7 +2134,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // is the case, so this is it! return ActivityManager.START_RETURN_INTENT_TO_CALLER; } - top.deliverNewIntentLocked(callingUid, r.intent); + top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); return ActivityManager.START_DELIVERED_TO_TOP; } } diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index 94874c823e93..b065b88c3459 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -140,6 +140,8 @@ public class ActivityTestMain extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Log.i(TAG, "Referrer: " + getReferrer()); + mAm = (ActivityManager)getSystemService(ACTIVITY_SERVICE); if (savedInstanceState != null) { mOverrideConfig = savedInstanceState.getParcelable(KEY_CONFIGURATION); |