Fix issue #21621920: VI: need mechanism to get current request

Add new APIs to associate a Request with a name, get all active
requests, and get active request by name.

Also add a new Activity.onProvideReferrer() which will allow
applications to propagate referrer information to the assistant
(and other apps they launch) in a consistent way.

Change-Id: I4ef74b5ed07447da9303a74a1bdf42e4966df363
diff --git a/api/current.txt b/api/current.txt
index 3b40214..6f74e49 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3449,6 +3449,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
@@ -5489,8 +5490,11 @@
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
 
-  public class VoiceInteractor {
+  public final class VoiceInteractor {
+    method public android.app.VoiceInteractor.Request getActiveRequest(java.lang.String);
+    method public android.app.VoiceInteractor.Request[] getActiveRequests();
     method public boolean submitRequest(android.app.VoiceInteractor.Request);
+    method public boolean submitRequest(android.app.VoiceInteractor.Request, java.lang.String);
     method public boolean[] supportsCommands(java.lang.String[]);
   }
 
@@ -5553,6 +5557,7 @@
     method public void cancel();
     method public android.app.Activity getActivity();
     method public android.content.Context getContext();
+    method public java.lang.String getName();
     method public void onAttached(android.app.Activity);
     method public void onCancel();
     method public void onDetached();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2dcb0a8..28a2372 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3535,6 +3535,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
@@ -5585,8 +5586,11 @@
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
 
-  public class VoiceInteractor {
+  public final class VoiceInteractor {
+    method public android.app.VoiceInteractor.Request getActiveRequest(java.lang.String);
+    method public android.app.VoiceInteractor.Request[] getActiveRequests();
     method public boolean submitRequest(android.app.VoiceInteractor.Request);
+    method public boolean submitRequest(android.app.VoiceInteractor.Request, java.lang.String);
     method public boolean[] supportsCommands(java.lang.String[]);
   }
 
@@ -5649,6 +5653,7 @@
     method public void cancel();
     method public android.app.Activity getActivity();
     method public android.content.Context getContext();
+    method public java.lang.String getName();
     method public void onAttached(android.app.Activity);
     method public void onCancel();
     method public void onDetached();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 90567c7..2a76a19 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4279,6 +4279,10 @@
         if (mParent == null) {
             int result = ActivityManager.START_RETURN_INTENT_TO_CALLER;
             try {
+                Uri referrer = onProvideReferrer();
+                if (referrer != null) {
+                    intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+                }
                 intent.migrateExtraStreamToClipData();
                 intent.prepareToLeaveProcess();
                 result = ActivityManagerNative.getDefault()
@@ -4463,6 +4467,10 @@
     @Override
     public void startActivityForResult(
             String who, Intent intent, int requestCode, @Nullable Bundle options) {
+        Uri referrer = onProvideReferrer();
+        if (referrer != null) {
+            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+        }
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, who,
@@ -4616,6 +4624,16 @@
     }
 
     /**
+     * Override to generate the desired referrer for the content currently being shown
+     * by the app.  The default implementation returns null, meaning the referrer will simply
+     * be the android-app: of the package name of this activity.  Return a non-null Uri to
+     * have that supplied as the {@link Intent#EXTRA_REFERRER} of any activities started from it.
+     */
+    public Uri onProvideReferrer() {
+        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
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index dabcc50c..6ae21eb 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2197,7 +2197,8 @@
             Bundle extras = data.readBundle();
             AssistStructure structure = AssistStructure.CREATOR.createFromParcel(data);
             AssistContent content = AssistContent.CREATOR.createFromParcel(data);
-            reportAssistContextExtras(token, extras, structure, content);
+            Uri referrer = data.readInt() != 0 ? Uri.CREATOR.createFromParcel(data) : null;
+            reportAssistContextExtras(token, extras, structure, content, referrer);
             reply.writeNoException();
             return true;
         }
@@ -5367,7 +5368,7 @@
     }
 
     public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
-            AssistContent content) throws RemoteException {
+            AssistContent content, Uri referrer) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -5375,6 +5376,12 @@
         data.writeBundle(extras);
         structure.writeToParcel(data, 0);
         content.writeToParcel(data, 0);
+        if (referrer != null) {
+            data.writeInt(1);
+            referrer.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
         mRemote.transact(REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ffb3fb8..e21c04a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2572,9 +2572,11 @@
         AssistStructure structure = null;
         AssistContent content = new AssistContent();
         ActivityClientRecord r = mActivities.get(cmd.activityToken);
+        Uri referrer = null;
         if (r != null) {
             r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
             r.activity.onProvideAssistData(data);
+            referrer = r.activity.onProvideReferrer();
             if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
                 structure = new AssistStructure(r.activity);
                 Intent activityIntent = r.activity.getIntent();
@@ -2597,7 +2599,7 @@
         }
         IActivityManager mgr = ActivityManagerNative.getDefault();
         try {
-            mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content);
+            mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content, referrer);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0d5e1c7..9311e5e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -436,7 +436,7 @@
             throws RemoteException;
 
     public void reportAssistContextExtras(IBinder token, Bundle extras,
-            AssistStructure structure, AssistContent content) throws RemoteException;
+            AssistStructure structure, AssistContent content, Uri referrer) throws RemoteException;
 
     public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
             Bundle args) throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 0cc57ba..653f1b6 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -24,6 +24,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.hardware.input.InputManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
@@ -1439,7 +1440,7 @@
      * objects and dispatches this call to the system activity manager; you can
      * override this to watch for the application to start an activity, and 
      * modify what happens when it does. 
-     *  
+     *
      * <p>This method returns an {@link ActivityResult} object, which you can 
      * use when intercepting application calls to avoid performing the start 
      * activity action but still return the result the application is 
@@ -1448,10 +1449,10 @@
      * you would like the application to see, and don't call up to the super 
      * class.  Note that an application is only expecting a result if 
      * <var>requestCode</var> is &gt;= 0.
-     *  
+     *
      * <p>This method throws {@link android.content.ActivityNotFoundException}
      * if there was no Activity found to run the given Intent.
-     * 
+     *
      * @param who The Context from which the activity is being started.
      * @param contextThread The main thread of the Context from which the activity
      *                      is being started.
@@ -1464,23 +1465,27 @@
      * @param requestCode Identifier for this request's result; less than zero 
      *                    if the caller is not expecting a result.
      * @param options Addition options.
-     * 
+     *
      * @return To force the return of a particular result, return an 
      *         ActivityResult object containing the desired data; otherwise
      *         return null.  The default implementation always returns null.
-     *  
+     *
      * @throws android.content.ActivityNotFoundException
-     * 
+     *
      * @see Activity#startActivity(Intent)
      * @see Activity#startActivityForResult(Intent, int)
      * @see Activity#startActivityFromChild
-     * 
+     *
      * {@hide}
      */
     public ActivityResult execStartActivity(
             Context who, IBinder contextThread, IBinder token, Activity target,
             Intent intent, int requestCode, Bundle options) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
+        Uri referrer = target != null ? target.onProvideReferrer() : null;
+        if (referrer != null) {
+            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
+        }
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index eccd9dc..9cc399d 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -58,9 +58,11 @@
  * request, rather than holding on to the activity instance yourself, either explicitly
  * or implicitly through a non-static inner class.
  */
-public class VoiceInteractor {
+public final class VoiceInteractor {
     static final String TAG = "VoiceInteractor";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
+
+    static final Request[] NO_REQUESTS = new Request[0];
 
     final IVoiceInteractor mInteractor;
 
@@ -189,7 +191,7 @@
         }
     };
 
-    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<>();
 
     static final int MSG_CONFIRMATION_RESULT = 1;
     static final int MSG_PICK_OPTION_RESULT = 2;
@@ -206,10 +208,22 @@
         IVoiceInteractorRequest mRequestInterface;
         Context mContext;
         Activity mActivity;
+        String mName;
 
         Request() {
         }
 
+        /**
+         * Return the name this request was submitted through
+         * {@link #submitRequest(android.app.VoiceInteractor.Request, String)}.
+         */
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Cancel this active request.
+         */
         public void cancel() {
             try {
                 mRequestInterface.cancel();
@@ -218,20 +232,39 @@
             }
         }
 
+        /**
+         * Return the current {@link Context} this request is associated with.  May change
+         * if the activity hosting it goes through a configuration change.
+         */
         public Context getContext() {
             return mContext;
         }
 
+        /**
+         * Return the current {@link Activity} this request is associated with.  Will change
+         * if the activity is restarted such as through a configuration change.
+         */
         public Activity getActivity() {
             return mActivity;
         }
 
+        /**
+         * Report from voice interaction service: this operation has been canceled, typically
+         * as a completion of a previous call to {@link #cancel}.
+         */
         public void onCancel() {
         }
 
+        /**
+         * The request is now attached to an activity, or being re-attached to a new activity
+         * after a configuration change.
+         */
         public void onAttached(Activity activity) {
         }
 
+        /**
+         * The request is being detached from an activity.
+         */
         public void onDetached() {
         }
 
@@ -239,6 +272,7 @@
             mRequestInterface = null;
             mContext = null;
             mActivity = null;
+            mName = null;
         }
 
         abstract IVoiceInteractorRequest submit(IVoiceInteractor interactor,
@@ -761,12 +795,31 @@
     }
 
     public boolean submitRequest(Request request) {
+        return submitRequest(request, null);
+    }
+
+    /**
+     * Submit a new {@link Request} to the voice interaction service.  The request must be
+     * one of the available subclasses -- {@link ConfirmationRequest}, {@link PickOptionRequest},
+     * {@link CompleteVoiceRequest}, {@link AbortVoiceRequest}, or {@link CommandRequest}.
+     *
+     * @param request The desired request to submit.
+     * @param name An optional name for this request, or null. This can be used later with
+     * {@link #getActiveRequests} and {@link #getActiveRequest} to find the request.
+     *
+     * @return Returns true of the request was successfully submitted, else false.
+     */
+    public boolean submitRequest(Request request, String name) {
         try {
+            if (request.mRequestInterface != null) {
+                throw new IllegalStateException("Given " + request + " is already active");
+            }
             IVoiceInteractorRequest ireq = request.submit(mInteractor,
                     mContext.getOpPackageName(), mCallback);
             request.mRequestInterface = ireq;
             request.mContext = mContext;
             request.mActivity = mActivity;
+            request.mName = name;
             synchronized (mActiveRequests) {
                 mActiveRequests.put(ireq.asBinder(), request);
             }
@@ -778,6 +831,43 @@
     }
 
     /**
+     * Return all currently active requests.
+     */
+    public Request[] getActiveRequests() {
+        synchronized (mActiveRequests) {
+            final int N = mActiveRequests.size();
+            if (N <= 0) {
+                return NO_REQUESTS;
+            }
+            Request[] requests = new Request[N];
+            for (int i=0; i<N; i++) {
+                requests[i] = mActiveRequests.valueAt(i);
+            }
+            return requests;
+        }
+    }
+
+    /**
+     * Return any currently active request that was submitted with the given name.
+     *
+     * @param name The name used to submit the request, as per
+     * {@link #submitRequest(android.app.VoiceInteractor.Request, String)}.
+     * @return Returns the active request with that name, or null if there was none.
+     */
+    public Request getActiveRequest(String name) {
+        synchronized (mActiveRequests) {
+            final int N = mActiveRequests.size();
+            for (int i=0; i<N; i++) {
+                Request req = mActiveRequests.valueAt(i);
+                if (name == req.getName() || (name != null && name.equals(req.getName()))) {
+                    return req;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Queries the supported commands available from the VoiceInteractionService.
      * The command is a string that describes the generic operation to be performed.
      * An example might be "org.example.commands.PICK_DATE" to ask the user to pick
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5c23204..25be96a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1226,7 +1226,8 @@
      * <p>
      * Input: {@link #EXTRA_ASSIST_PACKAGE}, {@link #EXTRA_ASSIST_CONTEXT}, can provide
      * additional optional contextual information about where the user was when they
-     * requested the assist.
+     * requested the assist; {@link #EXTRA_REFERRER} may be set with additional referrer
+     * information.
      * Output: nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5fedbe4..0d08c2a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10727,12 +10727,15 @@
     }
 
     public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
-            AssistContent content) {
+            AssistContent content, Uri referrer) {
         PendingAssistExtras pae = (PendingAssistExtras)token;
         synchronized (pae) {
             pae.result = extras;
             pae.structure = structure;
             pae.content = content;
+            if (referrer != null) {
+                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
+            }
             pae.haveResult = true;
             pae.notifyAll();
             if (pae.intent == null && pae.receiver == null) {
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index dae1ac3..3090a11 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
@@ -79,6 +80,9 @@
         super.onShow(args, showFlags);
         mState = STATE_IDLE;
         mStartIntent = args.getParcelable("intent");
+        if (mStartIntent == null) {
+            mStartIntent = new Intent(getContext(), TestInteractionActivity.class);
+        }
         if (mAssistVisualizer != null) {
             mAssistVisualizer.clearAssistData();
         }
@@ -119,6 +123,7 @@
     }
 
     public void onHandleAssist(Bundle assistBundle) {
+        boolean hasStructure = false;
         if (assistBundle != null) {
             Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
             if (assistContext != null) {
@@ -126,6 +131,7 @@
                 if (mAssistStructure != null) {
                     if (mAssistVisualizer != null) {
                         mAssistVisualizer.setAssistStructure(mAssistStructure);
+                        hasStructure = true;
                     }
                 }
                 AssistContent content = AssistContent.getAssistContent(assistContext);
@@ -133,10 +139,13 @@
                     Log.i(TAG, "Assist intent: " + content.getIntent());
                     Log.i(TAG, "Assist clipdata: " + content.getClipData());
                 }
-                return;
+            }
+            Uri referrer = assistBundle.getParcelable(Intent.EXTRA_REFERRER);
+            if (referrer != null) {
+                Log.i(TAG, "Referrer: " + referrer);
             }
         }
-        if (mAssistVisualizer != null) {
+        if (!hasStructure && mAssistVisualizer != null) {
             mAssistVisualizer.clearAssistData();
         }
     }
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index 67db289..c038414 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -23,15 +23,18 @@
 import android.os.Bundle;
 import android.service.voice.VoiceInteractionService;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
 
 public class TestInteractionActivity extends Activity implements View.OnClickListener {
     static final String TAG = "TestInteractionActivity";
 
+    static final String REQUEST_ABORT = "abort";
+    static final String REQUEST_COMPLETE = "complete";
+    static final String REQUEST_PICK = "pick";
+    static final String REQUEST_CONFIRM = "confirm";
+
     VoiceInteractor mInteractor;
     VoiceInteractor.Request mCurrentRequest = null;
     TextView mLog;
@@ -72,21 +75,32 @@
         mCancelButton.setOnClickListener(this);
 
         mInteractor = getVoiceInteractor();
-        mCurrentRequest = new VoiceInteractor.ConfirmationRequest(
-                new VoiceInteractor.Prompt("This is a confirmation"), null) {
-            @Override
-            public void onCancel() {
-                Log.i(TAG, "Canceled!");
-                getActivity().finish();
-            }
 
-            @Override
-            public void onConfirmationResult(boolean confirmed, Bundle result) {
-                Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
-                getActivity().finish();
-            }
-        };
-        mInteractor.submitRequest(mCurrentRequest);
+        VoiceInteractor.Request[] active = mInteractor.getActiveRequests();
+        for (int i=0; i<active.length; i++) {
+            Log.i(TAG, "Active #" + i + " / " + active[i].getName() + ": " + active[i]);
+        }
+
+        mCurrentRequest = mInteractor.getActiveRequest(REQUEST_CONFIRM);
+        if (mCurrentRequest == null) {
+            mCurrentRequest = new VoiceInteractor.ConfirmationRequest(
+                    new VoiceInteractor.Prompt("This is a confirmation"), null) {
+                @Override
+                public void onCancel() {
+                    Log.i(TAG, "Canceled!");
+                    getActivity().finish();
+                }
+
+                @Override
+                public void onConfirmationResult(boolean confirmed, Bundle result) {
+                    Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
+                    getActivity().finish();
+                }
+            };
+            mInteractor.submitRequest(mCurrentRequest, REQUEST_CONFIRM);
+        } else {
+            Log.i(TAG, "Restarting with active confirmation: " + mCurrentRequest);
+        }
     }
 
     @Override
@@ -112,7 +126,7 @@
                     getActivity().finish();
                 }
             };
-            mInteractor.submitRequest(req);
+            mInteractor.submitRequest(req, REQUEST_ABORT);
         } else if (v == mCompleteButton) {
             VoiceInteractor.CompleteVoiceRequest req = new VoiceInteractor.CompleteVoiceRequest(
                     new VoiceInteractor.Prompt("Woohoo, completed!"), null) {
@@ -129,7 +143,7 @@
                     getActivity().finish();
                 }
             };
-            mInteractor.submitRequest(req);
+            mInteractor.submitRequest(req, REQUEST_COMPLETE);
         } else if (v == mPickButton) {
             VoiceInteractor.PickOptionRequest.Option[] options =
                     new VoiceInteractor.PickOptionRequest.Option[5];
@@ -168,7 +182,7 @@
                     }
                 }
             };
-            mInteractor.submitRequest(req);
+            mInteractor.submitRequest(req, REQUEST_PICK);
         } else if (v == mJumpOutButton) {
             Log.i(TAG, "Jump out");
             Intent intent = new Intent(Intent.ACTION_MAIN);
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
index 5d212a4..a7636c3 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.view.View;
 
@@ -41,6 +42,11 @@
         super.onDestroy();
     }
 
+    @Override
+    public Uri onProvideReferrer() {
+        return Uri.parse("http://www.example.com/VoiceInteractionMain");
+    }
+
     View.OnClickListener mStartListener = new View.OnClickListener() {
         public void onClick(View v) {
             startService(new Intent(VoiceInteractionMain.this, MainInteractionService.class));