summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/utils/ServiceUtilities.cpp143
-rw-r--r--media/utils/include/mediautils/ServiceUtilities.h4
-rw-r--r--services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp40
-rw-r--r--services/audiopolicy/service/AudioPolicyService.cpp10
-rw-r--r--services/audiopolicy/service/AudioRecordClient.cpp26
-rw-r--r--services/audiopolicy/service/AudioRecordClient.h4
6 files changed, 159 insertions, 68 deletions
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index 4ee45c795e..92385f3203 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -40,6 +40,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 @@ static String16 resolveCallingPackage(PermissionController& permissionController
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) {
AttributionSourceState nextAttributionSource = callerAttributionSource;
@@ -113,7 +132,7 @@ std::optional<AttributionSourceState> resolveAttributionSource(
return std::optional<AttributionSourceState>{myAttributionSource};
}
-static bool checkRecordingInternal(const AttributionSourceState& attributionSource,
+static int checkRecordingInternal(const AttributionSourceState& attributionSource,
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
// device is rooted security model is considered compromised.
@@ -121,39 +140,74 @@ static bool checkRecordingInternal(const AttributionSourceState& attributionSour
// 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.
- const std::optional<AttributionSourceState> resolvedAttributionSource =
- resolveAttributionSource(attributionSource);
- 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);
+ 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;
}
bool recordingAllowed(const AttributionSourceState& attributionSource, audio_source_t source) {
- return checkRecordingInternal(attributionSource, String16(), /*start*/ false, source);
+ return checkRecordingInternal(attributionSource, String16(), /*start*/ false, source) !=
+ PERMISSION_HARD_DENIED;
}
-bool startRecording(const AttributionSourceState& attributionSource, const String16& msg,
+int startRecording(const AttributionSourceState& attributionSource, const String16& msg,
audio_source_t source) {
return checkRecordingInternal(attributionSource, msg, /*start*/ true, source);
}
@@ -164,19 +218,32 @@ void finishRecording(const AttributionSourceState& attributionSource, audio_sour
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);
- 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);
+ 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 46a70ddad5..71341aae89 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -87,7 +87,7 @@ static inline bool isAudioServerOrMediaServerUid(uid_t uid) {
bool recordingAllowed(const AttributionSourceState& attributionSource,
audio_source_t source = AUDIO_SOURCE_DEFAULT);
-bool startRecording(const AttributionSourceState& attributionSource,
+int startRecording(const AttributionSourceState& attributionSource,
const String16& msg, audio_source_t source);
void finishRecording(const AttributionSourceState& attributionSource, audio_source_t source);
std::optional<AttributionSourceState> resolveAttributionSource(
@@ -114,6 +114,8 @@ void anonymizeBluetoothAddress(char *address);
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 3ba55f350a..5d4f6e78e4 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -62,6 +62,11 @@ using media::audio::common::AudioUsage;
using media::audio::common::AudioUuid;
using media::audio::common::Int;
+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,
AUDIO_USAGE_EMERGENCY,
@@ -782,13 +787,12 @@ Status AudioPolicyService::startInput(int32_t portIdAidl)
std::stringstream msg;
msg << "Audio recording on session " << client->session;
+ const auto permitted = isAudioServerOrMediaServerUid(client->attributionSource.uid) &&
+ startRecording(client->attributionSource, String16(msg.str().c_str()),
+ client->attributes.source);
+
// check calling permissions
- if (!isAudioServerOrMediaServerUid(client->attributionSource.uid)
- && !(startRecording(client->attributionSource, 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);
@@ -807,13 +811,17 @@ Status AudioPolicyService::startInput(int32_t portIdAidl)
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();
@@ -899,7 +907,9 @@ Status AudioPolicyService::startInput(int32_t portIdAidl)
client->active = false;
client->startTimeNs = 0;
updateUidStates_l();
- finishRecording(client->attributionSource, client->attributes.source);
+ if (!client->silenced) {
+ finishRecording(client->attributionSource, client->attributes.source);
+ }
}
return binderStatusFromStatusT(status);
@@ -928,7 +938,9 @@ Status AudioPolicyService::stopInput(int32_t portIdAidl)
updateUidStates_l();
// finish the recording app op
- finishRecording(client->attributionSource, client->attributes.source);
+ if (!client->silenced) {
+ finishRecording(client->attributionSource, 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 09089cd445..7854efa437 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -63,6 +63,10 @@ static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
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) \
@@ -1158,9 +1162,9 @@ void AudioPolicyService::setAppState_l(sp<AudioRecordClient> client, app_state_t
} else {
std::stringstream msg;
msg << "Audio recording un-silenced on session " << client->session;
- if (!startRecording(client->attributionSource, String16(msg.str().c_str()),
- client->attributes.source)) {
- silenced = true;
+ if (startRecording(client->attributionSource, 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 44cbd871f2..26f92370e9 100644
--- a/services/audiopolicy/service/AudioRecordClient.cpp
+++ b/services/audiopolicy/service/AudioRecordClient.cpp
@@ -19,6 +19,7 @@
#include "AudioRecordClient.h"
#include "AudioPolicyService.h"
#include "binder/AppOpsManager.h"
+#include "mediautils/ServiceUtilities.h"
#include <algorithm>
@@ -47,7 +48,7 @@ int getTargetSdkForPackageName(std::string_view packageName) {
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;
@@ -120,16 +121,19 @@ OpRecordAudioMonitor::createIfNeeded(
|| attributionSource.packageName.value().size() == 0) {
return nullptr;
}
- return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source), commandThread);
+ return new OpRecordAudioMonitor(attributionSource, getOpForSource(attr.source),
+ isRecordOpRequired(attr.source),
+ commandThread);
}
-OpRecordAudioMonitor::OpRecordAudioMonitor(
- const AttributionSourceState& attributionSource, int32_t appOp,
- wp<AudioPolicyService::AudioCommandThread> commandThread) :
- mHasOp(true), mAttributionSource(attributionSource), mAppOp(appOp),
- mCommandThread(commandThread)
-{
-}
+OpRecordAudioMonitor::OpRecordAudioMonitor(const AttributionSourceState& attributionSource,
+ int32_t appOp, bool shouldMonitorRecord,
+ wp<AudioPolicyService::AudioCommandThread> commandThread)
+ : mHasOp(true),
+ mAttributionSource(attributionSource),
+ mAppOp(appOp),
+ mShouldMonitorRecord(shouldMonitorRecord),
+ mCommandThread(commandThread) {}
OpRecordAudioMonitor::~OpRecordAudioMonitor()
{
@@ -160,7 +164,7 @@ void OpRecordAudioMonitor::onFirstRef()
});
};
reg(mAppOp);
- if (mAppOp != AppOpsManager::OP_RECORD_AUDIO) {
+ if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) {
reg(AppOpsManager::OP_RECORD_AUDIO);
}
}
@@ -186,7 +190,7 @@ void OpRecordAudioMonitor::checkOp(bool updateUidStates) {
});
};
bool hasIt = check(mAppOp);
- if (mAppOp != AppOpsManager::OP_RECORD_AUDIO) {
+ if (mAppOp != AppOpsManager::OP_RECORD_AUDIO && mShouldMonitorRecord) {
hasIt = hasIt && check(AppOpsManager::OP_RECORD_AUDIO);
}
// verbose logging only log when appOp changed
diff --git a/services/audiopolicy/service/AudioRecordClient.h b/services/audiopolicy/service/AudioRecordClient.h
index d3be31618c..1c733d9517 100644
--- a/services/audiopolicy/service/AudioRecordClient.h
+++ b/services/audiopolicy/service/AudioRecordClient.h
@@ -43,7 +43,8 @@ public:
private:
OpRecordAudioMonitor(const AttributionSourceState& attributionSource, int32_t appOp,
- wp<AudioPolicyService::AudioCommandThread> commandThread);
+ bool shouldMonitorRecord,
+ wp<AudioPolicyService::AudioCommandThread> commandThread);
void onFirstRef() override;
@@ -68,6 +69,7 @@ private:
std::atomic_bool mHasOp;
const AttributionSourceState mAttributionSource;
const int32_t mAppOp;
+ const bool mShouldMonitorRecord;
wp<AudioPolicyService::AudioCommandThread> mCommandThread;
};