Use noteOpNoThrow instead of checkOpNoThrow in AssistDataRequester

To audit data access and verify the uid and package are consistent,
use noteOpNoThrow instead of checkOpNoThrow.

Bug: 187439908
Test: atest AssistDataRequesterTest
Test: atest CtsVoiceInteractionTestCases
Test: atest CtsAssistTestCases
Change-Id: Ib48040b5b55be09958da4398f4d663908573568c
diff --git a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
index dbc3b51..f5184e7 100644
--- a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
+++ b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
@@ -39,7 +39,6 @@
 Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
 Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
 Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
 Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index dc6825c..c935637 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -230,7 +230,7 @@
             in IBinder activityToken, int flags);
     boolean isAssistDataAllowedOnCurrentActivity();
     boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
-            in String callingPackageName);
+            in String callingPackageName, String callingAttributionTag);
 
     /**
      * Notify the system that the keyguard is going away.
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index b20dccc..387708c 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.service.voice;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -32,9 +33,12 @@
      * Start a new voice interaction session when requested from within an activity
      * by Activity.startLocalVoiceInteraction()
      * @param callingActivity The binder token representing the calling activity.
-     * @param options 
+     * @param attributionTag The attribution tag of the calling context or {@code null} for default
+     *                       attribution
+     * @param options A Bundle of private arguments to the current voice interaction service
      */
-    public abstract void startLocalVoiceInteraction(IBinder callingActivity, Bundle options);
+    public abstract void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+            @Nullable String attributionTag, @NonNull Bundle options);
 
     /**
      * Returns whether the currently selected voice interaction service supports local voice
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1170237..1b46107 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -242,7 +242,7 @@
             throw new IllegalStateException("Not available until onReady() is called");
         }
         try {
-            mSystemService.showSession(args, flags);
+            mSystemService.showSession(args, flags, getAttributionTag());
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 42500b4..95d4b0a 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1384,7 +1384,8 @@
             throw new IllegalStateException("Can't call before onCreate()");
         }
         try {
-            mSystemService.showSessionFromSession(mToken, args, flags);
+            mSystemService.showSessionFromSession(mToken, args, flags,
+                    mContext.getAttributionTag());
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index c056e26..f843318 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,6 +17,7 @@
 package com.android.internal.app;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -67,12 +68,53 @@
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
     }
 
-    public boolean showSessionForActiveService(Bundle args, int sourceFlags,
-            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+    /**
+     * Shows the session for the currently active service. Used to start a new session from system
+     * affordances.
+     *
+     * @param args the bundle to pass as arguments to the voice interaction session
+     * @param sourceFlags flags indicating the source of this show
+     * @param showCallback optional callback to be notified when the session was shown
+     * @param activityToken optional token of activity that needs to be on top
+     *
+     * @deprecated Use {@link #showSessionForActiveService(Bundle, int, String,
+     *             IVoiceInteractionSessionShowCallback, IBinder)} instead
+     */
+    @Deprecated
+    public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
+        return showSessionForActiveServiceInternal(args, sourceFlags, /* attributionTag */ null,
+                showCallback, activityToken);
+    }
+
+    /**
+     * Shows the session for the currently active service. Used to start a new session from system
+     * affordances.
+     *
+     * @param args the bundle to pass as arguments to the voice interaction session
+     * @param sourceFlags flags indicating the source of this show
+     * @param attributionTag the attribution tag of the calling context or {@code null} for default
+     *                       attribution
+     * @param showCallback optional callback to be notified when the session was shown
+     * @param activityToken optional token of activity that needs to be on top
+     */
+    public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+            @Nullable String attributionTag,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
+        return showSessionForActiveServiceInternal(args, sourceFlags, attributionTag, showCallback,
+                activityToken);
+    }
+
+    private boolean showSessionForActiveServiceInternal(@NonNull Bundle args, int sourceFlags,
+            @Nullable String attributionTag,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
         try {
             if (mVoiceInteractionManagerService != null) {
                 return mVoiceInteractionManagerService.showSessionForActiveService(args,
-                        sourceFlags, showCallback, activityToken);
+                        sourceFlags, attributionTag, showCallback, activityToken);
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call showSessionForActiveService", e);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 4b01357..83bf801 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -39,15 +39,16 @@
 import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
 
 interface IVoiceInteractionManagerService {
-    void showSession(in Bundle sessionArgs, int flags);
+    void showSession(in Bundle sessionArgs, int flags, String attributionTag);
     boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
             IVoiceInteractor interactor);
-    boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
+    boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags,
+            String attributionTag);
     boolean hideSessionFromSession(IBinder token);
     int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
-            String callingFeatureId);
+            String attributionTag);
     int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
