audio policy: fix playback permission checks

Align playback permission checks implementation in getOutputforAttr() on
capture checks in getInputForAttr(): use an "output type" returned by
audio policy manager and check permission accordingly.
- Do not modify but reject request to play to call uplink when permission
is denied.
- Add missing permission check for injection to remote submix.
- Clarify permission check for record from remote submix by using the
uid/pid of client explicitly.

Bug:
Test: CTS test for AudioTrack and AudioREcord.
Test: GTS tests for dynamic policies and playback capture
Change-Id: Ic0eb3b9921a491e97c58fa8f637583059a54b3a2
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index bd73cde..92d8c83 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -69,6 +69,14 @@
         API_INPUT_TELEPHONY_RX, // used for capture from telephony RX path
     } input_type_t;
 
+    typedef enum {
+        API_OUTPUT_INVALID = -1,
+        API_OUTPUT_LEGACY  = 0,// e.g. audio playing to speaker
+        API_OUT_MIX_PLAYBACK,  // used for "remote submix" playback of audio from remote source
+                               // to local capture
+        API_OUTPUT_TELEPHONY_TX, // used for playback to telephony TX path
+    } output_type_t;
+
 public:
     virtual ~AudioPolicyInterface() {}
     //
@@ -115,7 +123,8 @@
                                         audio_output_flags_t *flags,
                                         audio_port_handle_t *selectedDeviceId,
                                         audio_port_handle_t *portId,
-                                        std::vector<audio_io_handle_t> *secondaryOutputs) = 0;
+                                        std::vector<audio_io_handle_t> *secondaryOutputs,
+                                        output_type_t *outputType) = 0;
     // indicates to the audio policy manager that the output starts being used by corresponding stream.
     virtual status_t startOutput(audio_port_handle_t portId) = 0;
     // indicates to the audio policy manager that the output stops being used by corresponding stream.
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 3ea69f1..355e4f0 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -945,7 +945,8 @@
         audio_output_flags_t *flags,
         audio_port_handle_t *selectedDeviceId,
         bool *isRequestedDeviceForExclusiveUse,
-        std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
+        std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs,
+        output_type_t *outputType)
 {
     DeviceVector outputDevices;
     const audio_port_handle_t requestedPortId = *selectedDeviceId;
@@ -953,6 +954,7 @@
     const sp<DeviceDescriptor> requestedDevice =
         mAvailableOutputDevices.getDeviceFromId(requestedPortId);
 
+    *outputType = API_OUTPUT_INVALID;
     status_t status = getAudioAttributes(resultAttr, attr, *stream);
     if (status != NO_ERROR) {
         return status;
@@ -991,7 +993,13 @@
                                                   mix->mDeviceAddress,
                                                   AUDIO_FORMAT_DEFAULT);
         *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
+
         ALOGV("getOutputForAttr() returns output %d", *output);
+        if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
+            *outputType = API_OUT_MIX_PLAYBACK;
+        } else {
+            *outputType = API_OUTPUT_LEGACY;
+        }
         return NO_ERROR;
     }
     // Virtual sources must always be dynamicaly or explicitly routed
@@ -1048,6 +1056,12 @@
 
     *selectedDeviceId = getFirstDeviceId(outputDevices);
 
+    if (outputDevices.onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_TELEPHONY_TX)) {
+        *outputType = API_OUTPUT_TELEPHONY_TX;
+    } else {
+        *outputType = API_OUTPUT_LEGACY;
+    }
+
     ALOGV("%s returns output %d selectedDeviceId %d", __func__, *output, *selectedDeviceId);
 
     return NO_ERROR;
@@ -1062,7 +1076,8 @@
                                               audio_output_flags_t *flags,
                                               audio_port_handle_t *selectedDeviceId,
                                               audio_port_handle_t *portId,
-                                              std::vector<audio_io_handle_t> *secondaryOutputs)
+                                              std::vector<audio_io_handle_t> *secondaryOutputs,
+                                              output_type_t *outputType)
 {
     // The supplied portId must be AUDIO_PORT_HANDLE_NONE
     if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1082,7 +1097,7 @@
 
     status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
             config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
-            &secondaryOutputDescs);
+            &secondaryOutputDescs, outputType);
     if (status != NO_ERROR) {
         return status;
     }
@@ -3909,10 +3924,11 @@
         audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
         bool isRequestedDeviceForExclusiveUse = false;
         std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
+        output_type_t outputType;
         getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE,
                 &attributes, &stream, sourceDesc->uid(), &config, &flags,
                 &selectedDeviceId, &isRequestedDeviceForExclusiveUse,
