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