-            String callingFeatureId);
+            String attributionTag);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
@@ -125,12 +126,14 @@
      *
      * @param args the bundle to pass as arguments to the voice interaction session
      * @param sourceFlags flags indicating the source of this show
+     * @param attributionTag the attribution tag of the calling context or {@code null} for default
+     *                       attribution
      * @param showCallback optional callback to be notified when the session was shown
      * @param activityToken optional token of activity that needs to be on top
      * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
      */
     @EnforcePermission("ACCESS_VOICE_INTERACTION_SERVICE")
-    boolean showSessionForActiveService(in Bundle args, int sourceFlags,
+    boolean showSessionForActiveService(in Bundle args, int sourceFlags, String attributionTag,
             IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken);
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 916526d..ee30972 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -19,7 +19,6 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 import static android.app.ActivityTaskManager.getService;
 
 import android.annotation.NonNull;
@@ -27,7 +26,6 @@
 import android.app.Activity;
 import android.app.ActivityClient;
 import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -337,7 +335,8 @@
      * Shows a voice session identified by {@code token}
      * @return true if the session was shown, false otherwise
      */
-    public boolean showVoiceSession(IBinder token, Bundle args, int flags) {
+    public boolean showVoiceSession(@NonNull IBinder token, @NonNull Bundle args, int flags,
+            @Nullable String attributionTag) {
         IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface(
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
         if (service == null) {
@@ -346,7 +345,7 @@
         args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime());
 
         try {
-            return service.showSessionFromSession(token, args, flags);
+            return service.showSessionFromSession(token, args, flags, attributionTag);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 7c2673c..57ffdab 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -310,7 +310,8 @@
 
     private void startVoiceInteractor(Bundle args) {
         mAssistUtils.showSessionForActiveService(args,
-                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, null, null);
+                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mContext.getAttributionTag(),
+                null, null);
     }
 
     public void launchVoiceAssistFromKeyguard() {
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 98ad81d..98129ed 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -23,6 +23,8 @@
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.IActivityTaskManager;
@@ -41,6 +43,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Helper class to asynchronously fetch the assist data and screenshot from the current running
@@ -141,50 +144,35 @@
     }
 
     /**
-     * Request that autofill data be loaded asynchronously. The resulting data will be provided
-     * through the {@link AssistDataRequesterCallbacks}.
-     *
-     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
-     * String, boolean)}.
-     */
-    public void requestAutofillData(List<IBinder> activityTokens, int callingUid,
-            String callingPackage) {
-        requestData(activityTokens, true /* requestAutofillData */,
-                true /* fetchData */, false /* fetchScreenshot */,
-                true /* fetchStructure */, true /* allowFetchData */,
-                false /* allowFetchScreenshot */, false /* ignoreTopActivityCheck */, callingUid,
-                callingPackage);
-    }
-
-    /**
      * Request that assist data be loaded asynchronously. The resulting data will be provided
      * through the {@link AssistDataRequesterCallbacks}.
      *
-     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
-     * String, boolean)}.
+     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+     * int, String, String)}.
      */
-    public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+    public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
             final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
-            int callingUid, String callingPackage) {
+            int callingUid, @NonNull String callingPackage,
+            @Nullable String callingAttributionTag) {
         requestAssistData(activityTokens, fetchData, fetchScreenshot, true /* fetchStructure */,
                 allowFetchData, allowFetchScreenshot, false /* ignoreTopActivityCheck */,
-                callingUid, callingPackage);
+                callingUid, callingPackage, callingAttributionTag);
     }
 
     /**
      * Request that assist data be loaded asynchronously. The resulting data will be provided
      * through the {@link AssistDataRequesterCallbacks}.
      *
-     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
-     * String, boolean)}.
+     * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+     * int, String, String)}.
      */
-    public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+    public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
             final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
             boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
-            String callingPackage) {
+            @NonNull String callingPackage, @Nullable String callingAttributionTag) {
         requestData(activityTokens, false /* requestAutofillData */, fetchData, fetchScreenshot,
                 fetchStructure, allowFetchData, allowFetchScreenshot, ignoreTopActivityCheck,
-                callingUid, callingPackage);
+                callingUid, callingPackage, callingAttributionTag);
     }
 
     /**
@@ -208,14 +196,22 @@
      *     requester is allowed to fetch the assist screenshot
      * @param ignoreTopActivityCheck overrides the check for whether the activity is in focus when
      *     making the request. Used when passing an activity from Recents.
+     * @param callingUid the uid of the real caller
+     * @param callingPackage the package name of the real caller
+     * @param callingAttributionTag The {@link Context#createAttributionContext attribution tag}
+     *     of the calling context or {@code null} for default attribution
      */
