1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define LOG_TAG "UsecaseValidator"
17 // #define LOG_NDEBUG 0
18
19 #include <inttypes.h>
20
21 #include <utils/Log.h>
22
23 #include "media/UsecaseValidator.h"
24 #include "media/UsecaseLookup.h"
25
26 namespace android {
27 namespace media {
28 namespace {
29
30 class UsecaseValidatorImpl : public UsecaseValidator {
31 public:
UsecaseValidatorImpl()32 UsecaseValidatorImpl() {}
33
34 /**
35 * Register a new mixer/stream.
36 * Called when the stream is opened at the HAL and communicates
37 * immutable stream attributes like flags, sampling rate, format.
38 */
registerStream(audio_io_handle_t streamId,const audio_config_base_t & audioConfig,const audio_output_flags_t outputFlags)39 status_t registerStream(audio_io_handle_t streamId,
40 const audio_config_base_t& audioConfig __attribute__((unused)),
41 const audio_output_flags_t outputFlags) override {
42 ALOGV("%s output: %d flags: %#x", __func__, streamId, outputFlags);
43
44 // Check if FAST or MMAP output flag has been set.
45 bool outputFlagGame = outputFlags & (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ);
46 m_lookup.addStream(streamId, outputFlagGame);
47 return OK;
48 };
49
50 /**
51 * Unregister a stream/mixer.
52 * Called when the stream is closed.
53 */
unregisterStream(audio_io_handle_t streamId)54 status_t unregisterStream(audio_io_handle_t streamId) override {
55 ALOGV("%s output: %d", __func__, streamId);
56
57 m_lookup.removeStream(streamId);
58 return OK;
59 };
60
61 /**
62 * Indicates that some playback activity started on the stream.
63 * Called each time an audio track starts or resumes.
64 */
startClient(audio_io_handle_t streamId,audio_port_handle_t portId,const content::AttributionSourceState & attributionSource,const audio_attributes_t & attributes,const AttributesChangedCallback * callback)65 error::Result<audio_attributes_t> startClient(audio_io_handle_t streamId,
66 audio_port_handle_t portId, const content::AttributionSourceState& attributionSource,
67 const audio_attributes_t& attributes,
68 const AttributesChangedCallback *callback __attribute__((unused))) override {
69 ALOGV("%s output: %d portId: %d usage: %d pid: %d package: %s",
70 __func__, streamId, portId, attributes.usage, attributionSource.pid,
71 attributionSource.packageName.value_or("").c_str());
72
73 m_lookup.addTrack(streamId, portId);
74
75 return verifyAudioAttributes(streamId, attributionSource, attributes);
76 };
77
78 /**
79 * Indicates that some playback activity stopped on the stream.
80 * Called each time an audio track stops or pauses.
81 */
stopClient(audio_io_handle_t streamId,audio_port_handle_t portId)82 status_t stopClient(audio_io_handle_t streamId, audio_port_handle_t portId) override {
83 ALOGV("%s output: %d portId: %d", __func__, streamId, portId);
84
85 m_lookup.removeTrack(streamId, portId);
86 return OK;
87 };
88
89 /**
90 * Called to verify and update audio attributes for a track that is connected
91 * to the specified stream.
92 */
verifyAudioAttributes(audio_io_handle_t streamId,const content::AttributionSourceState & attributionSource,const audio_attributes_t & attributes)93 error::Result<audio_attributes_t> verifyAudioAttributes(audio_io_handle_t streamId,
94 const content::AttributionSourceState& attributionSource,
95 const audio_attributes_t& attributes) override {
96 ALOGV("%s output: %d usage: %d pid: %d package: %s",
97 __func__, streamId, attributes.usage, attributionSource.pid,
98 attributionSource.packageName.value_or("").c_str());
99
100 audio_attributes_t attrRet = attributes;
101
102 if (isUsageValid(attributes.usage) && isContentTypeValid(attributes.content_type)
103 && areFlagsValid(attributes.flags) && m_lookup.isGameStream(streamId)) {
104 ALOGI("%s update usage: %d to AUDIO_USAGE_GAME for output: %d pid: %d package: %s",
105 __func__, attributes.usage, streamId, attributionSource.pid,
106 attributionSource.packageName.value_or("").c_str());
107 // Set attribute usage Game.
108 attrRet.usage = AUDIO_USAGE_GAME;
109 }
110
111 return {attrRet};
112 };
113
114 protected:
115 /**
116 * Check if attribute usage valid.
117 */
isUsageValid(audio_usage_t usage)118 bool isUsageValid(audio_usage_t usage) {
119 ALOGV("isUsageValid usage: %d", usage);
120 switch (usage) {
121 case AUDIO_USAGE_MEDIA:
122 case AUDIO_USAGE_UNKNOWN:
123 return true;
124 default:
125 break;
126 }
127 return false;
128 }
129
isContentTypeValid(audio_content_type_t contentType)130 bool isContentTypeValid(audio_content_type_t contentType) {
131 ALOGV("isContentTypeValid contentType: %d", contentType);
132 switch (contentType) {
133 case AUDIO_CONTENT_TYPE_MUSIC:
134 case AUDIO_CONTENT_TYPE_MOVIE:
135 case AUDIO_CONTENT_TYPE_UNKNOWN:
136 return true;
137 default:
138 break;
139 }
140 return false;
141 }
142
areFlagsValid(audio_flags_mask_t flags)143 bool areFlagsValid(audio_flags_mask_t flags) {
144 ALOGV("areFlagsValid flags: %#x", flags);
145 if ((flags & (AUDIO_FLAG_SCO|AUDIO_FLAG_AUDIBILITY_ENFORCED|AUDIO_FLAG_BEACON)) != 0) {
146 return false;
147 }
148 if ((flags & AUDIO_FLAG_LOW_LATENCY) != 0) {
149 return true;
150 }
151 return false;
152 }
153
154 protected:
155 UsecaseLookup m_lookup;
156 };
157
158 } // namespace
159
createUsecaseValidator()160 std::unique_ptr<UsecaseValidator> createUsecaseValidator() {
161 return std::make_unique<UsecaseValidatorImpl>();
162 }
163
164 } // namespace media
165 } // namespace android
166