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