-    private void requestData(List<IBinder> activityTokens, final boolean requestAutofillData,
-            final boolean fetchData, final boolean fetchScreenshot, final boolean fetchStructure,
-            boolean allowFetchData, boolean allowFetchScreenshot, boolean ignoreTopActivityCheck,
-            int callingUid, String callingPackage) {
+    private void requestData(@NonNull List<IBinder> activityTokens,
+            final boolean requestAutofillData, final boolean fetchData,
+            final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
+            boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
+            @NonNull String callingPackage, @Nullable String callingAttributionTag) {
         // TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
         //                   then no assist data is requested for any of the other activities
 
+        Objects.requireNonNull(activityTokens);
+        Objects.requireNonNull(callingPackage);
+
         // Early exit if there are no activity to fetch for
         if (activityTokens.isEmpty()) {
             // No activities, just dispatch request-complete
@@ -241,8 +237,9 @@
         mAssistScreenshot.clear();
 
         if (fetchData) {
-            if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
-                    == MODE_ALLOWED && allowFetchData) {
+            if (mAppOpsManager.noteOpNoThrow(mRequestStructureAppOps, callingUid,
+                    callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+                    && allowFetchData) {
                 final int numActivities = activityTokens.size();
                 for (int i = 0; i < numActivities; i++) {
                     IBinder topActivity = activityTokens.get(i);
@@ -291,8 +288,9 @@
         }
 
         if (fetchScreenshot) {
-            if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
-                    == MODE_ALLOWED && allowFetchScreenshot) {
+            if (mAppOpsManager.noteOpNoThrow(mRequestScreenshotAppOps, callingUid,
+                    callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+                    && allowFetchScreenshot) {
                 try {
                     MetricsLogger.count(mContext, "assist_with_screen", 1);
                     mPendingScreenshotCount++;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 1ccdaf7..8f0cf2f 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1005,6 +1005,7 @@
     public boolean showAssistFromActivity(IBinder token, Bundle args) {
         final long ident = Binder.clearCallingIdentity();
         try {
+            final String callingAttributionTag;
             synchronized (mGlobalLock) {
                 final ActivityRecord caller = ActivityRecord.forTokenLocked(token);
                 final Task topRootTask = mService.getTopDisplayFocusedRootTask();
@@ -1020,9 +1021,10 @@
                             + " is not visible");
                     return false;
                 }
+                callingAttributionTag = top.launchedFromFeatureId;
             }
             return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION,
-                    null /* showCallback */, token);
+                    callingAttributionTag, null /* showCallback */, token);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -1039,6 +1041,7 @@
     @Override
     public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
         Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+        final String callingAttributionTag;
         synchronized (mGlobalLock) {
             final Task topRootTask = mService.getTopDisplayFocusedRootTask();
             final ActivityRecord activity = topRootTask != null
@@ -1056,9 +1059,10 @@
                 return;
             }
             activity.pendingVoiceInteractionStart = true;
+            callingAttributionTag = activity.launchedFromFeatureId;
         }
         LocalServices.getService(VoiceInteractionManagerInternal.class)
-                .startLocalVoiceInteraction(callingActivity, options);
+                .startLocalVoiceInteraction(callingActivity, callingAttributionTag, options);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6de1b95..8014f82 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2962,7 +2962,7 @@
 
     @Override
     public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
-            String callingPackageName) {
+            String callingPackageName, @Nullable String callingAttributionTag) {
         mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
                 "requestAssistDataForTask()");
         final long callingId = Binder.clearCallingIdentity();
@@ -2989,7 +2989,7 @@
         requester.requestAssistData(topActivityToken, true /* fetchData */,
                 false /* fetchScreenshot */, false /* fetchStructure */, true /* allowFetchData */,
                 false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
-                Binder.getCallingUid(), callingPackageName);
+                Binder.getCallingUid(), callingPackageName, callingAttributionTag);
 
         return true;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 2e85897..06b4ad9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -87,6 +87,7 @@
 
     private static final int TEST_UID = 0;
     private static final String TEST_PACKAGE = "";
+    private static final String TEST_ATTRIBUTION_TAG = "";
 
     private AssistDataRequester mDataRequester;
     private Callbacks mCallbacks;
@@ -148,9 +149,9 @@
             boolean assistScreenshotAllowed) throws Exception {
         doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
         doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
-                .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+                .noteOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString(), any(), any());
         doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
-                .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+                .noteOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString(), any(), any());
     }
 
     @Test
@@ -160,7 +161,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(5, 5, 1, 1);
     }
 
@@ -170,7 +172,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 0, 0, 0);
     }
 
