• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
17 package com.android.server.telecom;
18 
19 import static com.android.server.telecom.AudioRoute.BT_AUDIO_ROUTE_TYPES;
20 import static com.android.server.telecom.AudioRoute.DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE;
21 import static com.android.server.telecom.AudioRoute.TYPE_INVALID;
22 import static com.android.server.telecom.AudioRoute.TYPE_SPEAKER;
23 
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothDevice;
26 import android.bluetooth.BluetoothLeAudio;
27 import android.bluetooth.BluetoothProfile;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.media.AudioAttributes;
33 import android.media.AudioDeviceAttributes;
34 import android.media.AudioDeviceInfo;
35 import android.media.AudioManager;
36 import android.media.IAudioService;
37 import android.media.audiopolicy.AudioProductStrategy;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.RemoteException;
43 import android.telecom.CallAudioState;
44 import android.telecom.Log;
45 import android.telecom.Logging.Session;
46 import android.telecom.VideoProfile;
47 import android.util.ArrayMap;
48 import android.util.Pair;
49 
50 import androidx.annotation.NonNull;
51 
52 import com.android.internal.annotations.VisibleForTesting;
53 import com.android.internal.os.SomeArgs;
54 import com.android.internal.util.IndentingPrintWriter;
55 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
56 import com.android.server.telecom.flags.FeatureFlags;
57 import com.android.server.telecom.metrics.ErrorStats;
58 import com.android.server.telecom.metrics.TelecomMetricsController;
59 
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.HashMap;
63 import java.util.HashSet;
64 import java.util.LinkedHashMap;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Objects;
68 import java.util.Set;
69 import java.util.concurrent.CountDownLatch;
70 import java.util.concurrent.ExecutorService;
71 import java.util.concurrent.Executors;
72 
73 public class CallAudioRouteController implements CallAudioRouteAdapter {
74     private static final AudioRoute DUMMY_ROUTE = new AudioRoute(TYPE_INVALID, null, null);
75     private static final Map<Integer, Integer> ROUTE_MAP;
76     static {
77         ROUTE_MAP = new ArrayMap<>();
ROUTE_MAP.put(TYPE_INVALID, 0)78         ROUTE_MAP.put(TYPE_INVALID, 0);
ROUTE_MAP.put(AudioRoute.TYPE_EARPIECE, CallAudioState.ROUTE_EARPIECE)79         ROUTE_MAP.put(AudioRoute.TYPE_EARPIECE, CallAudioState.ROUTE_EARPIECE);
ROUTE_MAP.put(AudioRoute.TYPE_WIRED, CallAudioState.ROUTE_WIRED_HEADSET)80         ROUTE_MAP.put(AudioRoute.TYPE_WIRED, CallAudioState.ROUTE_WIRED_HEADSET);
ROUTE_MAP.put(AudioRoute.TYPE_SPEAKER, CallAudioState.ROUTE_SPEAKER)81         ROUTE_MAP.put(AudioRoute.TYPE_SPEAKER, CallAudioState.ROUTE_SPEAKER);
ROUTE_MAP.put(AudioRoute.TYPE_DOCK, CallAudioState.ROUTE_SPEAKER)82         ROUTE_MAP.put(AudioRoute.TYPE_DOCK, CallAudioState.ROUTE_SPEAKER);
ROUTE_MAP.put(AudioRoute.TYPE_BUS, CallAudioState.ROUTE_SPEAKER)83         ROUTE_MAP.put(AudioRoute.TYPE_BUS, CallAudioState.ROUTE_SPEAKER);
ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_SCO, CallAudioState.ROUTE_BLUETOOTH)84         ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_SCO, CallAudioState.ROUTE_BLUETOOTH);
ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_HA, CallAudioState.ROUTE_BLUETOOTH)85         ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_HA, CallAudioState.ROUTE_BLUETOOTH);
ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_LE, CallAudioState.ROUTE_BLUETOOTH)86         ROUTE_MAP.put(AudioRoute.TYPE_BLUETOOTH_LE, CallAudioState.ROUTE_BLUETOOTH);
ROUTE_MAP.put(AudioRoute.TYPE_STREAMING, CallAudioState.ROUTE_STREAMING)87         ROUTE_MAP.put(AudioRoute.TYPE_STREAMING, CallAudioState.ROUTE_STREAMING);
88     }
89 
90     /** Valid values for the first argument for SWITCH_BASELINE_ROUTE */
91     public static final int INCLUDE_BLUETOOTH_IN_BASELINE = 1;
92 
93     private final CallsManager mCallsManager;
94     private final Context mContext;
95     private AudioManager mAudioManager;
96     private CallAudioManager mCallAudioManager;
97     private final BluetoothRouteManager mBluetoothRouteManager;
98     private final CallAudioManager.AudioServiceFactory mAudioServiceFactory;
99     private final Handler mHandler;
100     private final WiredHeadsetManager mWiredHeadsetManager;
101     private Set<AudioRoute> mAvailableRoutes;
102     private Set<AudioRoute> mCallSupportedRoutes;
103     private AudioRoute mCurrentRoute;
104     private AudioRoute mEarpieceWiredRoute;
105     private AudioRoute mSpeakerDockRoute;
106     private AudioRoute mStreamingRoute;
107     private Set<AudioRoute> mStreamingRoutes;
108     private Map<AudioRoute, BluetoothDevice> mBluetoothRoutes;
109     private Pair<Integer, String> mActiveBluetoothDevice;
110     private Map<Integer, String> mActiveDeviceCache;
111     private String mBluetoothAddressForRinging;
112     private Map<Integer, AudioRoute> mTypeRoutes;
113     private PendingAudioRoute mPendingAudioRoute;
114     private AudioRoute.Factory mAudioRouteFactory;
115     private StatusBarNotifier mStatusBarNotifier;
116     private AudioManager.OnCommunicationDeviceChangedListener mCommunicationDeviceListener;
117     private ExecutorService mCommunicationDeviceChangedExecutor;
118     private FeatureFlags mFeatureFlags;
119     private int mFocusType;
120     private int mCallSupportedRouteMask = -1;
121     private BluetoothDevice mScoAudioConnectedDevice;
122     private boolean mAvailableRoutesUpdated;
123     private boolean mUsePreferredDeviceStrategy;
124     private AudioDeviceInfo mCurrentCommunicationDevice;
125     private final Object mLock = new Object();
126     private final TelecomSystem.SyncRoot mTelecomLock;
127     private CountDownLatch mAudioOperationsCompleteLatch;
128     private CountDownLatch mAudioActiveCompleteLatch;
129     private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() {
130         @Override
131         public void onReceive(Context context, Intent intent) {
132             Log.startSession("CARC.mSPCR");
133             try {
134                 if (AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED.equals(intent.getAction())) {
135                     if (mAudioManager != null) {
136                         AudioDeviceInfo info = mFeatureFlags.updatePreferredAudioDeviceLogic()
137                                 ? getCurrentCommunicationDevice()
138                                 : mAudioManager.getCommunicationDevice();
139                         if ((info != null) &&
140                                 (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)) {
141                             if (mCurrentRoute.getType() != AudioRoute.TYPE_SPEAKER) {
142                                 sendMessageWithSessionInfo(SPEAKER_ON);
143                             }
144                         } else {
145                             sendMessageWithSessionInfo(SPEAKER_OFF);
146                         }
147                     }
148                 } else {
149                     Log.w(this, "Received non-speakerphone-change intent");
150                 }
151             } finally {
152                 Log.endSession();
153             }
154         }
155     };
156     private final BroadcastReceiver mMuteChangeReceiver = new BroadcastReceiver() {
157         @Override
158         public void onReceive(Context context, Intent intent) {
159             Log.startSession("CARC.mCR");
160             try {
161                 if (AudioManager.ACTION_MICROPHONE_MUTE_CHANGED.equals(intent.getAction())) {
162                     if (mCallsManager.isInEmergencyCall()) {
163                         Log.i(this, "Mute was externally changed when there's an emergency call. "
164                                 + "Forcing mute back off.");
165                         sendMessageWithSessionInfo(MUTE_OFF);
166                     } else {
167                         sendMessageWithSessionInfo(MUTE_EXTERNALLY_CHANGED);
168                     }
169                 } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(intent.getAction())) {
170                     int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
171                     boolean isStreamMuted = intent.getBooleanExtra(
172                             AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
173 
174                     if (streamType == AudioManager.STREAM_RING && !isStreamMuted
175                             && mCallAudioManager != null) {
176                         Log.i(this, "Ring stream was un-muted.");
177                         mCallAudioManager.onRingerModeChange();
178                     }
179                 } else {
180                     Log.w(this, "Received non-mute-change intent");
181                 }
182             } finally {
183                 Log.endSession();
184             }
185         }
186     };
187     private CallAudioState mCallAudioState;
188     private boolean mIsMute;
189     private boolean mIsPending;
190     private boolean mIsActive;
191     private boolean mWasOnSpeaker;
192     private final TelecomMetricsController mMetricsController;
193 
CallAudioRouteController( Context context, CallsManager callsManager, CallAudioManager.AudioServiceFactory audioServiceFactory, AudioRoute.Factory audioRouteFactory, WiredHeadsetManager wiredHeadsetManager, BluetoothRouteManager bluetoothRouteManager, StatusBarNotifier statusBarNotifier, FeatureFlags featureFlags, TelecomMetricsController metricsController)194     public CallAudioRouteController(
195             Context context, CallsManager callsManager,
196             CallAudioManager.AudioServiceFactory audioServiceFactory,
197             AudioRoute.Factory audioRouteFactory, WiredHeadsetManager wiredHeadsetManager,
198             BluetoothRouteManager bluetoothRouteManager, StatusBarNotifier statusBarNotifier,
199             FeatureFlags featureFlags, TelecomMetricsController metricsController) {
200         mContext = context;
201         mCallsManager = callsManager;
202         mAudioManager = context.getSystemService(AudioManager.class);
203         mAudioServiceFactory = audioServiceFactory;
204         mAudioRouteFactory = audioRouteFactory;
205         mWiredHeadsetManager = wiredHeadsetManager;
206         mIsMute = false;
207         mBluetoothRouteManager = bluetoothRouteManager;
208         mStatusBarNotifier = statusBarNotifier;
209         mFeatureFlags = featureFlags;
210         mMetricsController = metricsController;
211         mFocusType = NO_FOCUS;
212         mScoAudioConnectedDevice = null;
213         mUsePreferredDeviceStrategy = true;
214         mWasOnSpeaker = false;
215         setCurrentCommunicationDevice(null);
216 
217         mTelecomLock = callsManager.getLock();
218         HandlerThread handlerThread = new HandlerThread(this.getClass().getSimpleName());
219         if (!mFeatureFlags.callAudioRoutingPerformanceImprovemenent()) {
220             handlerThread.start();
221         }
222 
223         // Register broadcast receivers
224         if (!mFeatureFlags.newAudioPathSpeakerBroadcastAndUnfocusedRouting()) {
225             IntentFilter speakerChangedFilter = new IntentFilter(
226                     AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED);
227             speakerChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
228             context.registerReceiver(mSpeakerPhoneChangeReceiver, speakerChangedFilter);
229         }
230 
231         IntentFilter micMuteChangedFilter = new IntentFilter(
232                 AudioManager.ACTION_MICROPHONE_MUTE_CHANGED);
233         micMuteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
234         context.registerReceiver(mMuteChangeReceiver, micMuteChangedFilter);
235 
236         IntentFilter muteChangedFilter = new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION);
237         muteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
238         context.registerReceiver(mMuteChangeReceiver, muteChangedFilter);
239 
240         // Register AudioManager#onCommunicationDeviceChangedListener listener to receive updates
241         // to communication device (via AudioManager#setCommunicationDevice). This is a replacement
242         // to using broadcasts in the hopes of improving performance.
243         mCommunicationDeviceChangedExecutor = Executors.newSingleThreadExecutor();
244         mCommunicationDeviceListener = new AudioManager.OnCommunicationDeviceChangedListener() {
245             @Override
246             public void onCommunicationDeviceChanged(AudioDeviceInfo device) {
247                 @AudioRoute.AudioRouteType int audioType = getAudioType(device);
248                 setCurrentCommunicationDevice(device);
249                 Log.i(this, "onCommunicationDeviceChanged: device (%s), audioType (%d)",
250                         device, audioType);
251                 if (audioType == TYPE_SPEAKER) {
252                     if (mCurrentRoute.getType() != TYPE_SPEAKER) {
253                         sendMessageWithSessionInfo(SPEAKER_ON);
254                     }
255                 } else {
256                     sendMessageWithSessionInfo(SPEAKER_OFF);
257                 }
258             }
259         };
260 
261         Looper looper = mFeatureFlags.callAudioRoutingPerformanceImprovemenent()
262                 ? Looper.getMainLooper()
263                 : handlerThread.getLooper();
264         // Create handler
265         mHandler = new Handler(looper) {
266             @Override
267             public void handleMessage(@NonNull Message msg) {
268                 synchronized (this) {
269                     preHandleMessage(msg);
270                     String address;
271                     BluetoothDevice bluetoothDevice;
272                     int focus;
273                     int handleEndTone;
274                     @AudioRoute.AudioRouteType int type;
275                     switch (msg.what) {
276                         case CONNECT_WIRED_HEADSET:
277                             handleWiredHeadsetConnected();
278                             break;
279                         case DISCONNECT_WIRED_HEADSET:
280                             handleWiredHeadsetDisconnected();
281                             break;
282                         case CONNECT_DOCK:
283                             handleDockConnected();
284                             break;
285                         case DISCONNECT_DOCK:
286                             handleDockDisconnected();
287                             break;
288                         case BLUETOOTH_DEVICE_LIST_CHANGED:
289                             break;
290                         case BT_ACTIVE_DEVICE_PRESENT:
291                             type = msg.arg1;
292                             address = (String) ((SomeArgs) msg.obj).arg2;
293                             handleBtActiveDevicePresent(type, address);
294                             break;
295                         case BT_ACTIVE_DEVICE_GONE:
296                             type = msg.arg1;
297                             handleBtActiveDeviceGone(type);
298                             break;
299                         case BT_DEVICE_ADDED:
300                             type = msg.arg1;
301                             bluetoothDevice = (BluetoothDevice) ((SomeArgs) msg.obj).arg2;
302                             handleBtConnected(type, bluetoothDevice);
303                             break;
304                         case BT_DEVICE_REMOVED:
305                             type = msg.arg1;
306                             bluetoothDevice = (BluetoothDevice) ((SomeArgs) msg.obj).arg2;
307                             handleBtDisconnected(type, bluetoothDevice);
308                             break;
309                         case SWITCH_EARPIECE:
310                         case USER_SWITCH_EARPIECE:
311                             handleSwitchEarpiece(msg.what == USER_SWITCH_EARPIECE);
312                             break;
313                         case SWITCH_BLUETOOTH:
314                         case USER_SWITCH_BLUETOOTH:
315                             address = (String) ((SomeArgs) msg.obj).arg2;
316                             handleSwitchBluetooth(address, msg.what == USER_SWITCH_BLUETOOTH);
317                             break;
318                         case SWITCH_HEADSET:
319                         case USER_SWITCH_HEADSET:
320                             handleSwitchHeadset(msg.what == USER_SWITCH_HEADSET);
321                             break;
322                         case SWITCH_SPEAKER:
323                         case USER_SWITCH_SPEAKER:
324                             handleSwitchSpeaker();
325                             break;
326                         case SWITCH_BASELINE_ROUTE:
327                             address = (String) ((SomeArgs) msg.obj).arg2;
328                             handleSwitchBaselineRoute(false,
329                                     msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE, address);
330                             break;
331                         case USER_SWITCH_BASELINE_ROUTE:
332                             handleSwitchBaselineRoute(true,
333                                     msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE, null);
334                             break;
335                         case SPEAKER_ON:
336                             handleSpeakerOn();
337                             break;
338                         case SPEAKER_OFF:
339                             handleSpeakerOff();
340                             break;
341                         case STREAMING_FORCE_ENABLED:
342                             handleStreamingEnabled();
343                             break;
344                         case STREAMING_FORCE_DISABLED:
345                             handleStreamingDisabled();
346                             break;
347                         case BT_AUDIO_CONNECTED:
348                             bluetoothDevice = (BluetoothDevice) ((SomeArgs) msg.obj).arg2;
349                             handleBtAudioActive(bluetoothDevice);
350                             break;
351                         case BT_AUDIO_DISCONNECTED:
352                             bluetoothDevice = (BluetoothDevice) ((SomeArgs) msg.obj).arg2;
353                             handleBtAudioInactive(bluetoothDevice);
354                             break;
355                         case MUTE_ON:
356                             handleMuteChanged(true);
357                             break;
358                         case MUTE_OFF:
359                             handleMuteChanged(false);
360                             break;
361                         case MUTE_EXTERNALLY_CHANGED:
362                             handleMuteChanged(mAudioManager.isMicrophoneMute());
363                             break;
364                         case TOGGLE_MUTE:
365                             handleMuteChanged(!mIsMute);
366                             break;
367                         case SWITCH_FOCUS:
368                             focus = msg.arg1;
369                             handleEndTone = (int) ((SomeArgs) msg.obj).arg2;
370                             handleSwitchFocus(focus, handleEndTone);
371                             break;
372                         case EXIT_PENDING_ROUTE:
373                             handleExitPendingRoute();
374                             break;
375                         case UPDATE_SYSTEM_AUDIO_ROUTE:
376                             // Based on the available routes for foreground call, adjust routing.
377                             updateRouteForForeground();
378                             // Force update to notify all ICS/CS.
379                             updateCallAudioState(new CallAudioState(mIsMute,
380                                     mCallAudioState.getRoute(),
381                                     mCallAudioState.getSupportedRouteMask(),
382                                     mCallAudioState.getActiveBluetoothDevice(),
383                                     mCallAudioState.getSupportedBluetoothDevices()));
384                         default:
385                             break;
386                     }
387                     postHandleMessage(msg);
388                 }
389             }
390         };
391     }
392     @Override
initialize()393     public void initialize() {
394         mAvailableRoutes = new HashSet<>();
395         mCallSupportedRoutes = new HashSet<>();
396         mBluetoothRoutes = Collections.synchronizedMap(new LinkedHashMap<>());
397         mActiveDeviceCache = new HashMap<>();
398         mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_SCO, null);
399         mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_HA, null);
400         mActiveDeviceCache.put(AudioRoute.TYPE_BLUETOOTH_LE, null);
401         mActiveBluetoothDevice = null;
402         mTypeRoutes = new ArrayMap<>();
403         mStreamingRoutes = new HashSet<>();
404         mPendingAudioRoute = new PendingAudioRoute(this, mAudioManager, mBluetoothRouteManager,
405                 mFeatureFlags);
406         mStreamingRoute = new AudioRoute(AudioRoute.TYPE_STREAMING, null, null);
407         mStreamingRoutes.add(mStreamingRoute);
408 
409         int supportMask = calculateSupportedRouteMaskInit();
410         if ((supportMask & CallAudioState.ROUTE_SPEAKER) != 0) {
411             int audioRouteType = AudioRoute.TYPE_SPEAKER;
412             // Create speaker routes
413             mSpeakerDockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_SPEAKER, null,
414                     mAudioManager);
415             if (mSpeakerDockRoute == null){
416                 Log.i(this, "Can't find available audio device info for route TYPE_SPEAKER, trying"
417                         + " for TYPE_BUS");
418                 mSpeakerDockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_BUS, null,
419                         mAudioManager);
420                 audioRouteType = AudioRoute.TYPE_BUS;
421             }
422             if (mSpeakerDockRoute != null) {
423                 mTypeRoutes.put(audioRouteType, mSpeakerDockRoute);
424                 updateAvailableRoutes(mSpeakerDockRoute, true);
425             } else {
426                 Log.w(this, "Can't find available audio device info for route TYPE_SPEAKER "
427                         + "or TYPE_BUS.");
428             }
429         }
430 
431         if ((supportMask & CallAudioState.ROUTE_WIRED_HEADSET) != 0) {
432             // Create wired headset routes
433             mEarpieceWiredRoute = mAudioRouteFactory.create(AudioRoute.TYPE_WIRED, null,
434                     mAudioManager);
435             if (mEarpieceWiredRoute == null) {
436                 Log.w(this, "Can't find available audio device info for route TYPE_WIRED_HEADSET");
437             } else {
438                 mTypeRoutes.put(AudioRoute.TYPE_WIRED, mEarpieceWiredRoute);
439                 updateAvailableRoutes(mEarpieceWiredRoute, true);
440             }
441         } else if ((supportMask & CallAudioState.ROUTE_EARPIECE) != 0) {
442             // Create earpiece routes
443             mEarpieceWiredRoute = mAudioRouteFactory.create(AudioRoute.TYPE_EARPIECE, null,
444                     mAudioManager);
445             if (mEarpieceWiredRoute == null) {
446                 Log.w(this, "Can't find available audio device info for route TYPE_EARPIECE");
447             } else {
448                 mTypeRoutes.put(AudioRoute.TYPE_EARPIECE, mEarpieceWiredRoute);
449                 updateAvailableRoutes(mEarpieceWiredRoute, true);
450             }
451         }
452 
453         // set current route
454         if (mEarpieceWiredRoute != null) {
455             mCurrentRoute = mEarpieceWiredRoute;
456         } else if (mSpeakerDockRoute != null) {
457             mCurrentRoute = mSpeakerDockRoute;
458         } else {
459             mCurrentRoute = DUMMY_ROUTE;
460         }
461         // Audio ops will only ever be completed if there's a call placed and it gains
462         // ACTIVE/RINGING focus, hence why the initial value is 0.
463         mAudioOperationsCompleteLatch = new CountDownLatch(0);
464         // This latch will be count down when ACTIVE/RINGING focus is gained. This is determined
465         // when the routing goes active.
466         mAudioActiveCompleteLatch = new CountDownLatch(1);
467         mIsActive = false;
468         mCallAudioState = new CallAudioState(mIsMute, ROUTE_MAP.get(mCurrentRoute.getType()),
469                 supportMask, null, new HashSet<>());
470         if (mFeatureFlags.newAudioPathSpeakerBroadcastAndUnfocusedRouting()) {
471             mAudioManager.addOnCommunicationDeviceChangedListener(
472                     mCommunicationDeviceChangedExecutor,
473                     mCommunicationDeviceListener);
474         }
475     }
476 
477     @Override
sendMessageWithSessionInfo(int message)478     public void sendMessageWithSessionInfo(int message) {
479         sendMessageWithSessionInfo(message, 0, (String) null);
480     }
481 
482     @Override
sendMessageWithSessionInfo(int message, int arg)483     public void sendMessageWithSessionInfo(int message, int arg) {
484         sendMessageWithSessionInfo(message, arg, (String) null);
485     }
486 
487     @Override
sendMessageWithSessionInfo(int message, int arg, String data)488     public void sendMessageWithSessionInfo(int message, int arg, String data) {
489         SomeArgs args = SomeArgs.obtain();
490         args.arg1 = Log.createSubsession();
491         args.arg2 = data;
492         sendMessage(message, arg, 0, args);
493     }
494 
495     @Override
sendMessageWithSessionInfo(int message, int arg, int data)496     public void sendMessageWithSessionInfo(int message, int arg, int data) {
497         SomeArgs args = SomeArgs.obtain();
498         args.arg1 = Log.createSubsession();
499         args.arg2 = data;
500         sendMessage(message, arg, 0, args);
501     }
502 
503     @Override
sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice)504     public void sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice) {
505         SomeArgs args = SomeArgs.obtain();
506         args.arg1 = Log.createSubsession();
507         args.arg2 = bluetoothDevice;
508         sendMessage(message, arg, 0, args);
509     }
510 
511     @Override
sendMessage(int message, Runnable r)512     public void sendMessage(int message, Runnable r) {
513         r.run();
514     }
515 
sendMessage(int what, int arg1, int arg2, Object obj)516     private void sendMessage(int what, int arg1, int arg2, Object obj) {
517         mHandler.sendMessage(Message.obtain(mHandler, what, arg1, arg2, obj));
518     }
519 
520     @Override
setCallAudioManager(CallAudioManager callAudioManager)521     public void setCallAudioManager(CallAudioManager callAudioManager) {
522         mCallAudioManager = callAudioManager;
523     }
524 
525     @Override
getCurrentCallAudioState()526     public CallAudioState getCurrentCallAudioState() {
527         return mCallAudioState;
528     }
529 
530     @Override
isHfpDeviceAvailable()531     public boolean isHfpDeviceAvailable() {
532         return !mBluetoothRoutes.isEmpty();
533     }
534 
535     @Override
getAdapterHandler()536     public Handler getAdapterHandler() {
537         return mHandler;
538     }
539 
540     @Override
getPendingAudioRoute()541     public PendingAudioRoute getPendingAudioRoute() {
542         return mPendingAudioRoute;
543     }
544 
545     @Override
dump(IndentingPrintWriter pw)546     public void dump(IndentingPrintWriter pw) {
547     }
548 
preHandleMessage(Message msg)549     private void preHandleMessage(Message msg) {
550         if (msg.obj instanceof SomeArgs) {
551             Session session = (Session) ((SomeArgs) msg.obj).arg1;
552             String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown");
553             Log.continueSession(session, "CARC.pM_" + messageCodeName);
554             Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1);
555         }
556     }
557 
postHandleMessage(Message msg)558     private void postHandleMessage(Message msg) {
559         Log.endSession();
560         if (msg.obj instanceof SomeArgs) {
561             ((SomeArgs) msg.obj).recycle();
562         }
563     }
564 
isActive()565     public boolean isActive() {
566         return mIsActive;
567     }
568 
isPending()569     public boolean isPending() {
570         return mIsPending;
571     }
572 
routeTo(boolean isDestRouteActive, AudioRoute destRoute)573     private void routeTo(boolean isDestRouteActive, AudioRoute destRoute) {
574         if (destRoute == null || (!destRoute.equals(mStreamingRoute)
575                 && !getCallSupportedRoutes().contains(destRoute))) {
576             Log.i(this, "Ignore routing to unavailable route: %s", destRoute);
577             if (mFeatureFlags.telecomMetricsSupport()) {
578                 mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
579                         ErrorStats.ERROR_AUDIO_ROUTE_UNAVAILABLE);
580             }
581             return;
582         }
583         // If another BT device connects during RINGING_FOCUS, in-band ringing will be disabled by
584         // default. In this case, we should adjust the active routing value so that we don't try
585         // to connect to the BT device as it will fail.
586         isDestRouteActive = maybeAdjustActiveRouting(destRoute, isDestRouteActive);
587         // It's possible that there are multiple HFP devices connected and if we receive SCO audio
588         // connected for the destination route's BT device, then we shouldn't disconnect SCO when
589         // clearing the communication device for the original route if it was also a HFP device.
590         // This does not apply to the route deactivation scenario.
591         boolean isScoDeviceAlreadyConnected = mScoAudioConnectedDevice != null && isDestRouteActive
592                 && Objects.equals(mScoAudioConnectedDevice, mBluetoothRoutes.get(destRoute));
593         if (mIsPending) {
594             if (destRoute.equals(mPendingAudioRoute.getDestRoute())
595                     && (mIsActive == isDestRouteActive)) {
596                 return;
597             }
598             Log.i(this, "Override current pending route destination from %s(active=%b) to "
599                             + "%s(active=%b)",
600                     mPendingAudioRoute.getDestRoute(), mIsActive, destRoute, isDestRouteActive);
601             // Ensure we don't keep waiting for SPEAKER_ON if dest route gets overridden.
602             if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue() && isDestRouteActive
603                     && mPendingAudioRoute.getDestRoute().getType() == TYPE_SPEAKER) {
604                 mPendingAudioRoute.clearPendingMessage(new Pair<>(SPEAKER_ON, null));
605             }
606             // override pending route while keep waiting for still pending messages for the
607             // previous pending route
608             mPendingAudioRoute.setOrigRoute(mIsActive /* origin */,
609                     mPendingAudioRoute.getDestRoute(), isDestRouteActive /* dest */,
610                     isScoDeviceAlreadyConnected);
611         } else {
612             if (mCurrentRoute.equals(destRoute) && (mIsActive == isDestRouteActive)) {
613                 return;
614             }
615             Log.i(this, "Enter pending route, orig%s(active=%b), dest%s(active=%b)", mCurrentRoute,
616                     mIsActive, destRoute, isDestRouteActive);
617             // route to pending route
618             if (getCallSupportedRoutes().contains(mCurrentRoute)) {
619                 mPendingAudioRoute.setOrigRoute(mIsActive /* origin */, mCurrentRoute,
620                         isDestRouteActive /* dest */, isScoDeviceAlreadyConnected);
621             } else {
622                 // Avoid waiting for pending messages for an unavailable route
623                 mPendingAudioRoute.setOrigRoute(mIsActive /* origin */, DUMMY_ROUTE,
624                         isDestRouteActive /* dest */, isScoDeviceAlreadyConnected);
625             }
626             mIsPending = true;
627         }
628         mPendingAudioRoute.setDestRoute(isDestRouteActive, destRoute,
629                 mBluetoothRoutes.get(destRoute), isScoDeviceAlreadyConnected);
630         mIsActive = isDestRouteActive;
631         mPendingAudioRoute.evaluatePendingState();
632         if (mFeatureFlags.telecomMetricsSupport()) {
633             mMetricsController.getAudioRouteStats().onRouteEnter(mPendingAudioRoute);
634         }
635     }
636 
handleWiredHeadsetConnected()637     private void handleWiredHeadsetConnected() {
638         AudioRoute wiredHeadsetRoute = null;
639         try {
640             wiredHeadsetRoute = mAudioRouteFactory.create(AudioRoute.TYPE_WIRED, null,
641                     mAudioManager);
642         } catch (IllegalArgumentException e) {
643             if (mFeatureFlags.telecomMetricsSupport()) {
644                 mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
645                         ErrorStats.ERROR_EXTERNAL_EXCEPTION);
646             }
647             Log.e(this, e, "Can't find available audio device info for route type:"
648                     + AudioRoute.DEVICE_TYPE_STRINGS.get(AudioRoute.TYPE_WIRED));
649         }
650 
651         if (wiredHeadsetRoute != null) {
652             updateAvailableRoutes(wiredHeadsetRoute, true);
653             updateAvailableRoutes(mEarpieceWiredRoute, false);
654             mTypeRoutes.put(AudioRoute.TYPE_WIRED, wiredHeadsetRoute);
655             mEarpieceWiredRoute = wiredHeadsetRoute;
656             routeTo(mIsActive, wiredHeadsetRoute);
657             onAvailableRoutesChanged();
658         }
659     }
660 
handleWiredHeadsetDisconnected()661     public void handleWiredHeadsetDisconnected() {
662         // Update audio route states
663         AudioRoute wiredHeadsetRoute = mTypeRoutes.remove(AudioRoute.TYPE_WIRED);
664         if (wiredHeadsetRoute != null) {
665             updateAvailableRoutes(wiredHeadsetRoute, false);
666             mEarpieceWiredRoute = null;
667         }
668         AudioRoute earpieceRoute = null;
669         try {
670             earpieceRoute = mTypeRoutes.get(AudioRoute.TYPE_EARPIECE) == null
671                 ? mAudioRouteFactory.create(AudioRoute.TYPE_EARPIECE, null,
672                     mAudioManager)
673                 : mTypeRoutes.get(AudioRoute.TYPE_EARPIECE);
674         } catch (IllegalArgumentException e) {
675             if (mFeatureFlags.telecomMetricsSupport()) {
676                 mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
677                         ErrorStats.ERROR_EXTERNAL_EXCEPTION);
678             }
679             Log.e(this, e, "Can't find available audio device info for route type:"
680                     + AudioRoute.DEVICE_TYPE_STRINGS.get(AudioRoute.TYPE_EARPIECE));
681         }
682         if (earpieceRoute != null) {
683             updateAvailableRoutes(earpieceRoute, true);
684             mEarpieceWiredRoute = earpieceRoute;
685             // In the case that the route was never created, ensure that we update the map.
686             mTypeRoutes.putIfAbsent(AudioRoute.TYPE_EARPIECE, mEarpieceWiredRoute);
687         }
688         onAvailableRoutesChanged();
689 
690         // Route to expected state
691         if (mCurrentRoute.equals(wiredHeadsetRoute)) {
692             // Preserve speaker routing if it was the last audio routing path when the wired headset
693             // disconnects. Ignore this special cased routing when the route isn't active
694             // (in other words, when we're not in a call).
695             AudioRoute route = mWasOnSpeaker && mIsActive && mSpeakerDockRoute != null
696                     && mSpeakerDockRoute.getType() == AudioRoute.TYPE_SPEAKER
697                     ? mSpeakerDockRoute : getBaseRoute(true, null);
698             routeTo(mIsActive, route);
699         }
700     }
701 
handleDockConnected()702     private void handleDockConnected() {
703         AudioRoute dockRoute = null;
704         try {
705             dockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_DOCK, null, mAudioManager);
706         } catch (IllegalArgumentException e) {
707             if (mFeatureFlags.telecomMetricsSupport()) {
708                 mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
709                         ErrorStats.ERROR_EXTERNAL_EXCEPTION);
710             }
711             Log.e(this, e, "Can't find available audio device info for route type:"
712                     + AudioRoute.DEVICE_TYPE_STRINGS.get(AudioRoute.TYPE_WIRED));
713         }
714 
715         if (dockRoute != null) {
716             updateAvailableRoutes(dockRoute, true);
717             updateAvailableRoutes(mSpeakerDockRoute, false);
718             mTypeRoutes.put(AudioRoute.TYPE_DOCK, dockRoute);
719             mSpeakerDockRoute = dockRoute;
720             routeTo(mIsActive, dockRoute);
721             onAvailableRoutesChanged();
722         }
723     }
724 
handleDockDisconnected()725     public void handleDockDisconnected() {
726         // Update audio route states
727         AudioRoute dockRoute = mTypeRoutes.get(AudioRoute.TYPE_DOCK);
728         if (dockRoute != null) {
729             updateAvailableRoutes(dockRoute, false);
730             mSpeakerDockRoute = null;
731         }
732         AudioRoute speakerRoute = mTypeRoutes.get(AudioRoute.TYPE_SPEAKER);
733         if (speakerRoute != null) {
734             updateAvailableRoutes(speakerRoute, true);
735             mSpeakerDockRoute = speakerRoute;
736         }
737         onAvailableRoutesChanged();
738 
739         // Route to expected state
740         if (mCurrentRoute.equals(dockRoute)) {
741             routeTo(mIsActive, getBaseRoute(true, null));
742         }
743     }
744 
handleStreamingEnabled()745     private void handleStreamingEnabled() {
746         if (!mCurrentRoute.equals(mStreamingRoute)) {
747             routeTo(mIsActive, mStreamingRoute);
748         } else {
749             Log.i(this, "ignore enable streaming, already in streaming");
750         }
751     }
752 
handleStreamingDisabled()753     private void handleStreamingDisabled() {
754         if (mCurrentRoute.equals(mStreamingRoute)) {
755             mCurrentRoute = DUMMY_ROUTE;
756             onAvailableRoutesChanged();
757             routeTo(mIsActive, getBaseRoute(true, null));
758         } else {
759             Log.i(this, "ignore disable streaming, not in streaming");
760         }
761     }
762 
763     /**
764      * Handles the case when SCO audio is connected for the BT headset. This follows shortly after
765      * the BT device has been established as an active device (BT_ACTIVE_DEVICE_PRESENT) and doesn't
766      * apply to other BT device types. In this case, the pending audio route will process the
767      * BT_AUDIO_CONNECTED message that will trigger routing to the pending destination audio route;
768      * otherwise, routing will be ignored if there aren't pending routes to be processed.
769      *
770      * Message being handled: BT_AUDIO_CONNECTED
771      */
handleBtAudioActive(BluetoothDevice bluetoothDevice)772     private void handleBtAudioActive(BluetoothDevice bluetoothDevice) {
773         if (mIsPending) {
774             Log.i(this, "handleBtAudioActive: is pending path");
775             if (Objects.equals(mPendingAudioRoute.getDestRoute().getBluetoothAddress(),
776                     bluetoothDevice.getAddress())) {
777                 mPendingAudioRoute.onMessageReceived(new Pair<>(BT_AUDIO_CONNECTED,
778                         bluetoothDevice.getAddress()), null);
779             }
780         }
781     }
782 
783     /**
784      * Handles the case when SCO audio is disconnected for the BT headset. In this case, the pending
785      * audio route will process the BT_AUDIO_DISCONNECTED message which will trigger routing to the
786      * pending destination audio route; otherwise, routing will be ignored if there aren't any
787      * pending routes to be processed.
788      *
789      * Message being handled: BT_AUDIO_DISCONNECTED
790      */
handleBtAudioInactive(BluetoothDevice bluetoothDevice)791     private void handleBtAudioInactive(BluetoothDevice bluetoothDevice) {
792         if (mIsPending) {
793             Log.i(this, "handleBtAudioInactive: is pending path");
794             if (Objects.equals(mPendingAudioRoute.getOrigRoute().getBluetoothAddress(),
795                     bluetoothDevice.getAddress())) {
796                 mPendingAudioRoute.onMessageReceived(new Pair<>(BT_AUDIO_DISCONNECTED,
797                         bluetoothDevice.getAddress()), null);
798             }
799         }
800     }
801 
802     /**
803      * This particular routing occurs when the BT device is trying to establish itself as a
804      * connected device (refer to BluetoothStateReceiver#handleConnectionStateChanged). The device
805      * is included as an available route and cached into the current BT routes.
806      *
807      * Message being handled: BT_DEVICE_ADDED
808      */
handleBtConnected(@udioRoute.AudioRouteType int type, BluetoothDevice bluetoothDevice)809     private void handleBtConnected(@AudioRoute.AudioRouteType int type,
810             BluetoothDevice bluetoothDevice) {
811         if (containsHearingAidPair(type, bluetoothDevice)) {
812             return;
813         }
814 
815         AudioRoute bluetoothRoute = mAudioRouteFactory.create(type, bluetoothDevice.getAddress(),
816                 mAudioManager);
817         if (bluetoothRoute == null) {
818             Log.w(this, "Can't find available audio device info for route type:"
819                     + AudioRoute.DEVICE_TYPE_STRINGS.get(type));
820         } else {
821             Log.i(this, "bluetooth route added: " + bluetoothRoute);
822             updateAvailableRoutes(bluetoothRoute, true);
823             mBluetoothRoutes.put(bluetoothRoute, bluetoothDevice);
824             onAvailableRoutesChanged();
825         }
826     }
827 
828     /**
829      * Handles the case when the BT device is in a disconnecting/disconnected state. In this case,
830      * the audio route for the specified device is removed from the available BT routes and the
831      * audio is routed to an available route if the current route is pointing to the device which
832      * got disconnected.
833      *
834      * Message being handled: BT_DEVICE_REMOVED
835      */
handleBtDisconnected(@udioRoute.AudioRouteType int type, BluetoothDevice bluetoothDevice)836     private void handleBtDisconnected(@AudioRoute.AudioRouteType int type,
837             BluetoothDevice bluetoothDevice) {
838         // Clean up unavailable routes
839         AudioRoute bluetoothRoute = getBluetoothRoute(type, bluetoothDevice.getAddress());
840         if (bluetoothRoute != null) {
841             Log.i(this, "bluetooth route removed: " + bluetoothRoute);
842             mBluetoothRoutes.remove(bluetoothRoute);
843             updateAvailableRoutes(bluetoothRoute, false);
844             onAvailableRoutesChanged();
845         }
846 
847         // Fallback to an available route
848         if (Objects.equals(mCurrentRoute, bluetoothRoute)) {
849             routeTo(mIsActive, getBaseRoute(true, null));
850         }
851     }
852 
853     /**
854      * This particular routing occurs when the specified bluetooth device is marked as the active
855      * device (refer to BluetoothStateReceiver#handleActiveDeviceChanged). This takes care of
856      * moving the call audio route to the bluetooth route.
857      *
858      * Message being handled: BT_ACTIVE_DEVICE_PRESENT
859      */
handleBtActiveDevicePresent(@udioRoute.AudioRouteType int type, String deviceAddress)860     private void handleBtActiveDevicePresent(@AudioRoute.AudioRouteType int type,
861             String deviceAddress) {
862         AudioRoute bluetoothRoute = getBluetoothRoute(type, deviceAddress);
863         boolean isBtDeviceCurrentActive = Objects.equals(bluetoothRoute,
864                 getArbitraryBluetoothDevice());
865         if (bluetoothRoute != null && isBtDeviceCurrentActive) {
866             Log.i(this, "request to route to bluetooth route: %s (active=%b)", bluetoothRoute,
867                     mIsActive);
868             routeTo(mIsActive, bluetoothRoute);
869         } else {
870             Log.i(this, "request to route to unavailable bluetooth route or the route isn't the "
871                     + "currently active device - type (%s), address (%s)", type, deviceAddress);
872         }
873     }
874 
875     /**
876      * Handles routing for when the active BT device is removed for a given audio route type. In
877      * this case, the audio is routed to another available route if the current route hasn't been
878      * adjusted yet or there is a pending destination route associated with the device type that
879      * went inactive. Note that BT_DEVICE_REMOVED will be processed first in this case, which will
880      * handle removing the BT route for the device that went inactive as well as falling back to
881      * an available route.
882      *
883      * Message being handled: BT_ACTIVE_DEVICE_GONE
884      */
handleBtActiveDeviceGone(@udioRoute.AudioRouteType int type)885     private void handleBtActiveDeviceGone(@AudioRoute.AudioRouteType int type) {
886         // Determine what the active device for the BT audio type was so that we can exclude this
887         // device from being used when calculating the base route.
888         String previouslyActiveDeviceAddress = mFeatureFlags
889                 .resolveActiveBtRoutingAndBtTimingIssue()
890                 ? mActiveDeviceCache.get(type)
891                 : null;
892         // It's possible that the dest route hasn't been set yet when the controller is first
893         // initialized.
894         boolean pendingRouteNeedsUpdate = mPendingAudioRoute.getDestRoute() != null
895                 && mPendingAudioRoute.getDestRoute().getType() == type;
896         boolean currentRouteNeedsUpdate = mCurrentRoute.getType() == type;
897         if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
898             if (pendingRouteNeedsUpdate) {
899                 pendingRouteNeedsUpdate = mPendingAudioRoute.getDestRoute().getBluetoothAddress()
900                         .equals(previouslyActiveDeviceAddress);
901             }
902             if (currentRouteNeedsUpdate) {
903                 currentRouteNeedsUpdate = mCurrentRoute.getBluetoothAddress()
904                         .equals(previouslyActiveDeviceAddress);
905             }
906         }
907         if ((mIsPending && pendingRouteNeedsUpdate) || (!mIsPending && currentRouteNeedsUpdate)) {
908             maybeDisableWasOnSpeaker(true);
909             // Fallback to an available route excluding the previously active device.
910             routeTo(mIsActive, getBaseRoute(true, previouslyActiveDeviceAddress));
911         }
912     }
913 
handleMuteChanged(boolean mute)914     private void handleMuteChanged(boolean mute) {
915         mIsMute = mute;
916         if (mIsMute != mAudioManager.isMicrophoneMute() && mIsActive) {
917             IAudioService audioService = mAudioServiceFactory.getAudioService();
918             Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", mute,
919                     audioService == null);
920             if (audioService != null) {
921                 try {
922                     audioService.setMicrophoneMute(mute, mContext.getOpPackageName(),
923                             mCallsManager.getCurrentUserHandle().getIdentifier(),
924                             mContext.getAttributionTag());
925                 } catch (RemoteException e) {
926                     if (mFeatureFlags.telecomMetricsSupport()) {
927                         mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
928                                 ErrorStats.ERROR_EXTERNAL_EXCEPTION);
929                     }
930                     Log.e(this, e, "Remote exception while toggling mute.");
931                     return;
932                 }
933             }
934         }
935         onMuteStateChanged(mIsMute);
936     }
937 
handleSwitchFocus(int focus, int handleEndTone)938     private void handleSwitchFocus(int focus, int handleEndTone) {
939         Log.i(this, "handleSwitchFocus: focus (%s)", focus);
940         mFocusType = focus;
941         switch (focus) {
942             case NO_FOCUS -> {
943                 mWasOnSpeaker = false;
944                 // Notify the CallAudioModeStateMachine that audio operations are complete so
945                 // that we can relinquish audio focus.
946                 mCallAudioManager.notifyAudioOperationsComplete();
947                 // Reset mute state after call ends. This should remain unaffected if audio routing
948                 // never went active.
949                 handleMuteChanged(false);
950                 // Ensure we reset call audio state at the end of the call (i.e. if we're on
951                 // speaker, route back to earpiece). If we're on BT, remain on BT if it's still
952                 // connected.
953                 AudioRoute route = mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()
954                         ? calculateBaselineRoute(false, true, null)
955                         : mCurrentRoute;
956                 routeTo(false, route);
957                 // Clear pending messages
958                 mPendingAudioRoute.clearPendingMessages();
959                 clearRingingBluetoothAddress();
960                 mUsePreferredDeviceStrategy = true;
961             }
962             case ACTIVE_FOCUS -> {
963                 // Route to active baseline route (we may need to change audio route in the case
964                 // when a video call is put on hold). Ignore route changes if we're handling playing
965                 // the end tone. Otherwise, it's possible that we'll override the route a client has
966                 // previously requested.
967                 if (handleEndTone == 0) {
968                     // Cache BT device switch in the case that inband ringing is disabled and audio
969                     // was routed to a watch. When active focus is received, this selection will be
970                     // honored provided that the current route is associated.
971                     Log.i(this, "handleSwitchFocus (ACTIVE_FOCUS): mBluetoothAddressForRinging = "
972                             + "%s, mCurrentRoute = %s", mBluetoothAddressForRinging, mCurrentRoute);
973                     AudioRoute audioRoute = mBluetoothAddressForRinging != null
974                             && mBluetoothAddressForRinging.equals(
975                                     mCurrentRoute.getBluetoothAddress())
976                             ? mCurrentRoute
977                             : getBaseRoute(true, null);
978                     // Once we have processed active focus once during the call, we can ignore using
979                     // the preferred device strategy.
980                     mUsePreferredDeviceStrategy = false;
981                     routeTo(true, audioRoute);
982                     clearRingingBluetoothAddress();
983                 }
984             }
985             case RINGING_FOCUS -> {
986                 if (!mIsActive) {
987                     AudioRoute route = getBaseRoute(true, null);
988                     BluetoothDevice device = mBluetoothRoutes.get(route);
989                     // Check if in-band ringtone is enabled for the device; if it isn't, move to
990                     // inactive route.
991                     if (device != null && !mBluetoothRouteManager
992                             .isInbandRingEnabled(route.getType(), device)) {
993                         routeTo(false, route);
994                     } else {
995                         routeTo(true, route);
996                     }
997                 } else {
998                     // Route is already active.
999                     BluetoothDevice device = mBluetoothRoutes.get(mCurrentRoute);
1000                     if (device != null && !mBluetoothRouteManager
1001                             .isInbandRingEnabled(mCurrentRoute.getType(), device)) {
1002                         routeTo(false, mCurrentRoute);
1003                     }
1004                 }
1005             }
1006         }
1007     }
1008 
handleSwitchEarpiece(boolean isUserRequest)1009     public void handleSwitchEarpiece(boolean isUserRequest) {
1010         AudioRoute earpieceRoute = mTypeRoutes.get(AudioRoute.TYPE_EARPIECE);
1011         if (earpieceRoute != null && getCallSupportedRoutes().contains(earpieceRoute)) {
1012             maybeDisableWasOnSpeaker(isUserRequest);
1013             routeTo(mIsActive, earpieceRoute);
1014         } else {
1015             Log.i(this, "ignore switch earpiece request");
1016         }
1017     }
1018 
handleSwitchBluetooth(String address, boolean isUserRequest)1019     private void handleSwitchBluetooth(String address, boolean isUserRequest) {
1020         Log.i(this, "handle switch to bluetooth with address %s", address);
1021         AudioRoute bluetoothRoute = null;
1022         BluetoothDevice bluetoothDevice = null;
1023         if (address == null) {
1024             bluetoothRoute = getArbitraryBluetoothDevice();
1025             bluetoothDevice = mBluetoothRoutes.get(bluetoothRoute);
1026         } else {
1027             for (AudioRoute route : getCallSupportedRoutes()) {
1028                 if (Objects.equals(address, route.getBluetoothAddress())) {
1029                     bluetoothRoute = route;
1030                     bluetoothDevice = mBluetoothRoutes.get(route);
1031                     break;
1032                 }
1033             }
1034         }
1035 
1036         if (bluetoothRoute != null && bluetoothDevice != null) {
1037             maybeDisableWasOnSpeaker(isUserRequest);
1038             if (mFocusType == RINGING_FOCUS) {
1039                 routeTo(mBluetoothRouteManager
1040                                 .isInbandRingEnabled(bluetoothRoute.getType(), bluetoothDevice)
1041                                 && mIsActive, bluetoothRoute);
1042                 mBluetoothAddressForRinging = bluetoothDevice.getAddress();
1043             } else {
1044                 routeTo(mIsActive, bluetoothRoute);
1045             }
1046         } else {
1047             Log.i(this, "ignore switch bluetooth request to unavailable address");
1048         }
1049     }
1050 
1051     /**
1052      * Retrieve the active BT device, if available, otherwise return the most recently tracked
1053      * active device, or null if none are available.
1054      * @return {@link AudioRoute} of the BT device.
1055      */
getArbitraryBluetoothDevice()1056     private AudioRoute getArbitraryBluetoothDevice() {
1057         synchronized (mLock) {
1058             if (mActiveBluetoothDevice != null) {
1059                 return getBluetoothRoute(
1060                     mActiveBluetoothDevice.first, mActiveBluetoothDevice.second);
1061             } else if (!mBluetoothRoutes.isEmpty()) {
1062                 return mBluetoothRoutes.keySet().stream().toList()
1063                     .get(mBluetoothRoutes.size() - 1);
1064             }
1065             return null;
1066         }
1067     }
1068 
handleSwitchHeadset(boolean isUserRequest)1069     private void handleSwitchHeadset(boolean isUserRequest) {
1070         AudioRoute headsetRoute = mTypeRoutes.get(AudioRoute.TYPE_WIRED);
1071         if (headsetRoute != null && getCallSupportedRoutes().contains(headsetRoute)) {
1072             maybeDisableWasOnSpeaker(isUserRequest);
1073             routeTo(mIsActive, headsetRoute);
1074         } else {
1075             Log.i(this, "ignore switch headset request");
1076         }
1077     }
1078 
handleSwitchSpeaker()1079     private void handleSwitchSpeaker() {
1080         if (mSpeakerDockRoute != null && getCallSupportedRoutes().contains(mSpeakerDockRoute)
1081                 && mSpeakerDockRoute.getType() == AudioRoute.TYPE_SPEAKER) {
1082             routeTo(mIsActive, mSpeakerDockRoute);
1083         } else {
1084             Log.i(this, "ignore switch speaker request");
1085         }
1086     }
1087 
handleSwitchBaselineRoute(boolean isExplicitUserRequest, boolean includeBluetooth, String btAddressToExclude)1088     private void handleSwitchBaselineRoute(boolean isExplicitUserRequest, boolean includeBluetooth,
1089             String btAddressToExclude) {
1090         Log.i(this, "handleSwitchBaselineRoute: includeBluetooth: %b, "
1091                 + "btAddressToExclude: %s", includeBluetooth, btAddressToExclude);
1092         AudioRoute pendingDestRoute = mPendingAudioRoute.getDestRoute();
1093         boolean areExcludedBtAndDestBtSame = btAddressToExclude != null
1094                 && pendingDestRoute != null
1095                 && Objects.equals(btAddressToExclude, pendingDestRoute.getBluetoothAddress());
1096         Pair<Integer, String> btDevicePendingMsg =
1097                 new Pair<>(BT_AUDIO_CONNECTED, btAddressToExclude);
1098 
1099         // If SCO is once again connected or there's a pending message for BT_AUDIO_CONNECTED, then
1100         // we know that the device has reconnected or is in the middle of connecting. Ignore routing
1101         // out of this BT device.
1102         boolean isExcludedDeviceConnectingOrConnected = areExcludedBtAndDestBtSame
1103                 && (Objects.equals(mBluetoothRoutes.get(pendingDestRoute), mScoAudioConnectedDevice)
1104                 || mPendingAudioRoute.getPendingMessages().contains(btDevicePendingMsg));
1105         // Check if the pending audio route or current route is already different from the route
1106         // including the BT device that should be excluded from route selection.
1107         boolean isCurrentOrDestRouteDifferent = btAddressToExclude != null
1108                 && ((mIsPending && !btAddressToExclude.equals(mPendingAudioRoute.getDestRoute()
1109                 .getBluetoothAddress())) || (!mIsPending && !btAddressToExclude.equals(
1110                         mCurrentRoute.getBluetoothAddress())));
1111         if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
1112             if (isExcludedDeviceConnectingOrConnected) {
1113                 Log.i(this, "BT device with address (%s) is currently connecting/connected. "
1114                         + "Ignoring route switch.", btAddressToExclude);
1115                 return;
1116             } else if (isCurrentOrDestRouteDifferent) {
1117                 Log.i(this, "Current or pending audio route isn't routed to device with address "
1118                         + "(%s). Ignoring route switch.", btAddressToExclude);
1119                 return;
1120             }
1121         }
1122         maybeDisableWasOnSpeaker(isExplicitUserRequest);
1123         routeTo(mIsActive, calculateBaselineRoute(isExplicitUserRequest, includeBluetooth,
1124                 btAddressToExclude));
1125     }
1126 
handleSpeakerOn()1127     private void handleSpeakerOn() {
1128         if (isPending()) {
1129             Log.i(this, "handleSpeakerOn: sending SPEAKER_ON to pending audio route");
1130             mPendingAudioRoute.onMessageReceived(new Pair<>(SPEAKER_ON, null), null);
1131             // Update status bar notification if we are in a call.
1132             mStatusBarNotifier.notifySpeakerphone(mCallsManager.hasAnyCalls());
1133         } else {
1134             if (mSpeakerDockRoute != null && getCallSupportedRoutes().contains(mSpeakerDockRoute)
1135                     && mSpeakerDockRoute.getType() == AudioRoute.TYPE_SPEAKER
1136                     && mCurrentRoute.getType() != AudioRoute.TYPE_SPEAKER) {
1137                 routeTo(mIsActive, mSpeakerDockRoute);
1138                 // Since the route switching triggered by this message, we need to manually send it
1139                 // again so that we won't stuck in the pending route
1140                 if (mIsActive) {
1141                     sendMessageWithSessionInfo(SPEAKER_ON);
1142                 }
1143             }
1144         }
1145     }
1146 
handleSpeakerOff()1147     private void handleSpeakerOff() {
1148         if (isPending()) {
1149             Log.i(this, "handleSpeakerOff - sending SPEAKER_OFF to pending audio route");
1150             mPendingAudioRoute.onMessageReceived(new Pair<>(SPEAKER_OFF, null), null);
1151             // Update status bar notification
1152             mStatusBarNotifier.notifySpeakerphone(false);
1153         } else if (mCurrentRoute.getType() == AudioRoute.TYPE_SPEAKER) {
1154             routeTo(mIsActive, getBaseRoute(true, null));
1155             // Since the route switching triggered by this message, we need to manually send it
1156             // again so that we won't stuck in the pending route
1157             if (mIsActive) {
1158                 sendMessageWithSessionInfo(SPEAKER_OFF);
1159             }
1160             onAvailableRoutesChanged();
1161         }
1162     }
1163 
1164     /**
1165      * This is invoked when there are no more pending audio routes to be processed, which signals
1166      * a change for the current audio route and the call audio state to be updated accordingly.
1167      */
handleExitPendingRoute()1168     public void handleExitPendingRoute() {
1169         if (mIsPending) {
1170             mCurrentRoute = mPendingAudioRoute.getDestRoute();
1171             Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
1172                     "Entering audio route: " + mCurrentRoute + " (active=" + mIsActive + ")");
1173             mIsPending = false;
1174             mPendingAudioRoute.clearPendingMessages();
1175             onCurrentRouteChanged();
1176             if (mIsActive) {
1177                 // Only set mWasOnSpeaker if the routing was active. We don't want to consider this
1178                 // selection outside of a call.
1179                 if (mCurrentRoute.getType() == TYPE_SPEAKER) {
1180                     mWasOnSpeaker = true;
1181                 }
1182                 // Reinitialize the audio ops complete latch since the routing went active. We
1183                 // should always expect operations to complete after this point.
1184                 if (mAudioOperationsCompleteLatch.getCount() == 0) {
1185                     mAudioOperationsCompleteLatch = new CountDownLatch(1);
1186                 }
1187                 mAudioActiveCompleteLatch.countDown();
1188             } else {
1189                 // Reinitialize the active routing latch when audio ops are complete so that it can
1190                 // once again be processed when a new call is placed/received.
1191                 if (mAudioActiveCompleteLatch.getCount() == 0) {
1192                     mAudioActiveCompleteLatch = new CountDownLatch(1);
1193                 }
1194                 mAudioOperationsCompleteLatch.countDown();
1195             }
1196             if (mFeatureFlags.telecomMetricsSupport()) {
1197                 mMetricsController.getAudioRouteStats().onRouteExit(mPendingAudioRoute, true);
1198             }
1199         }
1200     }
1201 
onCurrentRouteChanged()1202     private void onCurrentRouteChanged() {
1203         synchronized (mLock) {
1204             BluetoothDevice activeBluetoothDevice = null;
1205             int route = ROUTE_MAP.get(mCurrentRoute.getType());
1206             if (route == CallAudioState.ROUTE_STREAMING) {
1207                 updateCallAudioState(new CallAudioState(mIsMute, route, route));
1208                 return;
1209             }
1210             if (route == CallAudioState.ROUTE_BLUETOOTH) {
1211                 activeBluetoothDevice = mBluetoothRoutes.get(mCurrentRoute);
1212             }
1213             updateCallAudioState(new CallAudioState(mIsMute, route,
1214                     mCallAudioState.getRawSupportedRouteMask(), activeBluetoothDevice,
1215                     mCallAudioState.getSupportedBluetoothDevices()));
1216         }
1217     }
1218 
onAvailableRoutesChanged()1219     private void onAvailableRoutesChanged() {
1220         synchronized (mLock) {
1221             int routeMask = 0;
1222             Set<BluetoothDevice> availableBluetoothDevices = new HashSet<>();
1223             for (AudioRoute route : getCallSupportedRoutes()) {
1224                 routeMask |= ROUTE_MAP.get(route.getType());
1225                 if (BT_AUDIO_ROUTE_TYPES.contains(route.getType())) {
1226                     BluetoothDevice deviceToAdd = mBluetoothRoutes.get(route);
1227                     // Only include the lead device for LE audio (otherwise, the routes will show
1228                     // two separate devices in the UI).
1229                     if (deviceToAdd != null && route.getType() == AudioRoute.TYPE_BLUETOOTH_LE
1230                             && getLeAudioService() != null) {
1231                         int groupId = getLeAudioService().getGroupId(deviceToAdd);
1232                         if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
1233                             deviceToAdd = getLeAudioService().getConnectedGroupLeadDevice(groupId);
1234                         }
1235                     }
1236                     // This will only ever be null when the lead device (LE) is disconnected and
1237                     // try to obtain the lead device for the 2nd bud.
1238                     if (deviceToAdd != null) {
1239                         availableBluetoothDevices.add(deviceToAdd);
1240                     }
1241                 }
1242             }
1243 
1244             updateCallAudioState(new CallAudioState(mIsMute, mCallAudioState.getRoute(), routeMask,
1245                     mCallAudioState.getActiveBluetoothDevice(), availableBluetoothDevices));
1246         }
1247     }
1248 
onMuteStateChanged(boolean mute)1249     private void onMuteStateChanged(boolean mute) {
1250         updateCallAudioState(new CallAudioState(mute, mCallAudioState.getRoute(),
1251                 mCallAudioState.getSupportedRouteMask(), mCallAudioState.getActiveBluetoothDevice(),
1252                 mCallAudioState.getSupportedBluetoothDevices()));
1253     }
1254 
1255     /**
1256      * Retrieves the current call's supported audio route and adjusts the audio routing if the
1257      * current route isn't supported.
1258      */
updateRouteForForeground()1259     private void updateRouteForForeground() {
1260         boolean updatedRouteForCall = updateCallSupportedAudioRoutes();
1261         // Ensure that current call audio state has updated routes for current call.
1262         if (updatedRouteForCall) {
1263             mCallAudioState = new CallAudioState(mIsMute, mCallAudioState.getRoute(),
1264                     mCallSupportedRouteMask, mCallAudioState.getActiveBluetoothDevice(),
1265                     mCallAudioState.getSupportedBluetoothDevices());
1266             // Update audio route if foreground call doesn't support the current route.
1267             if ((mCallSupportedRouteMask & mCallAudioState.getRoute()) == 0) {
1268                 routeTo(mIsActive, getBaseRoute(true, null));
1269             }
1270         }
1271     }
1272 
1273     /**
1274      * Update supported audio routes for the foreground call if present.
1275      */
updateCallSupportedAudioRoutes()1276     private boolean updateCallSupportedAudioRoutes() {
1277         int availableRouteMask = 0;
1278         Call foregroundCall = mCallsManager.getForegroundCall();
1279         mCallSupportedRoutes.clear();
1280         if (foregroundCall != null) {
1281             int foregroundCallSupportedRouteMask = foregroundCall.getSupportedAudioRoutes();
1282             for (AudioRoute route : getAvailableRoutes()) {
1283                 int routeType = ROUTE_MAP.get(route.getType());
1284                 availableRouteMask |= routeType;
1285                 if ((routeType & foregroundCallSupportedRouteMask) == routeType) {
1286                     mCallSupportedRoutes.add(route);
1287                 }
1288             }
1289             mCallSupportedRouteMask = availableRouteMask & foregroundCallSupportedRouteMask;
1290             return true;
1291         } else {
1292             mCallSupportedRouteMask = -1;
1293             return false;
1294         }
1295     }
1296 
updateCallAudioState(CallAudioState newCallAudioState)1297     private void updateCallAudioState(CallAudioState newCallAudioState) {
1298         synchronized (mTelecomLock) {
1299             Log.i(this, "updateCallAudioState: updating call audio state to %s", newCallAudioState);
1300             CallAudioState oldState = mCallAudioState;
1301             mCallAudioState = newCallAudioState;
1302             // Update status bar notification
1303             mStatusBarNotifier.notifyMute(newCallAudioState.isMuted());
1304             mCallsManager.onCallAudioStateChanged(oldState, mCallAudioState);
1305             updateAudioStateForTrackedCalls(mCallAudioState);
1306         }
1307     }
1308 
updateAudioStateForTrackedCalls(CallAudioState newCallAudioState)1309     private void updateAudioStateForTrackedCalls(CallAudioState newCallAudioState) {
1310         List<Call> calls = new ArrayList<>(mCallsManager.getTrackedCalls());
1311         for (Call call : calls) {
1312             if (call != null && call.getConnectionService() != null) {
1313                 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState);
1314             }
1315         }
1316     }
1317 
getPreferredAudioRouteFromStrategy()1318     private AudioRoute getPreferredAudioRouteFromStrategy() {
1319         // Get preferred device
1320         AudioDeviceAttributes deviceAttr = getPreferredDeviceForStrategy();
1321         Log.i(this, "getPreferredAudioRouteFromStrategy: preferred device is %s", deviceAttr);
1322         if (deviceAttr == null) {
1323             return null;
1324         }
1325 
1326         // Get corresponding audio route
1327         @AudioRoute.AudioRouteType int type = DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.get(
1328                 deviceAttr.getType());
1329         AudioDeviceInfo currentCommunicationDevice = null;
1330         if (mFeatureFlags.updatePreferredAudioDeviceLogic()) {
1331             currentCommunicationDevice = getCurrentCommunicationDevice();
1332         }
1333         // We will default to TYPE_INVALID if the currentCommunicationDevice is null or the type
1334         // cannot be resolved from the given audio device info.
1335         int communicationDeviceAudioType = getAudioType(currentCommunicationDevice);
1336         // Sync the preferred device strategy with the current communication device if there's a
1337         // valid audio device output set as the preferred device strategy. This will address timing
1338         // issues between updates made to the preferred device strategy. From the audio fwk
1339         // standpoint, updates to the communication device take precedent to changes in the
1340         // preferred device strategy so the former should be used as the source of truth.
1341         if (type != TYPE_INVALID && communicationDeviceAudioType != TYPE_INVALID
1342                 && communicationDeviceAudioType != type) {
1343             type = communicationDeviceAudioType;
1344         }
1345         if (BT_AUDIO_ROUTE_TYPES.contains(type)) {
1346             return getBluetoothRoute(type, deviceAttr.getAddress());
1347         } else {
1348             return mTypeRoutes.get(type);
1349         }
1350     }
1351 
getPreferredDeviceForStrategy()1352     private AudioDeviceAttributes getPreferredDeviceForStrategy() {
1353         // Get audio produce strategy
1354         AudioProductStrategy strategy = null;
1355         final AudioAttributes attr = new AudioAttributes.Builder()
1356                 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
1357                 .build();
1358         List<AudioProductStrategy> strategies = AudioManager.getAudioProductStrategies();
1359         for (AudioProductStrategy s : strategies) {
1360             if (s.supportsAudioAttributes(attr)) {
1361                 strategy = s;
1362             }
1363         }
1364         if (strategy == null) {
1365             return null;
1366         }
1367 
1368         return mAudioManager.getPreferredDeviceForStrategy(strategy);
1369     }
1370 
getPreferredAudioRouteFromDefault(boolean isExplicitUserRequest, boolean includeBluetooth, String btAddressToExclude)1371     private AudioRoute getPreferredAudioRouteFromDefault(boolean isExplicitUserRequest,
1372             boolean includeBluetooth, String btAddressToExclude) {
1373         boolean skipEarpiece = false;
1374         Call foregroundCall = mCallAudioManager.getForegroundCall();
1375         if (!mFeatureFlags.fixUserRequestBaselineRouteVideoCall()) {
1376             isExplicitUserRequest = false;
1377         }
1378         if (!isExplicitUserRequest) {
1379             synchronized (mTelecomLock) {
1380                 skipEarpiece = foregroundCall != null
1381                         && VideoProfile.isVideo(foregroundCall.getVideoState());
1382             }
1383         }
1384         // Route to earpiece, wired, or speaker route if there are not bluetooth routes or if there
1385         // are only wearables available.
1386         AudioRoute activeWatchOrNonWatchDeviceRoute =
1387                 getActiveWatchOrNonWatchDeviceRoute(btAddressToExclude);
1388         if ((!mCallSupportedRoutes.isEmpty() && (mCallSupportedRouteMask
1389                 & CallAudioState.ROUTE_BLUETOOTH) == 0) || mBluetoothRoutes.isEmpty()
1390                 || !includeBluetooth || activeWatchOrNonWatchDeviceRoute == null) {
1391             Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to "
1392                     + "available non-BT route.");
1393             boolean callSupportsEarpieceWiredRoute = mCallSupportedRoutes.isEmpty()
1394                     || mCallSupportedRoutes.contains(mEarpieceWiredRoute);
1395             // If call supported route doesn't contain earpiece/wired/BT, it should have speaker
1396             // enabled. Otherwise, no routes would be supported for the call which should never be
1397             // the case.
1398             AudioRoute defaultRoute = mEarpieceWiredRoute != null && callSupportsEarpieceWiredRoute
1399                     ? mEarpieceWiredRoute
1400                     : mSpeakerDockRoute;
1401             // Ensure that we default to speaker route if we're in a video call, but disregard it if
1402             // a wired headset is plugged in. Also consider the case when we're holding/unholding a
1403             // call. If the route was on speaker mode, ensure that we preserve the route selection.
1404             boolean shouldDefaultSpeaker = mFeatureFlags.maybeDefaultSpeakerAfterUnhold()
1405                     && mWasOnSpeaker;
1406             if ((skipEarpiece || shouldDefaultSpeaker) && defaultRoute != null
1407                     && defaultRoute.getType() == AudioRoute.TYPE_EARPIECE) {
1408                 Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to "
1409                         + "speaker route for (video) call.");
1410                 defaultRoute = mSpeakerDockRoute;
1411             }
1412             return defaultRoute;
1413         } else {
1414             // Most recent active route will always be the last in the array (ensure that we don't
1415             // auto route to a wearable device unless it's already active).
1416             String autoRoutingToWatchExcerpt = mFeatureFlags.ignoreAutoRouteToWatchDevice()
1417                     ? " (except watch)"
1418                     : "";
1419             Log.i(this, "getPreferredAudioRouteFromDefault: Audio routing defaulting to "
1420                     + "most recently active BT route" + autoRoutingToWatchExcerpt + ".");
1421             return activeWatchOrNonWatchDeviceRoute;
1422         }
1423     }
1424 
calculateSupportedRouteMaskInit()1425     private int calculateSupportedRouteMaskInit() {
1426         Log.i(this, "calculateSupportedRouteMaskInit: is wired headset plugged in - %s",
1427                 mWiredHeadsetManager.isPluggedIn());
1428         int routeMask = CallAudioState.ROUTE_SPEAKER;
1429 
1430         if (mWiredHeadsetManager.isPluggedIn()) {
1431             routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
1432         } else {
1433             AudioDeviceInfo[] deviceList = mAudioManager.getDevices(
1434                     AudioManager.GET_DEVICES_OUTPUTS);
1435             for (AudioDeviceInfo device: deviceList) {
1436                 if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
1437                     routeMask |= CallAudioState.ROUTE_EARPIECE;
1438                     break;
1439                 }
1440             }
1441         }
1442         return routeMask;
1443     }
1444 
1445     @VisibleForTesting
getAvailableRoutes()1446     public Set<AudioRoute> getAvailableRoutes() {
1447         if (mCurrentRoute.equals(mStreamingRoute)) {
1448             return mStreamingRoutes;
1449         } else {
1450             return mAvailableRoutes;
1451         }
1452     }
1453 
getCallSupportedRoutes()1454     public Set<AudioRoute> getCallSupportedRoutes() {
1455         if (mCurrentRoute.equals(mStreamingRoute)) {
1456             return mStreamingRoutes;
1457         } else {
1458             if (mAvailableRoutesUpdated) {
1459                 updateCallSupportedAudioRoutes();
1460                 mAvailableRoutesUpdated = false;
1461             }
1462             return mCallSupportedRoutes.isEmpty() ? mAvailableRoutes : mCallSupportedRoutes;
1463         }
1464     }
1465 
getCurrentRoute()1466     public AudioRoute getCurrentRoute() {
1467         return mCurrentRoute;
1468     }
1469 
getBluetoothRoute(@udioRoute.AudioRouteType int audioRouteType, String address)1470     public AudioRoute getBluetoothRoute(@AudioRoute.AudioRouteType int audioRouteType,
1471             String address) {
1472         for (AudioRoute route : mBluetoothRoutes.keySet()) {
1473             if (route.getType() == audioRouteType && route.getBluetoothAddress().equals(address)) {
1474                 return route;
1475             }
1476         }
1477         return null;
1478     }
1479 
getBaseRoute(boolean includeBluetooth, String btAddressToExclude)1480     public AudioRoute getBaseRoute(boolean includeBluetooth, String btAddressToExclude) {
1481         // Catch-all case for all invocations to this method where we shouldn't be using
1482         // getPreferredAudioRouteFromStrategy
1483         if (mFeatureFlags.updatePreferredAudioDeviceLogic() && !mUsePreferredDeviceStrategy) {
1484             return calculateBaselineRoute(false, includeBluetooth, btAddressToExclude);
1485         }
1486         AudioRoute destRoute = getPreferredAudioRouteFromStrategy();
1487         Log.i(this, "getBaseRoute: preferred audio route is %s", destRoute);
1488         if (destRoute == null || (destRoute.getBluetoothAddress() != null && (!includeBluetooth
1489                 || destRoute.getBluetoothAddress().equals(btAddressToExclude)))) {
1490             destRoute = getPreferredAudioRouteFromDefault(false, includeBluetooth, btAddressToExclude);
1491         }
1492         if (destRoute != null && !getCallSupportedRoutes().contains(destRoute)) {
1493             destRoute = null;
1494         }
1495         Log.i(this, "getBaseRoute - audio routing to %s", destRoute);
1496         return destRoute;
1497     }
1498 
calculateBaselineRoute(boolean isExplicitUserRequest, boolean includeBluetooth, String btAddressToExclude)1499     private AudioRoute calculateBaselineRoute(boolean isExplicitUserRequest,
1500             boolean includeBluetooth, String btAddressToExclude) {
1501         AudioRoute destRoute = getPreferredAudioRouteFromDefault(isExplicitUserRequest,
1502                 includeBluetooth, btAddressToExclude);
1503         if (destRoute != null && !getCallSupportedRoutes().contains(destRoute)) {
1504             destRoute = null;
1505         }
1506         Log.i(this, "getBaseRoute - audio routing to %s", destRoute);
1507         return destRoute;
1508     }
1509 
1510     /**
1511      * Don't add additional AudioRoute when a hearing aid pair is detected. The devices have
1512      * separate addresses, so we need to perform explicit handling to ensure we don't
1513      * treat them as two separate devices.
1514      */
containsHearingAidPair(@udioRoute.AudioRouteType int type, BluetoothDevice bluetoothDevice)1515     private boolean containsHearingAidPair(@AudioRoute.AudioRouteType int type,
1516             BluetoothDevice bluetoothDevice) {
1517         // Check if it is a hearing aid pair and skip connecting to the other device in this case.
1518         // Traverse mBluetoothRoutes backwards as the most recently active device will be inserted
1519         // last.
1520         String existingHearingAidAddress = null;
1521         List<AudioRoute> bluetoothRoutes = mBluetoothRoutes.keySet().stream().toList();
1522         for (int i = bluetoothRoutes.size() - 1; i >= 0; i--) {
1523             AudioRoute audioRoute = bluetoothRoutes.get(i);
1524             if (audioRoute.getType() == AudioRoute.TYPE_BLUETOOTH_HA) {
1525                 existingHearingAidAddress = audioRoute.getBluetoothAddress();
1526                 break;
1527             }
1528         }
1529 
1530         // Check that route is for hearing aid and that there exists another hearing aid route
1531         // created for the first device (of the pair) that was connected.
1532         if (type == AudioRoute.TYPE_BLUETOOTH_HA && existingHearingAidAddress != null) {
1533             BluetoothAdapter bluetoothAdapter = mBluetoothRouteManager.getDeviceManager()
1534                     .getBluetoothAdapter();
1535             if (bluetoothAdapter != null) {
1536                 List<BluetoothDevice> activeHearingAids =
1537                         bluetoothAdapter.getActiveDevices(BluetoothProfile.HEARING_AID);
1538                 for (BluetoothDevice hearingAid : activeHearingAids) {
1539                     if (hearingAid != null && hearingAid.getAddress() != null) {
1540                         String address = hearingAid.getAddress();
1541                         if (address.equals(bluetoothDevice.getAddress())
1542                                 || address.equals(existingHearingAidAddress)) {
1543                             Log.i(this, "containsHearingAidPair: Detected a hearing aid "
1544                                     + "pair, ignoring creating a new AudioRoute");
1545                             return true;
1546                         }
1547                     }
1548                 }
1549             }
1550         }
1551         return false;
1552     }
1553 
1554     /**
1555      * Prevent auto routing to a wearable device when calculating the default bluetooth audio route
1556      * to move to. This function ensures that the most recently active non-wearable device is
1557      * selected for routing unless a wearable device has already been identified as an active
1558      * device.
1559      */
getActiveWatchOrNonWatchDeviceRoute(String btAddressToExclude)1560     private AudioRoute getActiveWatchOrNonWatchDeviceRoute(String btAddressToExclude) {
1561         if (!mFeatureFlags.ignoreAutoRouteToWatchDevice()) {
1562             Log.i(this, "getActiveWatchOrNonWatchDeviceRoute: ignore_auto_route_to_watch_device "
1563                     + "flag is disabled. Routing to most recently reported active device.");
1564             return getMostRecentlyActiveBtRoute(btAddressToExclude);
1565         }
1566 
1567         List<AudioRoute> bluetoothRoutes = getAvailableBluetoothDevicesForRouting();
1568         // Traverse the routes from the most recently active recorded devices first.
1569         AudioRoute nonWatchDeviceRoute = null;
1570         for (int i = bluetoothRoutes.size() - 1; i >= 0; i--) {
1571             AudioRoute route = bluetoothRoutes.get(i);
1572             BluetoothDevice device = mBluetoothRoutes.get(route);
1573             // Skip excluded BT address and LE audio if it's not the lead device.
1574             if (route.getBluetoothAddress().equals(btAddressToExclude)
1575                     || isLeAudioNonLeadDeviceOrServiceUnavailable(route.getType(), device)) {
1576                 continue;
1577             }
1578             // Check if the most recently active device is a watch device.
1579             boolean isActiveDevice;
1580             synchronized (mLock) {
1581                 isActiveDevice = mActiveBluetoothDevice != null
1582                     && device.getAddress().equals(mActiveBluetoothDevice.second);
1583             }
1584             if (i == (bluetoothRoutes.size() - 1) && mBluetoothRouteManager.isWatch(device)
1585                     && (device.equals(mCallAudioState.getActiveBluetoothDevice())
1586                     || isActiveDevice)) {
1587                 Log.i(this, "getActiveWatchOrNonWatchDeviceRoute: Routing to active watch - %s",
1588                         bluetoothRoutes.get(0));
1589                 return bluetoothRoutes.get(0);
1590             }
1591             // Record the first occurrence of a non-watch device route if found.
1592             if (!mBluetoothRouteManager.isWatch(device)) {
1593                 nonWatchDeviceRoute = route;
1594                 break;
1595             }
1596         }
1597 
1598         Log.i(this, "Routing to a non-watch device - %s", nonWatchDeviceRoute);
1599         return nonWatchDeviceRoute;
1600     }
1601 
getAvailableBluetoothDevicesForRouting()1602     private List<AudioRoute> getAvailableBluetoothDevicesForRouting() {
1603         List<AudioRoute> bluetoothRoutes = new ArrayList<>(mBluetoothRoutes.keySet());
1604         if (!mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
1605             return bluetoothRoutes;
1606         }
1607         // Consider the active device (BT_ACTIVE_DEVICE_PRESENT) if it exists first.
1608         AudioRoute activeDeviceRoute = getArbitraryBluetoothDevice();
1609         if (activeDeviceRoute != null && (bluetoothRoutes.isEmpty()
1610                 || !bluetoothRoutes.get(bluetoothRoutes.size() - 1).equals(activeDeviceRoute))) {
1611             Log.i(this, "getActiveWatchOrNonWatchDeviceRoute: active BT device (%s) present."
1612                     + "Considering this device for selection first.", activeDeviceRoute);
1613             bluetoothRoutes.add(activeDeviceRoute);
1614         }
1615         return bluetoothRoutes;
1616     }
1617 
1618     /**
1619      * Returns the most actively reported bluetooth route excluding the passed in route.
1620      */
getMostRecentlyActiveBtRoute(String btAddressToExclude)1621     private AudioRoute getMostRecentlyActiveBtRoute(String btAddressToExclude) {
1622         List<AudioRoute> bluetoothRoutes = mBluetoothRoutes.keySet().stream().toList();
1623         for (int i = bluetoothRoutes.size() - 1; i >= 0; i--) {
1624             AudioRoute route = bluetoothRoutes.get(i);
1625             // Skip LE route if it's not the lead device.
1626             if (isLeAudioNonLeadDeviceOrServiceUnavailable(
1627                     route.getType(), mBluetoothRoutes.get(route))) {
1628                 continue;
1629             }
1630             if (!route.getBluetoothAddress().equals(btAddressToExclude)) {
1631                 return route;
1632             }
1633         }
1634         return null;
1635     }
1636 
isLeAudioNonLeadDeviceOrServiceUnavailable(@udioRoute.AudioRouteType int type, BluetoothDevice device)1637     private boolean isLeAudioNonLeadDeviceOrServiceUnavailable(@AudioRoute.AudioRouteType int type,
1638             BluetoothDevice device) {
1639         BluetoothLeAudio leAudioService = getLeAudioService();
1640         if (type != AudioRoute.TYPE_BLUETOOTH_LE) {
1641             return false;
1642         } else if (leAudioService == null) {
1643             return true;
1644         }
1645 
1646         int groupId = leAudioService.getGroupId(device);
1647         if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
1648             BluetoothDevice leadDevice = leAudioService.getConnectedGroupLeadDevice(groupId);
1649             Log.i(this, "Lead device for device (%s) is %s.", device, leadDevice);
1650             return leadDevice == null || !device.getAddress().equals(leadDevice.getAddress());
1651         }
1652         return false;
1653     }
1654 
getLeAudioService()1655     private BluetoothLeAudio getLeAudioService() {
1656         return mBluetoothRouteManager.getDeviceManager().getLeAudioService();
1657     }
1658 
1659     @VisibleForTesting
setAudioManager(AudioManager audioManager)1660     public void setAudioManager(AudioManager audioManager) {
1661         mAudioManager = audioManager;
1662     }
1663 
1664     @VisibleForTesting
setAudioRouteFactory(AudioRoute.Factory audioRouteFactory)1665     public void setAudioRouteFactory(AudioRoute.Factory audioRouteFactory) {
1666         mAudioRouteFactory = audioRouteFactory;
1667     }
1668 
getBluetoothRoutes()1669     public Map<AudioRoute, BluetoothDevice> getBluetoothRoutes() {
1670         return mBluetoothRoutes;
1671     }
1672 
overrideIsPending(boolean isPending)1673     public void overrideIsPending(boolean isPending) {
1674         mIsPending = isPending;
1675     }
1676 
1677     @VisibleForTesting
setScoAudioConnectedDevice(BluetoothDevice device)1678     public void setScoAudioConnectedDevice(BluetoothDevice device) {
1679         mScoAudioConnectedDevice = device;
1680     }
1681 
clearRingingBluetoothAddress()1682     private void clearRingingBluetoothAddress() {
1683         mBluetoothAddressForRinging = null;
1684     }
1685 
1686     /**
1687      * Update the active bluetooth device being tracked (as well as for individual profiles).
1688      * We need to keep track of active devices for individual profiles because of potential
1689      * inconsistencies found in BluetoothStateReceiver#handleActiveDeviceChanged. When multiple
1690      * profiles are paired, we could have a scenario where an active device A is replaced
1691      * with an active device B (from a different profile), which is then removed as an active
1692      * device shortly after, causing device A to be reactive. It's possible that the active device
1693      * changed intent is never received again for device A so an active device cache is necessary
1694      * to track these devices at a profile level.
1695      * @param device {@link Pair} containing the BT audio route type (i.e. SCO/HA/LE) and the
1696      *                           address of the device.
1697      */
updateActiveBluetoothDevice(Pair<Integer, String> device)1698     public void updateActiveBluetoothDevice(Pair<Integer, String> device) {
1699         synchronized (mLock) {
1700             mActiveDeviceCache.put(device.first, device.second);
1701             // Update most recently active device if address isn't null (meaning
1702             // some device is active).
1703             if (device.second != null) {
1704                 mActiveBluetoothDevice = device;
1705             } else {
1706                 // If a device was removed, check to ensure that no other device is
1707                 //still considered active.
1708                 boolean hasActiveDevice = false;
1709                 List<Map.Entry<Integer, String>> activeBtDevices =
1710                         new ArrayList<>(mActiveDeviceCache.entrySet());
1711                 for (Map.Entry<Integer, String> activeDevice : activeBtDevices) {
1712                     Integer btAudioType = activeDevice.getKey();
1713                     String address = activeDevice.getValue();
1714                     if (address != null) {
1715                         hasActiveDevice = true;
1716                         if (mFeatureFlags.resolveActiveBtRoutingAndBtTimingIssue()) {
1717                             mActiveBluetoothDevice = new Pair<>(btAudioType, address);
1718                         }
1719                         break;
1720                     }
1721                 }
1722                 if (!hasActiveDevice) {
1723                     mActiveBluetoothDevice = null;
1724                 }
1725             }
1726         }
1727     }
1728 
updateAvailableRoutes(AudioRoute route, boolean includeRoute)1729     private void updateAvailableRoutes(AudioRoute route, boolean includeRoute) {
1730         if (includeRoute) {
1731             mAvailableRoutes.add(route);
1732         } else {
1733             mAvailableRoutes.remove(route);
1734         }
1735         mAvailableRoutesUpdated = true;
1736     }
1737 
1738     @VisibleForTesting
setActive(boolean active)1739     public void setActive(boolean active) {
1740         if (active) {
1741             mFocusType = ACTIVE_FOCUS;
1742         } else {
1743             mFocusType = NO_FOCUS;
1744         }
1745         mIsActive = active;
1746     }
1747 
fallBack(String btAddressToExclude)1748     void fallBack(String btAddressToExclude) {
1749         mMetricsController.getAudioRouteStats().onRouteExit(mPendingAudioRoute, false);
1750         sendMessageWithSessionInfo(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE,
1751                 btAddressToExclude);
1752     }
1753 
getAudioOperationsCompleteLatch()1754     public CountDownLatch getAudioOperationsCompleteLatch() {
1755         return mAudioOperationsCompleteLatch;
1756     }
1757 
getAudioActiveCompleteLatch()1758     public CountDownLatch getAudioActiveCompleteLatch() {
1759         return mAudioActiveCompleteLatch;
1760     }
1761 
getAudioType(AudioDeviceInfo device)1762     private @AudioRoute.AudioRouteType int getAudioType(AudioDeviceInfo device) {
1763         return device != null
1764                 ? DEVICE_INFO_TYPE_TO_AUDIO_ROUTE_TYPE.getOrDefault(
1765                 device.getType(), TYPE_INVALID)
1766                 : TYPE_INVALID;
1767     }
1768 
1769     @VisibleForTesting
getUsePreferredDeviceStrategy()1770     public boolean getUsePreferredDeviceStrategy() {
1771         return mUsePreferredDeviceStrategy;
1772     }
1773 
1774     @VisibleForTesting
setCurrentCommunicationDevice(AudioDeviceInfo device)1775     public void setCurrentCommunicationDevice(AudioDeviceInfo device) {
1776         synchronized (mLock) {
1777             mCurrentCommunicationDevice = device;
1778         }
1779     }
1780 
getCurrentCommunicationDevice()1781     public AudioDeviceInfo getCurrentCommunicationDevice() {
1782         synchronized (mLock) {
1783             return mCurrentCommunicationDevice;
1784         }
1785     }
1786 
maybeDisableWasOnSpeaker(boolean isUserRequest)1787     private void maybeDisableWasOnSpeaker(boolean isUserRequest) {
1788         if (isUserRequest) {
1789             mWasOnSpeaker = false;
1790         }
1791     }
1792 
1793     /*
1794      * Adjusts routing to go inactive if we're active in the case that we're processing
1795      * RINGING_FOCUS and another BT headset is connected which causes in-band ringing to get
1796      * disabled. If we stay in active routing, Telecom will send requests to connect to these BT
1797      * devices while the call is ringing and each of these requests will fail at the BT stack side.
1798      * By default, in-band ringtone is disabled when more than one BT device is paired. Instead,
1799      * ringtone is played using the headset's default ringtone.
1800      */
maybeAdjustActiveRouting(AudioRoute destRoute, boolean isDestRouteActive)1801     private boolean maybeAdjustActiveRouting(AudioRoute destRoute, boolean isDestRouteActive) {
1802         BluetoothDevice device = mBluetoothRoutes.get(destRoute);
1803         // If routing is active and in-band ringing is disabled while the call is ringing, move to
1804         // inactive routing.
1805         if (isDestRouteActive && mFocusType == RINGING_FOCUS && device != null
1806                 && !mBluetoothRouteManager.isInbandRingEnabled(destRoute.getType(), device)) {
1807             return false;
1808         }
1809         else if (!isDestRouteActive && mFocusType == RINGING_FOCUS && (device == null
1810                 || mBluetoothRouteManager.isInbandRingEnabled(destRoute.getType(), device))) {
1811             // If the routing is inactive while the call is ringing and we re-evaluate this to find
1812             // that we're routing to a non-BT device or a BT device that does support in-band
1813             // ringing, then re-enable active routing (i.e. second HFP headset is disconnected
1814             // while call is ringing).
1815             return true;
1816         }
1817         return isDestRouteActive;
1818     }
1819 }
1820