-                &secondaryOutputs);
+                &secondaryOutputs, &outputType);
         if (output == AUDIO_IO_HANDLE_NONE) {
             ALOGV("%s no output for device %s",
                   __FUNCTION__, dumpDeviceTypes(sinkDevices.types()).c_str());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 500b636..322c188 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -121,7 +121,8 @@
                                   audio_output_flags_t *flags,
                                   audio_port_handle_t *selectedDeviceId,
                                   audio_port_handle_t *portId,
-                                  std::vector<audio_io_handle_t> *secondaryOutputs) override;
+                                  std::vector<audio_io_handle_t> *secondaryOutputs,
+                                  output_type_t *outputType) override;
         virtual status_t startOutput(audio_port_handle_t portId);
         virtual status_t stopOutput(audio_port_handle_t portId);
         virtual void releaseOutput(audio_port_handle_t portId);
@@ -809,7 +810,8 @@
                 audio_output_flags_t *flags,
                 audio_port_handle_t *selectedDeviceId,
                 bool *isRequestedDeviceForExclusiveUse,
-                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
+                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs,
+                output_type_t *outputType);
         // internal method to return the output handle for the given device and format
         audio_io_handle_t getOutputForDevices(
                 const DeviceVector &devices,
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 10355bf..feb930e 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -181,13 +181,13 @@
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
-    ALOGV("getOutputForAttr()");
+    ALOGV("%s()", __func__);
     Mutex::Autolock _l(mLock);
 
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
     if (!isAudioServerOrMediaServerUid(callingUid) || uid == (uid_t)-1) {
         ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
-                "%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
+                "%s uid %d tried to pass itself off as %d", __func__, callingUid, uid);
         uid = callingUid;
     }
     if (!mPackageManager.allowPlaybackCapture(uid)) {
@@ -197,27 +197,39 @@
             && !bypassInterruptionPolicyAllowed(pid, uid)) {
         attr->flags &= ~(AUDIO_FLAG_BYPASS_INTERRUPTION_POLICY|AUDIO_FLAG_BYPASS_MUTE);
     }
-    audio_output_flags_t originalFlags = flags;
     AutoCallerClear acc;
+    AudioPolicyInterface::output_type_t outputType;
     status_t result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid,
                                                  config,
                                                  &flags, selectedDeviceId, portId,
-                                                 secondaryOutputs);
+                                                 secondaryOutputs,
+                                                 &outputType);
 
     // FIXME: Introduce a way to check for the the telephony device before opening the output
-    if ((result == NO_ERROR) &&
-        (flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) &&
-        !modifyPhoneStateAllowed(pid, uid)) {
-        // If the app tries to play music through the telephony device and doesn't have permission
-        // the fallback to the default output device.
-        mAudioPolicyManager->releaseOutput(*portId);
-        flags = originalFlags;
-        *selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
-        *portId = AUDIO_PORT_HANDLE_NONE;
-        secondaryOutputs->clear();
-        result = mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, uid, config,
-                                                       &flags, selectedDeviceId, portId,
-                                                       secondaryOutputs);
+    if (result == NO_ERROR) {
+        // enforce permission (if any) required for each type of input
+        switch (outputType) {
+        case AudioPolicyInterface::API_OUTPUT_LEGACY:
+            break;
+        case AudioPolicyInterface::API_OUTPUT_TELEPHONY_TX:
+            if (!modifyPhoneStateAllowed(pid, uid)) {
+                ALOGE("%s() permission denied: modify phone state not allowed for uid %d",
+                    __func__, uid);
+                result = PERMISSION_DENIED;
+            }
+            break;
+        case AudioPolicyInterface::API_OUT_MIX_PLAYBACK:
+            if (!modifyAudioRoutingAllowed(pid, uid)) {
+                ALOGE("%s() permission denied: modify audio routing not allowed for uid %d",
+                    __func__, uid);
+                result = PERMISSION_DENIED;
+            }
+            break;
+        case AudioPolicyInterface::API_OUTPUT_INVALID:
+        default:
+            LOG_ALWAYS_FATAL("%s() encountered an invalid output type %d",
+                __func__, (int)outputType);
+        }
     }
 
     if (result == NO_ERROR) {
@@ -434,7 +446,7 @@
                 }
                 break;
             case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
-                if (!modifyAudioRoutingAllowed()) {
+                if (!modifyAudioRoutingAllowed(pid, uid)) {
                     ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");
                     status = PERMISSION_DENIED;
                 }
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 1ee1eea..e4a19ea 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -179,9 +179,10 @@
     audio_port_handle_t localPortId;
     if (!portId) portId = &localPortId;
     *portId = AUDIO_PORT_HANDLE_NONE;
+    AudioPolicyInterface::output_type_t outputType;
     ASSERT_EQ(OK, mManager->getOutputForAttr(
                     &attr, output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
-                    selectedDeviceId, portId, {}));
+                    selectedDeviceId, portId, {}, &outputType));
     ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
     ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
 }