Merge cherrypicks of ['googleplex-android-review.googlesource.com/34305922', 'googleplex-android-review.googlesource.com/34305923'] into security-aosp-24Q3-release.
Change-Id: I088eb0df70683a3b392c76ea4112435f02f96818
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 7bec8cf..7072d5c 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -41,6 +41,11 @@
namespace android {
+namespace {
+constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED;
+constexpr auto PERMISSION_HARD_DENIED = permission::PermissionChecker::PERMISSION_HARD_DENIED;
+}
+
using content::AttributionSourceState;
static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
@@ -71,19 +76,33 @@
int32_t getOpForSource(audio_source_t source) {
switch (source) {
- case AUDIO_SOURCE_HOTWORD:
- return AppOpsManager::OP_RECORD_AUDIO_HOTWORD;
+ case AUDIO_SOURCE_FM_TUNER:
+ return AppOpsManager::OP_NONE;
case AUDIO_SOURCE_ECHO_REFERENCE: // fallthrough
case AUDIO_SOURCE_REMOTE_SUBMIX:
return AppOpsManager::OP_RECORD_AUDIO_OUTPUT;
case AUDIO_SOURCE_VOICE_DOWNLINK:
return AppOpsManager::OP_RECORD_INCOMING_PHONE_AUDIO;
+ case AUDIO_SOURCE_HOTWORD:
+ return AppOpsManager::OP_RECORD_AUDIO_HOTWORD;
case AUDIO_SOURCE_DEFAULT:
default:
return AppOpsManager::OP_RECORD_AUDIO;
}
}
+bool isRecordOpRequired(audio_source_t source) {
+ switch (source) {
+ case AUDIO_SOURCE_FM_TUNER:
+ case AUDIO_SOURCE_ECHO_REFERENCE: // fallthrough
+ case AUDIO_SOURCE_REMOTE_SUBMIX:
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ return false;
+ default:
+ return true;
+ }
+}
+
std::optional<AttributionSourceState> resolveAttributionSource(
const AttributionSourceState& callerAttributionSource, const uint32_t virtualDeviceId) {
AttributionSourceState nextAttributionSource = callerAttributionSource;
@@ -115,7 +134,8 @@
return std::optional<AttributionSourceState>{myAttributionSource};
}
- static bool checkRecordingInternal(const AttributionSourceState &attributionSource,
+
+static int checkRecordingInternal(const AttributionSourceState &attributionSource,
const uint32_t virtualDeviceId,
const String16 &msg, bool start, audio_source_t source) {
// Okay to not track in app ops as audio server or media server is us and if
@@ -124,49 +144,83 @@
// user is active, but it is a core system service so let it through.
// TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
- if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return true;
-
- // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
- // may open a record track on behalf of a client. Note that pid may be a tid.
- // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
- std::optional<AttributionSourceState> resolvedAttributionSource =
- resolveAttributionSource(attributionSource, virtualDeviceId);
- if (!resolvedAttributionSource.has_value()) {
- return false;
- }
+ if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return PERMISSION_GRANTED;
const int32_t attributedOpCode = getOpForSource(source);
+ if (isRecordOpRequired(source)) {
+ // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
+ // may open a record track on behalf of a client. Note that pid may be a tid.
+ // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
+ std::optional<AttributionSourceState> resolvedAttributionSource =
+ resolveAttributionSource(attributionSource, virtualDeviceId);
+ if (!resolvedAttributionSource.has_value()) {
+ return PERMISSION_HARD_DENIED;
+ }
- permission::PermissionChecker permissionChecker;
- bool permitted = false;
- if (start) {
- permitted = (permissionChecker.checkPermissionForStartDataDeliveryFromDatasource(
- sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
- attributedOpCode) != permission::PermissionChecker::PERMISSION_HARD_DENIED);
+ permission::PermissionChecker permissionChecker;
+ int permitted;
+ if (start) {
+ // Do a double-check, where we first check without actually starting in order to handle
+ // the behavior of AppOps where ops are sometimes started but paused for SOFT_DENIED.
+ // Since there is no way to maintain reference consensus due to this behavior, avoid
+ // starting an op when a restriction is in place by first checking. In the case where we
+ // startOp would fail, call a noteOp (which will also fail) instead. This preserves
+ // behavior that is reliant on listening to op rejected events (such as the hint
+ // dialogue to unmute the microphone). Technically racy, but very unlikely.
+ //
+ // TODO(b/294609684) To be removed when the pause state for an OP is removed.
+ permitted = permissionChecker.checkPermissionForPreflightFromDatasource(
+ sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
+ attributedOpCode);
+ if (permitted == PERMISSION_GRANTED) {
+ permitted = permissionChecker.checkPermissionForStartDataDeliveryFromDatasource(
+ sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
+ attributedOpCode);
+ } else {
+ // intentionally don't set permitted
+ permissionChecker.checkPermissionForDataDeliveryFromDatasource(
+ sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
+ attributedOpCode);
+ }
+ } else {
+ permitted = permissionChecker.checkPermissionForPreflightFromDatasource(
+ sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
+ attributedOpCode);
+ }
+
+ return permitted;
} else {
- permitted = (permissionChecker.checkPermissionForPreflightFromDatasource(
- sAndroidPermissionRecordAudio, resolvedAttributionSource.value(), msg,
- attributedOpCode) != permission::PermissionChecker::PERMISSION_HARD_DENIED);
+ if (attributedOpCode == AppOpsManager::OP_NONE) return PERMISSION_GRANTED; // nothing to do
+ AppOpsManager ap{};
+ PermissionController pc{};
+ return ap.startOpNoThrow(
+ attributedOpCode, attributionSource.uid,
+ resolveCallingPackage(pc,
+ String16{attributionSource.packageName.value_or("").c_str()},
+ attributionSource.uid),
+ false,
+ attributionSource.attributionTag.has_value()
+ ? String16{attributionSource.attributionTag.value().c_str()}
+ : String16{},
+ msg);
}
-
- return permitted;
}
static constexpr int DEVICE_ID_DEFAULT = 0;
bool recordingAllowed(const AttributionSourceState &attributionSource, audio_source_t source) {
return checkRecordingInternal(attributionSource, DEVICE_ID_DEFAULT, String16(), /*start*/ false,
- source);
+ source) != PERMISSION_HARD_DENIED;
}
bool recordingAllowed(const AttributionSourceState &attributionSource,
const uint32_t virtualDeviceId,
audio_source_t source) {
return checkRecordingInternal(attributionSource, virtualDeviceId,
- String16(), /*start*/ false, source);
+ String16(), /*start*/ false, source) != PERMISSION_HARD_DENIED;
}
-bool startRecording(const AttributionSourceState& attributionSource,
+int startRecording(const AttributionSourceState& attributionSource,
const uint32_t virtualDeviceId,
const String16& msg,
audio_source_t source) {
@@ -181,19 +235,32 @@
uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid));
if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return;
- // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
- // may open a record track on behalf of a client. Note that pid may be a tid.
- // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
- const std::optional<AttributionSourceState> resolvedAttributionSource =
- resolveAttributionSource(attributionSource, virtualDeviceId);
- if (!resolvedAttributionSource.has_value()) {
- return;
- }
-
const int32_t attributedOpCode = getOpForSource(source);
- permission::PermissionChecker permissionChecker;
- permissionChecker.finishDataDeliveryFromDatasource(attributedOpCode,
- resolvedAttributionSource.value());
+ if (isRecordOpRequired(source)) {
+ // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
+ // may open a record track on behalf of a client. Note that pid may be a tid.
+ // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
+ const std::optional<AttributionSourceState> resolvedAttributionSource =
+ resolveAttributionSource(attributionSource, virtualDeviceId);
+ if (!resolvedAttributionSource.has_value()) {
+ return;
+ }
+
+ permission::PermissionChecker permissionChecker;
+ permissionChecker.finishDataDeliveryFromDatasource(attributedOpCode,
+ resolvedAttributionSource.value());
+ } else {
+ if (attributedOpCode == AppOpsManager::OP_NONE) return; // nothing to do
+ AppOpsManager ap{};
+ PermissionController pc{};
+ ap.finishOp(attributedOpCode, attributionSource.uid,
+ resolveCallingPackage(
+ pc, String16{attributionSource.packageName.value_or("").c_str()},
+ attributionSource.uid),
+ attributionSource.attributionTag.has_value()
+ ? String16{attributionSource.attributionTag.value().c_str()}
+ : String16{});
+ }
}
bool captureAudioOutputAllowed(const AttributionSourceState& attributionSource) {
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index e0fabfd..bc53f1b 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -91,7 +91,7 @@
bool recordingAllowed(const AttributionSourceState &attributionSource,
uint32_t virtualDeviceId,
audio_source_t source);
-bool startRecording(const AttributionSourceState& attributionSource, uint32_t virtualDeviceId,
+int startRecording(const AttributionSourceState& attributionSource, uint32_t virtualDeviceId,
const String16& msg, audio_source_t source);
void finishRecording(const AttributionSourceState& attributionSource, uint32_t virtualDeviceId,
audio_source_t source);
@@ -115,6 +115,8 @@
void purgePermissionCache();
int32_t getOpForSource(audio_source_t source);
+bool isRecordOpRequired(audio_source_t source);
+
AttributionSourceState getCallingAttributionSource();
status_t checkIMemory(const sp<IMemory>& iMemory);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 768cd07..b7aec7e 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -67,6 +67,10 @@
using media::audio::common::Int;
constexpr int kDefaultVirtualDeviceId = 0;
+namespace {
+constexpr auto PERMISSION_HARD_DENIED = permission::PermissionChecker::PERMISSION_HARD_DENIED;
+constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED;
+}
const std::vector<audio_usage_t>& SYSTEM_USAGES = {
AUDIO_USAGE_CALL_ASSISTANT,
@@ -824,12 +828,11 @@
std::stringstream msg;
msg << "Audio recording on session " << client->session;
+ const auto permitted = startRecording(client->attributionSource, client->virtualDeviceId,
+ String16(msg.str().c_str()), client->attributes.source);
+
// check calling permissions
- if (!(startRecording(client->attributionSource, client->virtualDeviceId,
- String16(msg.str().c_str()), client->attributes.source)
- || client->attributes.source == AUDIO_SOURCE_FM_TUNER
- || client->attributes.source == AUDIO_SOURCE_REMOTE_SUBMIX
- || client->attributes.source == AUDIO_SOURCE_ECHO_REFERENCE)) {
+ if (permitted == PERMISSION_HARD_DENIED) {
ALOGE("%s permission denied: recording not allowed for attribution source %s",
__func__, client->attributionSource.toString().c_str());
return binderStatusFromStatusT(PERMISSION_DENIED);
@@ -849,13 +852,17 @@
return binderStatusFromStatusT(INVALID_OPERATION);
}
- // Force the possibly silenced client to be unsilenced since we just called
- // startRecording (i.e. we have assumed it is unsilenced).
- // At this point in time, the client is inactive, so no calls to appops are sent in
- // setAppState_l.
- // This ensures existing clients have the same behavior as new clients (starting unsilenced).
+ // Force the possibly silenced client to match the state on the appops side
+ // following the call to startRecording (i.e. unsilenced iff call succeeded)
+ // At this point in time, the client is inactive, so no calls to appops are
+ // sent in setAppState_l. This ensures existing clients have the same
+ // behavior as new clients.
// TODO(b/282076713)
- setAppState_l(client, APP_STATE_TOP);
+ if (permitted == PERMISSION_GRANTED) {
+ setAppState_l(client, APP_STATE_TOP);
+ } else {
+ setAppState_l(client, APP_STATE_IDLE);
+ }
client->active = true;
client->startTimeNs = systemTime();
@@ -941,8 +948,10 @@
client->active = false;
client->startTimeNs = 0;
updateUidStates_l();
- finishRecording(client->attributionSource, client->virtualDeviceId,
- client->attributes.source);
+ if (!client->silenced) {
+ finishRecording(client->attributionSource, client->virtualDeviceId,
+ client->attributes.source);
+ }
}
return binderStatusFromStatusT(status);
@@ -971,7 +980,11 @@
updateUidStates_l();
// finish the recording app op
- finishRecording(client->attributionSource, client->virtualDeviceId, client->attributes.source);
+ if (!client->silenced) {
+ finishRecording(client->attributionSource, client->virtualDeviceId,
+ client->attributes.source);
+ }
+
AutoCallerClear acc;
return binderStatusFromStatusT(mAudioPolicyManager->stopInput(portId));
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index cc67481..e511c85 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -61,6 +61,10 @@
static const String16 sManageAudioPolicyPermission("android.permission.MANAGE_AUDIO_POLICY");
+namespace {
+constexpr auto PERMISSION_GRANTED = permission::PermissionChecker::PERMISSION_GRANTED;
+}
+
// Creates an association between Binder code to name for IAudioPolicyService.
#define IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST \
BINDER_METHOD_ENTRY(onNewAudioModulesAvailable) \
@@ -1217,9 +1221,10 @@
} else {
std::stringstream msg;
msg << "Audio recording un-silenced on session " << client->session;
- if (!startRecording(client->attributionSource, client->virtualDeviceId,
- String16(msg.str().c_str()), client->attributes.source)) {
- silenced = true;
+ if (startRecording(client->attributionSource, client->virtualDeviceId,
+ String16(msg.str().c_str()), client->attributes.source)
+ != PERMISSION_GRANTED) {
+ return;
}
}
}
diff --git a/services/audiopolicy/service/AudioRecordClient.cpp b/services/audiopolicy/service/AudioRecordClient.cpp
index 6d8b3cf..3a64d08 100644
--- a/services/audiopolicy/service/AudioRecordClient.cpp
+++ b/services/audiopolicy/service/AudioRecordClient.cpp
@@ -18,15 +18,18 @@
#include "AudioRecordClient.h"
#include "AudioPolicyService.h"
+#include "binder/AppOpsManager.h"
+#include "mediautils/ServiceUtilities.h"
#include <android_media_audiopolicy.h>
+#include <algorithm>
+
namespace android::media::audiopolicy {
namespace audiopolicy_flags = android::media::audiopolicy;
using android::AudioPolicyService;
namespace {
-bool isAppOpSource(audio_source_t source)
-{
+bool isAppOpSource(audio_source_t source) {
switch (source) {
case AUDIO_SOURCE_FM_TUNER:
case AUDIO_SOURCE_ECHO_REFERENCE:
@@ -46,7 +49,7 @@
if (pm != nullptr) {
const auto status = pm->getTargetSdkVersionForPackage(
String16{packageName.data(), packageName.size()}, &targetSdk);
- return status.isOk() ? targetSdk : -1;
+ return status.isOk() ? targetSdk : __ANDROID_API_FUTURE__;
}
}
return targetSdk;
@@ -55,7 +58,40 @@
bool doesPackageTargetAtLeastU(std::string_view packageName) {
return getTargetSdkForPackageName(packageName) >= __ANDROID_API_U__;
}
-}
+
+class AttrSourceItr {
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = AttributionSourceState;
+ using pointer = const value_type*;
+ using reference = const value_type&;
+
+ AttrSourceItr() : mAttr(nullptr) {}
+
+ AttrSourceItr(const AttributionSourceState& attr) : mAttr(&attr) {}
+
+ reference operator*() const { return *mAttr; }
+ pointer operator->() const { return mAttr; }
+
+ AttrSourceItr& operator++() {
+ mAttr = !mAttr->next.empty() ? mAttr->next.data() : nullptr;
+ return *this;
+ }
+
+ AttrSourceItr operator++(int) {
+ AttrSourceItr tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ friend bool operator==(const AttrSourceItr& a, const AttrSourceItr& b) = default;
+
+ static AttrSourceItr end() { return AttrSourceItr{}; }
+private:
+ const AttributionSourceState * mAttr;
+};
+} // anonymous
// static
sp<OpRecordAudioMonitor>
@@ -83,18 +119,23 @@
}
return new OpRecordAudioMonitor(attributionSource, virtualDeviceId, attr,
- getOpForSource(attr.source), commandThread);
+ getOpForSource(attr.source),
+ isRecordOpRequired(attr.source),
+ commandThread);
}
-OpRecordAudioMonitor::OpRecordAudioMonitor(
- const AttributionSourceState &attributionSource,
- const uint32_t virtualDeviceId, const audio_attributes_t &attr,
- int32_t appOp,
- wp<AudioPolicyService::AudioCommandThread> commandThread) :
- mHasOp(true), mAttributionSource(attributionSource),
- mVirtualDeviceId(virtualDeviceId), mAttr(attr), mAppOp(appOp),
- mCommandThread(commandThread) {
-}
+OpRecordAudioMonitor::OpRecordAudioMonitor(const AttributionSourceState& attributionSource,
+ const uint32_t virtualDeviceId,
+ const audio_attributes_t& attr, int32_t appOp,
+ bool shouldMonitorRecord,
+ wp<AudioPolicyService::AudioCommandThread> commandThread)
+ : mHasOp(true),
+ mAttributionSource(attributionSource),
+ mVirtualDeviceId(virtualDeviceId),
+ mAttr(attr),
+ mAppOp(appOp),
+ mShouldMonitorRecord(shouldMonitorRecord),
+ mCommandThread(commandThread) {}
OpRecordAudioMonitor::~OpRecordAudioMonitor()
{
@@ -110,15 +151,24 @@
mOpCallback = new RecordAudioOpCallback(this);
ALOGV("start watching op %d for %s", mAppOp, mAttributionSource.toString().c_str());
- int flags = doesPackageTargetAtLeastU(
- mAttributionSource.packageName.value_or("")) ?
- AppOpsManager::WATCH_FOREGROUND_CHANGES : 0;
- // TODO: We need to always watch AppOpsManager::OP_RECORD_AUDIO too
- // since it controls the mic permission for legacy apps.
- mAppOpsManager.startWatchingMode(mAppOp, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
- mAttributionSource.packageName.value_or(""))),
- flags,
- mOpCallback);
+ int flags = doesPackageTargetAtLeastU(mAttributionSource.packageName.value_or(""))
+ ? AppOpsManager::WATCH_FOREGROUND_CHANGES
+ : 0;
+
+ const auto reg = [&](int32_t op) {
+ std::for_each(AttrSourceItr{mAttributionSource}, AttrSourceItr::end(),
+ [&](const auto& attr) {
+ mAppOpsManager.startWatchingMode(
+ op,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+ attr.packageName.value_or(""))),
+ flags, mOpCallback);
+ });
+ };
+ reg(mAppOp);
+ if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) {
+ reg(AppOpsManager::OP_RECORD_AUDIO);
+ }
}
bool OpRecordAudioMonitor::hasOp() const {
@@ -131,14 +181,20 @@
// due to the UID in createIfNeeded(). As a result for those record track, it's:
// - not called from constructor,
// - not called from RecordAudioOpCallback because the callback is not installed in this case
-void OpRecordAudioMonitor::checkOp(bool updateUidStates)
-{
- // TODO: We need to always check AppOpsManager::OP_RECORD_AUDIO too
- // since it controls the mic permission for legacy apps.
- const int32_t mode = mAppOpsManager.checkOp(mAppOp,
- mAttributionSource.uid, VALUE_OR_FATAL(aidl2legacy_string_view_String16(
- mAttributionSource.packageName.value_or(""))));
- bool hasIt = (mode == AppOpsManager::MODE_ALLOWED);
+void OpRecordAudioMonitor::checkOp(bool updateUidStates) {
+ const auto check = [&](int32_t op) -> bool {
+ return std::all_of(
+ AttrSourceItr{mAttributionSource}, AttrSourceItr::end(), [&](const auto& x) {
+ return mAppOpsManager.checkOp(op, x.uid,
+ VALUE_OR_FATAL(aidl2legacy_string_view_String16(
+ x.packageName.value_or("")))) ==
+ AppOpsManager::MODE_ALLOWED;
+ });
+ };
+ bool hasIt = check(mAppOp);
+ if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) {
+ hasIt = hasIt && check(AppOpsManager::OP_RECORD_AUDIO);
+ }
if (audiopolicy_flags::record_audio_device_aware_permission()) {
const bool canRecord = recordingAllowed(mAttributionSource, mVirtualDeviceId, mAttr.source);
@@ -173,4 +229,4 @@
}
}
-} // android::media::audiopolicy::internal
+} // namespace android::media::audiopolicy
diff --git a/services/audiopolicy/service/AudioRecordClient.h b/services/audiopolicy/service/AudioRecordClient.h
index 76aff41..977d77b 100644
--- a/services/audiopolicy/service/AudioRecordClient.h
+++ b/services/audiopolicy/service/AudioRecordClient.h
@@ -47,6 +47,7 @@
uint32_t virtualDeviceId,
const audio_attributes_t &attr,
int32_t appOp,
+ bool shouldMonitorRecord,
wp<AudioPolicyService::AudioCommandThread> commandThread);
void onFirstRef() override;
@@ -74,6 +75,7 @@
const uint32_t mVirtualDeviceId;
const audio_attributes_t mAttr;
const int32_t mAppOp;
+ const bool mShouldMonitorRecord;
wp<AudioPolicyService::AudioCommandThread> mCommandThread;
};