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