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