@@ -180,7 +183,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 1, 0, 1);
     }
 
@@ -191,7 +195,8 @@
 
         mCallbacks.mCanHandleReceivedData = false;
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertEquals(5, mDataRequester.getPendingDataCount());
         assertEquals(1, mDataRequester.getPendingScreenshotCount());
         mGate.countDown();
@@ -226,7 +231,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 0, 0, 1);
     }
 
@@ -236,7 +242,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         // Expect a single null data when the appops is denied
         assertReceivedDataCount(0, 1, 0, 1);
     }
@@ -249,7 +256,8 @@
                 anyBoolean(), anyBoolean());
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         // Expect a single null data when requestAssistContextExtras() fails
         assertReceivedDataCount(0, 1, 0, 1);
     }
@@ -261,7 +269,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(5, 5, 0, 0);
     }
 
@@ -272,7 +281,8 @@
                 !CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         // Expect a single null screenshot when the appops is denied
         assertReceivedDataCount(5, 5, 0, 1);
     }
@@ -284,7 +294,8 @@
 
         mCallbacks.mCanHandleReceivedData = false;
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         mGate.countDown();
         waitForIdle(mHandler);
         assertThat(mCallbacks.mReceivedData).isEmpty();
@@ -297,7 +308,8 @@
                 CALLER_ASSIST_SCREENSHOT_ALLOWED);
 
         mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
-                !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+                !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+                TEST_ATTRIBUTION_TAG);
         assertReceivedDataCount(0, 1, 0, 1);
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7699262..e18c7ec 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -223,12 +223,13 @@
 
     class LocalService extends VoiceInteractionManagerInternal {
         @Override
-        public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+        public void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+                @Nullable String attributionTag, @NonNull Bundle options) {
             if (DEBUG) {
                 Slog.i(TAG, "startLocalVoiceInteraction " + callingActivity);
             }
             VoiceInteractionManagerService.this.mServiceStub.startLocalVoiceInteraction(
-                    callingActivity, options);
+                    callingActivity, attributionTag, options);
         }
 
         @Override
@@ -383,14 +384,15 @@
         }
 
         // TODO: VI Make sure the caller is the current user or profile
