• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.car.audio;
17 
18 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
19 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_FAILED;
20 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
21 
22 import static com.android.car.audio.CarAudioContext.ALARM;
23 import static com.android.car.audio.CarAudioContext.ANNOUNCEMENT;
24 import static com.android.car.audio.CarAudioContext.CALL;
25 import static com.android.car.audio.CarAudioContext.CALL_RING;
26 import static com.android.car.audio.CarAudioContext.EMERGENCY;
27 import static com.android.car.audio.CarAudioContext.MUSIC;
28 import static com.android.car.audio.CarAudioContext.NAVIGATION;
29 import static com.android.car.audio.CarAudioContext.NOTIFICATION;
30 import static com.android.car.audio.CarAudioContext.SAFETY;
31 import static com.android.car.audio.CarAudioContext.SYSTEM_SOUND;
32 import static com.android.car.audio.CarAudioContext.VEHICLE_STATUS;
33 import static com.android.car.audio.CarAudioContext.VOICE_COMMAND;
34 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
35 
36 import android.annotation.UserIdInt;
37 import android.car.builtin.os.TraceHelper;
38 import android.car.builtin.os.UserManagerHelper;
39 import android.car.builtin.util.Slogf;
40 import android.car.builtin.util.TimingsTraceLog;
41 import android.car.settings.CarSettings;
42 import android.database.ContentObserver;
43 import android.media.AudioAttributes;
44 import android.net.Uri;
45 import android.os.Handler;
46 import android.provider.Settings;
47 import android.util.SparseArray;
48 import android.util.proto.ProtoOutputStream;
49 
50 import com.android.car.CarLog;
51 import com.android.car.CarServiceUtils;
52 import com.android.car.audio.CarAudioContext.AudioContext;
53 import com.android.car.audio.CarAudioDumpProto.CarAudioZoneFocusProto.CarAudioFocusProto;
54 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
55 import com.android.car.internal.util.IndentingPrintWriter;
56 import com.android.internal.annotations.GuardedBy;
57 import com.android.internal.annotations.VisibleForTesting;
58 
59 import java.util.List;
60 import java.util.Objects;
61 
62 /**
63  * FocusInteraction is responsible for evaluating how incoming focus requests should be handled
64  * based on pre-defined interaction behaviors for each incoming {@link AudioContext} in relation to
65  * a {@link AudioContext} that is currently holding focus.
66  */
67 final class FocusInteraction {
68 
69     private static final String TAG = CarLog.tagFor(FocusInteraction.class);
70 
71     static final Uri AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL_URI =
72             Settings.Secure.getUriFor(
73                     CarSettings.Secure.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL);
74 
75     // Values for the internal interaction matrix we use to make focus decisions
76     @VisibleForTesting
77     static final int INTERACTION_REJECT = 0; // Focus not granted
78     @VisibleForTesting
79     static final int INTERACTION_EXCLUSIVE = 1; // Focus granted, others loose focus
80     @VisibleForTesting
81     static final int INTERACTION_CONCURRENT = 2; // Focus granted, others keep focus
82 
83     private static final SparseArray<SparseArray<Integer>> INTERACTION_MATRIX =
84             new SparseArray<>(/* initialCapacity= */ 13);
85 
86     static {
87         // Each Row represents CarAudioContext of current focus holder
88         // Each Column represents CarAudioContext of incoming request (labels along the right)
89         // Cell value is one of INTERACTION_REJECT, INTERACTION_EXCLUSIVE,
90         // or INTERACTION_CONCURRENT
91 
92         // Focus holder: INVALID
93         INTERACTION_MATRIX.append(/* INVALID= */ 0, new SparseArray() {
94             {
95                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
96                 append(MUSIC, INTERACTION_REJECT); // MUSIC
97                 append(NAVIGATION, INTERACTION_REJECT); // NAVIGATION
98                 append(VOICE_COMMAND, INTERACTION_REJECT); // VOICE_COMMAND
99                 append(CALL_RING, INTERACTION_REJECT); // CALL_RING
100                 append(CALL, INTERACTION_REJECT); // CALL
101                 append(ALARM, INTERACTION_REJECT); // ALARM
102                 append(NOTIFICATION, INTERACTION_REJECT); // NOTIFICATION
103                 append(SYSTEM_SOUND, INTERACTION_REJECT); // SYSTEM_SOUND,
104                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
105                 append(SAFETY, INTERACTION_EXCLUSIVE); // SAFETY
106                 append(VEHICLE_STATUS, INTERACTION_REJECT); // VEHICLE_STATUS
107                 append(ANNOUNCEMENT, INTERACTION_REJECT); // ANNOUNCEMENT
108             }
109         });
110         // Focus holder: MUSIC
INTERACTION_MATRIX.append(MUSIC, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_EXCLUSIVE); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); append(CALL_RING, INTERACTION_EXCLUSIVE); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_EXCLUSIVE); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_EXCLUSIVE); } })111         INTERACTION_MATRIX.append(MUSIC, new SparseArray() {
112             {
113                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
114                 append(MUSIC, INTERACTION_EXCLUSIVE); // MUSIC
115                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
116                 append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); // VOICE_COMMAND
117                 append(CALL_RING, INTERACTION_EXCLUSIVE); // CALL_RING
118                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
119                 append(ALARM, INTERACTION_EXCLUSIVE); // ALARM
120                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
121                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
122                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
123                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
124                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
125                 append(ANNOUNCEMENT, INTERACTION_EXCLUSIVE); // ANNOUNCEMENT
126             }
127         });
128         // Focus holder: NAVIGATION
INTERACTION_MATRIX.append(NAVIGATION, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); append(CALL_RING, INTERACTION_CONCURRENT); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_CONCURRENT); } })129         INTERACTION_MATRIX.append(NAVIGATION, new SparseArray() {
130             {
131                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
132                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
133                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
134                 append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); // VOICE_COMMAND
135                 append(CALL_RING, INTERACTION_CONCURRENT); // CALL_RING
136                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
137                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
138                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
139                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
140                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
141                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
142                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
143                 append(ANNOUNCEMENT, INTERACTION_CONCURRENT); // ANNOUNCEMENT
144             }
145         });
146         // Focus holder: VOICE_COMMAND
INTERACTION_MATRIX.append(VOICE_COMMAND, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_REJECT); append(VOICE_COMMAND, INTERACTION_CONCURRENT); append(CALL_RING, INTERACTION_EXCLUSIVE); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_REJECT); append(NOTIFICATION, INTERACTION_REJECT); append(SYSTEM_SOUND, INTERACTION_REJECT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_REJECT); } })147         INTERACTION_MATRIX.append(VOICE_COMMAND, new SparseArray() {
148             {
149                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
150                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
151                 append(NAVIGATION, INTERACTION_REJECT); // NAVIGATION
152                 append(VOICE_COMMAND, INTERACTION_CONCURRENT); // VOICE_COMMAND
153                 append(CALL_RING, INTERACTION_EXCLUSIVE); // CALL_RING
154                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
155                 append(ALARM, INTERACTION_REJECT); // ALARM
156                 append(NOTIFICATION, INTERACTION_REJECT); // NOTIFICATION
157                 append(SYSTEM_SOUND, INTERACTION_REJECT); // SYSTEM_SOUND,
158                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
159                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
160                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
161                 append(ANNOUNCEMENT, INTERACTION_REJECT); // ANNOUNCEMENT
162             }
163         });
164         // Focus holder: CALL_RING
INTERACTION_MATRIX.append(CALL_RING, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_REJECT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_CONCURRENT); append(CALL_RING, INTERACTION_CONCURRENT); append(CALL, INTERACTION_CONCURRENT); append(ALARM, INTERACTION_REJECT); append(NOTIFICATION, INTERACTION_REJECT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_REJECT); } })165         INTERACTION_MATRIX.append(CALL_RING, new SparseArray() {
166             {
167                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
168                 append(MUSIC, INTERACTION_REJECT); // MUSIC
169                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
170                 append(VOICE_COMMAND, INTERACTION_CONCURRENT); // VOICE_COMMAND
171                 append(CALL_RING, INTERACTION_CONCURRENT); // CALL_RING
172                 append(CALL, INTERACTION_CONCURRENT); // CALL
173                 append(ALARM, INTERACTION_REJECT); // ALARM
174                 append(NOTIFICATION, INTERACTION_REJECT); // NOTIFICATION
175                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
176                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
177                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
178                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
179                 append(ANNOUNCEMENT, INTERACTION_REJECT); // ANNOUNCEMENT
180             }
181         });
182         // Focus holder: CALL
INTERACTION_MATRIX.append(CALL, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_REJECT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_REJECT); append(CALL_RING, INTERACTION_CONCURRENT); append(CALL, INTERACTION_CONCURRENT); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_REJECT); append(EMERGENCY, INTERACTION_CONCURRENT); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_REJECT); } })183         INTERACTION_MATRIX.append(CALL, new SparseArray() {
184             {
185                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
186                 append(MUSIC, INTERACTION_REJECT); // MUSIC
187                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
188                 append(VOICE_COMMAND, INTERACTION_REJECT); // VOICE_COMMAND
189                 append(CALL_RING, INTERACTION_CONCURRENT); // CALL_RING
190                 append(CALL, INTERACTION_CONCURRENT); // CALL
191                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
192                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
193                 append(SYSTEM_SOUND, INTERACTION_REJECT); // SYSTEM_SOUND,
194                 append(EMERGENCY, INTERACTION_CONCURRENT); // EMERGENCY
195                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
196                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
197                 append(ANNOUNCEMENT, INTERACTION_REJECT); // ANNOUNCEMENT
198             }
199         });
200         // Focus holder: ALARM
INTERACTION_MATRIX.append(ALARM, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); append(CALL_RING, INTERACTION_EXCLUSIVE); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_REJECT); } })201         INTERACTION_MATRIX.append(ALARM, new SparseArray() {
202             {
203                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
204                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
205                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
206                 append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); // VOICE_COMMAND
207                 append(CALL_RING, INTERACTION_EXCLUSIVE); // CALL_RING
208                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
209                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
210                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
211                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
212                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
213                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
214                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
215                 append(ANNOUNCEMENT, INTERACTION_REJECT); // ANNOUNCEMENT
216             }
217         });
218         // Focus holder: NOTIFICATION
INTERACTION_MATRIX.append(NOTIFICATION, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); append(CALL_RING, INTERACTION_EXCLUSIVE); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_CONCURRENT); } })219         INTERACTION_MATRIX.append(NOTIFICATION, new SparseArray() {
220             {
221                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
222                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
223                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
224                 append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); // VOICE_COMMAND
225                 append(CALL_RING, INTERACTION_EXCLUSIVE); // CALL_RING
226                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
227                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
228                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
229                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
230                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
231                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
232                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
233                 append(ANNOUNCEMENT, INTERACTION_CONCURRENT); // ANNOUNCEMENT
234             }
235         });
236         // Focus holder: SYSTEM_SOUND
INTERACTION_MATRIX.append(SYSTEM_SOUND, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); append(CALL_RING, INTERACTION_EXCLUSIVE); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_CONCURRENT); } })237         INTERACTION_MATRIX.append(SYSTEM_SOUND, new SparseArray() {
238             {
239                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
240                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
241                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
242                 append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); // VOICE_COMMAND
243                 append(CALL_RING, INTERACTION_EXCLUSIVE); // CALL_RING
244                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
245                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
246                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
247                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
248                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
249                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
250                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
251                 append(ANNOUNCEMENT, INTERACTION_CONCURRENT); // ANNOUNCEMENT
252             }
253         });
254         // Focus holder: EMERGENCY
INTERACTION_MATRIX.append(EMERGENCY, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_REJECT); append(NAVIGATION, INTERACTION_REJECT); append(VOICE_COMMAND, INTERACTION_REJECT); append(CALL_RING, INTERACTION_REJECT); append(CALL, INTERACTION_CONCURRENT); append(ALARM, INTERACTION_REJECT); append(NOTIFICATION, INTERACTION_REJECT); append(SYSTEM_SOUND, INTERACTION_REJECT); append(EMERGENCY, INTERACTION_CONCURRENT); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_REJECT); append(ANNOUNCEMENT, INTERACTION_REJECT); } })255         INTERACTION_MATRIX.append(EMERGENCY, new SparseArray() {
256             {
257                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
258                 append(MUSIC, INTERACTION_REJECT); // MUSIC
259                 append(NAVIGATION, INTERACTION_REJECT); // NAVIGATION
260                 append(VOICE_COMMAND, INTERACTION_REJECT); // VOICE_COMMAND
261                 append(CALL_RING, INTERACTION_REJECT); // CALL_RING
262                 append(CALL, INTERACTION_CONCURRENT); // CALL
263                 append(ALARM, INTERACTION_REJECT); // ALARM
264                 append(NOTIFICATION, INTERACTION_REJECT); // NOTIFICATION
265                 append(SYSTEM_SOUND, INTERACTION_REJECT); // SYSTEM_SOUND,
266                 append(EMERGENCY, INTERACTION_CONCURRENT); // EMERGENCY
267                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
268                 append(VEHICLE_STATUS, INTERACTION_REJECT); // VEHICLE_STATUS
269                 append(ANNOUNCEMENT, INTERACTION_REJECT); // ANNOUNCEMENT
270             }
271         });
272         // Focus holder: SAFETY
INTERACTION_MATRIX.append(SAFETY, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_CONCURRENT); append(CALL_RING, INTERACTION_CONCURRENT); append(CALL, INTERACTION_CONCURRENT); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_CONCURRENT); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_CONCURRENT); } })273         INTERACTION_MATRIX.append(SAFETY, new SparseArray() {
274             {
275                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
276                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
277                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
278                 append(VOICE_COMMAND, INTERACTION_CONCURRENT); // VOICE_COMMAND
279                 append(CALL_RING, INTERACTION_CONCURRENT); // CALL_RING
280                 append(CALL, INTERACTION_CONCURRENT); // CALL
281                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
282                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
283                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
284                 append(EMERGENCY, INTERACTION_CONCURRENT); // EMERGENCY
285                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
286                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
287                 append(ANNOUNCEMENT, INTERACTION_CONCURRENT); // ANNOUNCEMENT
288             }
289         });
290         // Focus holder: VEHICLE_STATUS
INTERACTION_MATRIX.append(VEHICLE_STATUS, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_CONCURRENT); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_CONCURRENT); append(CALL_RING, INTERACTION_CONCURRENT); append(CALL, INTERACTION_CONCURRENT); append(ALARM, INTERACTION_CONCURRENT); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_CONCURRENT); } })291         INTERACTION_MATRIX.append(VEHICLE_STATUS, new SparseArray() {
292             {
293                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
294                 append(MUSIC, INTERACTION_CONCURRENT); // MUSIC
295                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
296                 append(VOICE_COMMAND, INTERACTION_CONCURRENT); // VOICE_COMMAND
297                 append(CALL_RING, INTERACTION_CONCURRENT); // CALL_RING
298                 append(CALL, INTERACTION_CONCURRENT); // CALL
299                 append(ALARM, INTERACTION_CONCURRENT); // ALARM
300                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
301                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
302                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
303                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
304                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
305                 append(ANNOUNCEMENT, INTERACTION_CONCURRENT); // ANNOUNCEMENT
306             }
307         });
308         // Focus holder: ANNOUNCEMENT
INTERACTION_MATRIX.append(ANNOUNCEMENT, new SparseArray() { { append( 0, INTERACTION_REJECT); append(MUSIC, INTERACTION_EXCLUSIVE); append(NAVIGATION, INTERACTION_CONCURRENT); append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); append(CALL_RING, INTERACTION_EXCLUSIVE); append(CALL, INTERACTION_EXCLUSIVE); append(ALARM, INTERACTION_EXCLUSIVE); append(NOTIFICATION, INTERACTION_CONCURRENT); append(SYSTEM_SOUND, INTERACTION_CONCURRENT); append(EMERGENCY, INTERACTION_EXCLUSIVE); append(SAFETY, INTERACTION_CONCURRENT); append(VEHICLE_STATUS, INTERACTION_CONCURRENT); append(ANNOUNCEMENT, INTERACTION_EXCLUSIVE); } })309         INTERACTION_MATRIX.append(ANNOUNCEMENT, new SparseArray() {
310             {
311                 append(/* INVALID= */ 0, INTERACTION_REJECT); // INVALID
312                 append(MUSIC, INTERACTION_EXCLUSIVE); // MUSIC
313                 append(NAVIGATION, INTERACTION_CONCURRENT); // NAVIGATION
314                 append(VOICE_COMMAND, INTERACTION_EXCLUSIVE); // VOICE_COMMAND
315                 append(CALL_RING, INTERACTION_EXCLUSIVE); // CALL_RING
316                 append(CALL, INTERACTION_EXCLUSIVE); // CALL
317                 append(ALARM, INTERACTION_EXCLUSIVE); // ALARM
318                 append(NOTIFICATION, INTERACTION_CONCURRENT); // NOTIFICATION
319                 append(SYSTEM_SOUND, INTERACTION_CONCURRENT); // SYSTEM_SOUND,
320                 append(EMERGENCY, INTERACTION_EXCLUSIVE); // EMERGENCY
321                 append(SAFETY, INTERACTION_CONCURRENT); // SAFETY
322                 append(VEHICLE_STATUS, INTERACTION_CONCURRENT); // VEHICLE_STATUS
323                 append(ANNOUNCEMENT, INTERACTION_EXCLUSIVE); // ANNOUNCEMENT
324             }
325         });
326     }
327 
328     private final Object mLock = new Object();
329 
330     @GuardedBy("mLock")
331     private final SparseArray<SparseArray<Integer>> mInteractionMatrix;
332 
333     private ContentObserver mContentObserver;
334 
335     private final CarAudioSettings mCarAudioFocusSettings;
336 
337     private final ContentObserverFactory mContentObserverFactory;
338     private int mUserId;
339 
340     /**
341      * Constructs a focus interaction instance.
342      */
FocusInteraction(CarAudioSettings carAudioSettings, ContentObserverFactory contentObserverFactory)343     FocusInteraction(CarAudioSettings carAudioSettings,
344             ContentObserverFactory contentObserverFactory) {
345         mCarAudioFocusSettings = Objects.requireNonNull(carAudioSettings,
346                 "Car Audio Settings can not be null.");
347         mContentObserverFactory = Objects.requireNonNull(contentObserverFactory,
348                 "Content Observer Factory can not be null.");
349         mInteractionMatrix = INTERACTION_MATRIX.clone();
350     }
351 
navigationOnCallSettingChanged()352     private void navigationOnCallSettingChanged() {
353         synchronized (mLock) {
354             if (mUserId != UserManagerHelper.USER_NULL) {
355                 setRejectNavigationOnCallLocked(isRejectNavigationOnCallEnabledInSettings(mUserId));
356             }
357         }
358     }
359 
360     @GuardedBy("mLock")
setRejectNavigationOnCallLocked(boolean navigationRejectedWithCall)361     public void setRejectNavigationOnCallLocked(boolean navigationRejectedWithCall) {
362         int callContext = CarAudioContext.getLegacyContextForUsage(
363                         AudioAttributes.USAGE_VOICE_COMMUNICATION);
364         int navContext = CarAudioContext.getLegacyContextForUsage(
365                         AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
366         mInteractionMatrix.get(callContext).put(navContext,
367                 navigationRejectedWithCall ? INTERACTION_REJECT : INTERACTION_CONCURRENT);
368     }
369 
isRejectNavigationOnCallEnabled()370     public boolean isRejectNavigationOnCallEnabled() {
371         int callContext = CarAudioContext.getLegacyContextForUsage(
372                 AudioAttributes.USAGE_VOICE_COMMUNICATION);
373         int navContext = CarAudioContext.getLegacyContextForUsage(
374                 AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
375         synchronized (mLock) {
376             return mInteractionMatrix.get(callContext).get(navContext) == INTERACTION_REJECT;
377         }
378     }
379 
380     /**
381      * Evaluates interaction between incoming focus audio attribute usage and the current focus
382      * request based on interaction matrix.
383      *
384      * <p><b>Note</b> In addition to returning the request results
385      * for the incoming request based on this interaction, this method also adds the current {@code
386      * focusHolder} to the {@code focusLosers} list when appropriate.
387      *
388      * @param requestedUsage        Audio attribute usage of the incoming request
389      * @param focusHolder           {@link FocusEntry} for current focus holder
390      * @param allowDucking          Whether ducking is allowed
391      * @param allowsDelayedFocus    Whether delayed focus is allowed
392      * @param focusLosers           Mutable array to add focusHolder to if it should lose focus
393      * @return result of focus interaction, can be any of
394      *      {@link android.media.AudioManager#AUDIOFOCUS_REQUEST_DELAYED},
395      *      {@link android.media.AudioManager#AUDIOFOCUS_REQUEST_FAILED}, or
396      *      {@link android.media.AudioManager#AUDIOFOCUS_REQUEST_GRANTED}
397      */
evaluateRequest(int requestedUsage, FocusEntry focusHolder, boolean allowDucking, boolean allowsDelayedFocus, List<FocusEntry> focusLosers)398     int evaluateRequest(int requestedUsage, FocusEntry focusHolder, boolean allowDucking,
399             boolean allowsDelayedFocus, List<FocusEntry> focusLosers) {
400         int holderUsage = focusHolder.getAudioFocusInfo().getAttributes().getSystemUsage();
401 
402         TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE);
403         t.traceBegin("evaluate-focus-request");
404         synchronized (mLock) {
405             int focusDecision = getFocusInteractionLocked(requestedUsage, holderUsage);
406 
407             switch (focusDecision) {
408                 case INTERACTION_REJECT:
409                     if (allowsDelayedFocus) {
410                         t.traceEnd();
411                         return AUDIOFOCUS_REQUEST_DELAYED;
412                     }
413                     t.traceEnd();
414                     return AUDIOFOCUS_REQUEST_FAILED;
415                 case INTERACTION_EXCLUSIVE:
416                     focusLosers.add(focusHolder);
417                     t.traceEnd();
418                     return AUDIOFOCUS_REQUEST_GRANTED;
419                 case INTERACTION_CONCURRENT:
420                     // If ducking isn't allowed by the focus requester, then everybody else
421                     // must get a LOSS.
422                     // If a focus holder has set the AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS flag,
423                     // they must get a LOSS message even if ducking would otherwise be allowed.
424                     // If a focus holder holds the RECEIVE_CAR_AUDIO_DUCKING_EVENTS permission,
425                     // they must receive all audio focus losses.
426                     if (!allowDucking
427                             || focusHolder.wantsPauseInsteadOfDucking()
428                             || focusHolder.receivesDuckEvents()) {
429                         focusLosers.add(focusHolder);
430                     }
431                     t.traceEnd();
432                     return AUDIOFOCUS_REQUEST_GRANTED;
433                 default:
434                     Slogf.e(TAG, "Unsupported CarAudioContext %d - rejecting request",
435                             focusDecision);
436                     t.traceEnd();
437                     return AUDIOFOCUS_REQUEST_FAILED;
438             }
439         }
440     }
441 
442     @GuardedBy("mLock")
getFocusInteractionLocked(int requestedUsage, int holderUsage)443     private int getFocusInteractionLocked(int requestedUsage, int holderUsage) {
444         int requestedContext = CarAudioContext.getLegacyContextForUsage(requestedUsage);
445         int holderContext = CarAudioContext.getLegacyContextForUsage(holderUsage);
446         SparseArray<Integer> holderRow = mInteractionMatrix.get(holderContext);
447         return holderRow.get(requestedContext);
448     }
449 
450     /**
451      * Sets userId for interaction focus settings
452      */
setUserIdForSettings(@serIdInt int userId)453     void setUserIdForSettings(@UserIdInt int userId) {
454         synchronized (mLock) {
455             if (mContentObserver != null) {
456                 mCarAudioFocusSettings.getContentResolverForUser(mUserId)
457                         .unregisterContentObserver(mContentObserver);
458                 mContentObserver = null;
459             }
460             mUserId = userId;
461             if (mUserId == UserManagerHelper.USER_NULL) {
462                 setRejectNavigationOnCallLocked(false);
463                 return;
464             }
465             var carHandlerThread = CarServiceUtils.getHandlerThread(
466                     CarAudioService.class.getSimpleName());
467             mContentObserver = mContentObserverFactory.createObserver(
468                     this::navigationOnCallSettingChanged,
469                     new Handler(carHandlerThread.getLooper()));
470             mCarAudioFocusSettings.getContentResolverForUser(mUserId)
471                     .registerContentObserver(AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL_URI,
472                             /* notifyForDescendants= */false, mContentObserver);
473             setRejectNavigationOnCallLocked(isRejectNavigationOnCallEnabledInSettings(mUserId));
474         }
475     }
476 
isRejectNavigationOnCallEnabledInSettings(@serIdInt int userId)477     private boolean isRejectNavigationOnCallEnabledInSettings(@UserIdInt int userId) {
478         return mCarAudioFocusSettings.isRejectNavigationOnCallEnabledInSettings(userId);
479     }
480 
481     @VisibleForTesting
getInteractionMatrix()482     SparseArray<SparseArray<Integer>> getInteractionMatrix() {
483         synchronized (mLock) {
484             return mInteractionMatrix.clone();
485         }
486     }
487 
488     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)489     public void dump(IndentingPrintWriter writer) {
490         boolean rejectNavigationOnCall = isRejectNavigationOnCallEnabled();
491         writer.printf("Reject Navigation on Call: %b\n", rejectNavigationOnCall);
492     }
493 
494     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)495     public void dumpProto(ProtoOutputStream proto) {
496         long focusInteractionToken = proto.start(CarAudioFocusProto.FOCUS_INTERACTION);
497         boolean rejectNavigationOnCall = isRejectNavigationOnCallEnabled();
498         proto.write(CarAudioFocusProto.FocusInteractionProto.REJECT_NAVIGATION_ON_CALL,
499                 rejectNavigationOnCall);
500         proto.end(focusInteractionToken);
501     }
502 }
503