Add attribution context for MusicRecognitionManager attribution.

In addition:
  1) Use attribution tag specified in the MusicRecognitionService's manifest.
  2) Explicitly check for RECORD_AUDIO permission of receiving service.

Example audio attribution from dumpsys:
  RECORD_AUDIO (allow):
    MusicRecognitionDoneByServiceX=[
      Access: [fg-tpd] 2021-02-23 18:21:01.753 (-14s877ms) duration=+8s250ms proxy[uid=1000, pkg=android, attributionTag=MusicRecognitionManagerService]
    ]

Test: atest and manual inspection of attributions with dumpsys.
Change-Id: Ie415104580a1814b0b74f2d5b489bf8247d9238d
Bug: 178174412
Bug: 169403302
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8e1da08..c2f824f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5592,6 +5592,10 @@
     <!-- Attribution for Gnss Time Update service. -->
     <attribution android:tag="GnssTimeUpdateService"
                  android:label="@string/gnss_time_update_service"/>
+    <!-- Attribution for MusicRecognitionManagerService.
+         <p>Not for use by third-party applications.</p> -->
+    <attribution android:tag="MusicRecognitionManagerService"
+        android:label="@string/music_recognition_manager_service"/>
 
     <application android:process="system"
                  android:persistent="true"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2b1168f..dbb9fe5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -453,6 +453,9 @@
     <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]-->
     <string name="gnss_time_update_service">GNSS Time Update Service</string>
 
+    <!-- Attribution for MusicRecognitionManagerService. [CHAR LIMIT=NONE]-->
+    <string name="music_recognition_manager_service">Music Recognition Manager Service</string>
+
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
     <string name="factory_reset_warning">Your device will be erased</string>
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl
new file mode 100644
index 0000000..ceef73c
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl
@@ -0,0 +1,10 @@
+package android.media.musicrecognition;
+
+/**
+ * Interface from {@link MusicRecognitionService} to system to pass attribution tag.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionAttributionTagCallback {
+  void onAttributionTag(in String attributionTag);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
index 26543ed..c970161a 100644
--- a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
@@ -4,6 +4,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.IBinder;
 import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback;
 
 /**
  * Interface from the system to a {@link MusicRecognitionService}.
@@ -15,4 +16,6 @@
       in ParcelFileDescriptor fd,
       in AudioFormat audioFormat,
       in IMusicRecognitionServiceCallback callback);
+
+  void getAttributionTag(in IMusicRecognitionAttributionTagCallback callback);
 }
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
index 15215c4..10a6554 100644
--- a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
@@ -4,7 +4,7 @@
 import android.media.MediaMetadata;
 
 /**
- * Interface from a {@MusicRecognitionService} the system.
+ * Interface from a {@MusicRecognitionService} to the system.
  *
  * @hide
  */
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
index 04b4c39b..385aff0 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionService.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -90,7 +90,7 @@
                                             try {
                                                 callback.onRecognitionSucceeded(result, extras);
                                             } catch (RemoteException e) {
-                                                e.rethrowFromSystemServer();
+                                                throw e.rethrowFromSystemServer();
                                             }
                                         }
 
@@ -99,11 +99,18 @@
                                             try {
                                                 callback.onRecognitionFailed(failureCode);
                                             } catch (RemoteException e) {
-                                                e.rethrowFromSystemServer();
+                                                throw e.rethrowFromSystemServer();
                                             }
                                         }
                                     }));
                 }
+
+                @Override
+                public void getAttributionTag(
+                        IMusicRecognitionAttributionTagCallback callback) throws RemoteException {
+                    String tag = MusicRecognitionService.this.getAttributionTag();
+                    callback.onAttributionTag(tag);
+                }
             };
 
     @Override
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
index 87b2c84..4c5bbeb 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -48,6 +48,8 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
 
 /**
  * Handles per-user requests received by
@@ -60,6 +62,11 @@
         implements RemoteMusicRecognitionService.Callbacks {
 
     private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName();
+    private static final String MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG =
+            "MusicRecognitionManagerService";
+    private static final String KEY_MUSIC_RECOGNITION_SERVICE_ATTRIBUTION_TAG =
+            "android.media.musicrecognition.attributiontag";
+
     // Number of bytes per sample of audio (which is a short).
     private static final int BYTES_PER_SAMPLE = 2;
     private static final int MAX_STREAMING_SECONDS = 24;
@@ -68,18 +75,24 @@
     @GuardedBy("mLock")
     private RemoteMusicRecognitionService mRemoteService;
     private final AppOpsManager mAppOpsManager;
+    private final String mAttributionMessage;
 
-    private String mAttributionTag;
-    private String mAttributionMessage;
+    // Service info of the remote MusicRecognitionService (which the audio gets forwarded to).
     private ServiceInfo mServiceInfo;
+    private CompletableFuture<String> mAttributionTagFuture;
 
     MusicRecognitionManagerPerUserService(
             @NonNull MusicRecognitionManagerService primary,
             @NonNull Object lock, int userId) {
         super(primary, lock, userId);
-        mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
+
+        // When attributing audio-access, this establishes that audio access is performed by
+        // MusicRecognitionManager (on behalf of the receiving service, whose attribution tag,
+        // provided by mAttributionTagFuture, is used for the actual calls to startProxyOp(...).
+        mAppOpsManager = getContext().createAttributionContext(
+            MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG).getSystemService(AppOpsManager.class);
         mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId);
-        mAttributionTag = null;
+        mAttributionTagFuture = null;
         mServiceInfo = null;
     }
 
@@ -126,10 +139,13 @@
                     new MusicRecognitionServiceCallback(clientCallback),
                     mMaster.isBindInstantServiceAllowed(),
                     mMaster.verbose);
+
             try {
                 mServiceInfo =
                         getContext().getPackageManager().getServiceInfo(
-                                mRemoteService.getComponentName(), 0);
+                                mRemoteService.getComponentName(), PackageManager.GET_META_DATA);
+                mAttributionTagFuture = mRemoteService.getAttributionTag();
+                Slog.i(TAG, "Remote service bound: " + mRemoteService.getComponentName());
             } catch (PackageManager.NameNotFoundException e) {
                 Slog.e(TAG, "Service was not found.", e);
             }
@@ -172,11 +188,13 @@
         ParcelFileDescriptor audioSink = clientPipe.second;
         ParcelFileDescriptor clientRead = clientPipe.first;
 
-        mMaster.mExecutorService.execute(() -> {
-            streamAudio(recognitionRequest, clientCallback, audioSink);
-        });
+        mAttributionTagFuture.thenAcceptAsync(
+                tag -> {
+                    streamAudio(tag, recognitionRequest, clientCallback, audioSink);
+                }, mMaster.mExecutorService);
+
         // Send the pipe down to the lookup service while we write to it asynchronously.
-        mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat());
+        mRemoteService.onAudioStreamStarted(clientRead, recognitionRequest.getAudioFormat());
     }
 
     /**
@@ -186,10 +204,12 @@
      * @param clientCallback the callback to notify on errors.
      * @param audioSink the sink to which to stream audio to.
      */