-        void startLocalVoiceInteraction(final IBinder token, Bundle options) {
+        void startLocalVoiceInteraction(@NonNull final IBinder token,
+                @Nullable String attributionTag, @NonNull Bundle options) {
             if (mImpl == null) return;
 
             final int callingUid = Binder.getCallingUid();
             final long caller = Binder.clearCallingIdentity();
             try {
                 mImpl.showSessionLocked(options,
-                        VoiceInteractionSession.SHOW_SOURCE_ACTIVITY,
+                        VoiceInteractionSession.SHOW_SOURCE_ACTIVITY, attributionTag,
                         new IVoiceInteractionSessionShowCallback.Stub() {
                             @Override
                             public void onFailed() {
@@ -898,13 +900,13 @@
         }
 
         @Override
-        public void showSession(Bundle args, int flags) {
+        public void showSession(@NonNull Bundle args, int flags, @Nullable String attributionTag) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.showSessionLocked(args, flags, null, null);
+                    mImpl.showSessionLocked(args, flags, attributionTag, null, null);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -929,7 +931,8 @@
         }
 
         @Override
-        public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) {
+        public boolean showSessionFromSession(@NonNull IBinder token, @NonNull Bundle sessionArgs,
+                int flags, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "showSessionFromSession without running voice interaction service");
@@ -937,7 +940,7 @@
                 }
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.showSessionLocked(sessionArgs, flags, null, null);
+                    return mImpl.showSessionLocked(sessionArgs, flags, attributionTag, null, null);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -961,8 +964,8 @@
         }
 
         @Override
-        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
-                String callingFeatureId) {
+        public int startVoiceActivity(@NonNull IBinder token, @NonNull Intent intent,
+                @Nullable String resolvedType, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -980,8 +983,8 @@
                     } else {
                         Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity.");
                     }
-                    return mImpl.startVoiceActivityLocked(
-                            callingFeatureId, callingPid, callingUid, token, intent, resolvedType);
+                    return mImpl.startVoiceActivityLocked(attributionTag, callingPid, callingUid,
+                            token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -989,8 +992,8 @@
         }
 
         @Override
-        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
-                String callingFeatureId) {
+        public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
+                @Nullable String resolvedType, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1000,7 +1003,7 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
+                    return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
                             callingUid, token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
@@ -1669,8 +1672,10 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
         @Override
-        public boolean showSessionForActiveService(Bundle args, int sourceFlags,
-                IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+        public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+                @Nullable String attributionTag,
+                @Nullable IVoiceInteractionSessionShowCallback showCallback,
+                @Nullable IBinder activityToken) {
             if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()");
 
             synchronized (this) {
@@ -1691,7 +1696,7 @@
                             sourceFlags
                                     | VoiceInteractionSession.SHOW_WITH_ASSIST
                                     | VoiceInteractionSession.SHOW_WITH_SCREENSHOT,
-                            showCallback, activityToken);
+                            attributionTag, showCallback, activityToken);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b9793ca..9f66059 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -244,8 +244,10 @@
                 /* direct= */ true);
     }
 
-    public boolean showSessionLocked(Bundle args, int flags,
-            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+    public boolean showSessionLocked(@NonNull Bundle args, int flags,
+            @Nullable String attributionTag,
+            @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @Nullable IBinder activityToken) {
         if (mActiveSession == null) {
             mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
                     mSessionComponentName, mUser, mContext, this,
@@ -269,8 +271,8 @@
         } else {
             visibleActivities = allVisibleActivities;
         }
-        return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
-                visibleActivities);
+        return mActiveSession.showLocked(args, flags, attributionTag, mDisabledShowContext,
+                showCallback, visibleActivities);
     }
 
     public void getActiveServiceSupportedActions(List<String> commands,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
index 9bdf4e4..80f5dc5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -114,8 +114,8 @@
 
         try {
             Bundle args = new Bundle();
-            boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0, callback,
-                    /* activityToken= */ null);
+            boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0,
+                     /* attributionTag= */ null, callback, /* activityToken= */ null);
 
             if (!ok) {
                 pw.println("showSessionForActiveService() returned false");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 093e976..63781cc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -29,6 +29,8 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
@@ -263,9 +265,9 @@
         return flags;
     }
 
-    public boolean showLocked(Bundle args, int flags, int disabledContext,
-            IVoiceInteractionSessionShowCallback showCallback,
-            List<ActivityAssistInfo> topActivities) {
+    public boolean showLocked(@NonNull Bundle args, int flags, @Nullable String attributionTag,
+            int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback,
+            @NonNull List<ActivityAssistInfo> topActivities) {
         if (mBound) {
             if (!mFullyBound) {
                 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -291,12 +293,15 @@
                 for (int i = 0; i < topActivitiesCount; i++) {
                     topActivitiesToken.add(topActivities.get(i).getActivityToken());
                 }
+
                 mAssistDataRequester.requestAssistData(topActivitiesToken,
                         fetchData,
                         fetchScreenshot,
                         (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
                         (disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
-                        mCallingUid, mSessionComponentName.getPackageName());
+                        mCallingUid,
+                        mSessionComponentName.getPackageName(),
+                        attributionTag);
 
                 boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
                         || mAssistDataRequester.getPendingScreenshotCount() > 0;