-    private void streamAudio(@NonNull RecognitionRequest recognitionRequest,
-            IMusicRecognitionManagerCallback clientCallback, ParcelFileDescriptor audioSink) {
+    private void streamAudio(@Nullable String attributionTag,
+            @NonNull RecognitionRequest recognitionRequest,
+            IMusicRecognitionManagerCallback clientCallback,
+            ParcelFileDescriptor audioSink) {
         try {
-            startRecordAudioOp();
+            startRecordAudioOp(attributionTag);
         } catch (SecurityException e) {
             // A security exception can occur if the MusicRecognitionService (receiving the audio)
             // does not (or does no longer) hold the necessary permissions to record audio.
@@ -214,7 +234,7 @@
             Slog.e(TAG, "Audio streaming stopped.", e);
         } finally {
             audioRecord.release();
-            finishRecordAudioOp();
+            finishRecordAudioOp(attributionTag);
             try {
                 clientCallback.onAudioStreamClosed();
             } catch (RemoteException ignored) {
@@ -323,23 +343,32 @@
      * Tracks that the RECORD_AUDIO operation started (attributes it to the service receiving the
      * audio).
      */
-    private void startRecordAudioOp() {
-        mAppOpsManager.startProxyOp(
+    private void startRecordAudioOp(@Nullable String attributionTag) {
+        int status = mAppOpsManager.startProxyOp(
                 Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
                 mServiceInfo.applicationInfo.uid,
                 mServiceInfo.packageName,
-                mAttributionTag,
+                attributionTag,
                 mAttributionMessage);
+        // The above should already throw a SecurityException. This is just a fallback.
+        if (status != AppOpsManager.MODE_ALLOWED) {
+            throw new SecurityException(String.format(
+                    "Failed to obtain RECORD_AUDIO permission (status: %d) for "
+                    + "receiving service: %s", status, mServiceInfo.getComponentName()));
+        }
+        Slog.i(TAG, String.format(
+                "Starting audio streaming. Attributing to %s (%d) with tag '%s'",
+                mServiceInfo.packageName, mServiceInfo.applicationInfo.uid, attributionTag));
     }
 
 
     /** Tracks that the RECORD_AUDIO operation finished. */
-    private void finishRecordAudioOp() {
+    private void finishRecordAudioOp(@Nullable String attributionTag) {
         mAppOpsManager.finishProxyOp(
                 Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
                 mServiceInfo.applicationInfo.uid,
                 mServiceInfo.packageName,
-                mAttributionTag);
+                attributionTag);
     }
 
     /** Establishes an audio stream from the DSP audio source. */
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
index 6c7d673..99b4482 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
@@ -20,15 +20,20 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioFormat;
+import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback;
 import android.media.musicrecognition.IMusicRecognitionService;
 import android.media.musicrecognition.MusicRecognitionService;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.text.format.DateUtils;
 
 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
 import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback;
 
+import java.util.concurrent.CompletableFuture;
+
+
 /** Remote connection to an instance of {@link MusicRecognitionService}. */
 public class RemoteMusicRecognitionService extends
         AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService,
@@ -81,9 +86,26 @@
      * Sends the given descriptor to the app's {@link MusicRecognitionService} to read the
      * audio.
      */
-    public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd,
+    public void onAudioStreamStarted(@NonNull ParcelFileDescriptor fd,
             @NonNull AudioFormat audioFormat) {
         scheduleAsyncRequest(
                 binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback));
     }
+
+
+    /**
+     * Returns the name of the <attribution> tag defined in the remote service's manifest.
+     */
+    public CompletableFuture<String> getAttributionTag() {
+        CompletableFuture<String> attributionTagFuture = new CompletableFuture<String>();
+        scheduleAsyncRequest(
+                binder -> binder.getAttributionTag(
+                    new IMusicRecognitionAttributionTagCallback.Stub() {
+                        @Override
+                        public void onAttributionTag(String tag) throws RemoteException {
+                            attributionTagFuture.complete(tag);
+                        }
+                    }));
+        return attributionTagFuture;
+    }
 }