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.CallAudioRouteAdapter.PENDING_ROUTE_FAILED; 20 import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_ROUTE; 21 import static com.android.server.telecom.CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE; 22 23 import android.bluetooth.BluetoothDevice; 24 import android.media.AudioManager; 25 import android.telecom.Log; 26 import android.util.ArraySet; 27 import android.util.Pair; 28 29 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 30 31 import java.util.Set; 32 33 /** 34 * Used to represent the intermediate state during audio route switching. 35 * Usually, audio route switching start with a communication device setting request to audio 36 * framework and will be completed with corresponding success broadcasts or messages. Instance of 37 * this class is responsible for tracking the pending success signals according to the original 38 * audio route and the destination audio route of this switching. 39 */ 40 public class PendingAudioRoute { 41 private CallAudioRouteController mCallAudioRouteController; 42 private AudioManager mAudioManager; 43 private BluetoothRouteManager mBluetoothRouteManager; 44 /** 45 * The {@link AudioRoute} that this pending audio switching started with 46 */ 47 private AudioRoute mOrigRoute; 48 /** 49 * The expected destination {@link AudioRoute} of this pending audio switching, can be changed 50 * by new switching request during the ongoing switching 51 */ 52 private AudioRoute mDestRoute; 53 private Set<Pair<Integer, String>> mPendingMessages; 54 private boolean mActive; 55 /** 56 * The device that has been set for communication by Telecom 57 */ 58 private @AudioRoute.AudioRouteType int mCommunicationDeviceType = AudioRoute.TYPE_INVALID; 59 PendingAudioRoute(CallAudioRouteController controller, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager)60 PendingAudioRoute(CallAudioRouteController controller, AudioManager audioManager, 61 BluetoothRouteManager bluetoothRouteManager) { 62 mCallAudioRouteController = controller; 63 mAudioManager = audioManager; 64 mBluetoothRouteManager = bluetoothRouteManager; 65 mPendingMessages = new ArraySet<>(); 66 mActive = false; 67 mCommunicationDeviceType = AudioRoute.TYPE_INVALID; 68 } 69 setOrigRoute(boolean active, AudioRoute origRoute)70 void setOrigRoute(boolean active, AudioRoute origRoute) { 71 origRoute.onOrigRouteAsPendingRoute(active, this, mAudioManager, mBluetoothRouteManager); 72 mOrigRoute = origRoute; 73 } 74 getOrigRoute()75 AudioRoute getOrigRoute() { 76 return mOrigRoute; 77 } 78 setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device, boolean isScoAudioConnected)79 void setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device, 80 boolean isScoAudioConnected) { 81 destRoute.onDestRouteAsPendingRoute(active, this, device, 82 mAudioManager, mBluetoothRouteManager, isScoAudioConnected); 83 mActive = active; 84 mDestRoute = destRoute; 85 } 86 getDestRoute()87 public AudioRoute getDestRoute() { 88 return mDestRoute; 89 } 90 addMessage(int message, String bluetoothDevice)91 public void addMessage(int message, String bluetoothDevice) { 92 mPendingMessages.add(new Pair<>(message, bluetoothDevice)); 93 } 94 onMessageReceived(Pair<Integer, String> message, String btAddressToExclude)95 public void onMessageReceived(Pair<Integer, String> message, String btAddressToExclude) { 96 Log.i(this, "onMessageReceived: message - %s", message); 97 if (message.first == PENDING_ROUTE_FAILED) { 98 // Fallback to base route 99 mCallAudioRouteController.sendMessageWithSessionInfo( 100 SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE, btAddressToExclude); 101 return; 102 } 103 104 // Removes the first occurrence of the specified message from this list, if it is present. 105 mPendingMessages.remove(message); 106 evaluatePendingState(); 107 } 108 evaluatePendingState()109 public void evaluatePendingState() { 110 if (mPendingMessages.isEmpty()) { 111 mCallAudioRouteController.sendMessageWithSessionInfo( 112 CallAudioRouteAdapter.EXIT_PENDING_ROUTE); 113 } else { 114 Log.i(this, "evaluatePendingState: mPendingMessages - %s", mPendingMessages); 115 } 116 } 117 clearPendingMessages()118 public void clearPendingMessages() { 119 mPendingMessages.clear(); 120 } 121 clearPendingMessage(Pair<Integer, String> message)122 public void clearPendingMessage(Pair<Integer, String> message) { 123 mPendingMessages.remove(message); 124 } 125 isActive()126 public boolean isActive() { 127 return mActive; 128 } 129 getCommunicationDeviceType()130 public @AudioRoute.AudioRouteType int getCommunicationDeviceType() { 131 return mCommunicationDeviceType; 132 } 133 setCommunicationDeviceType( @udioRoute.AudioRouteType int communicationDeviceType)134 public void setCommunicationDeviceType( 135 @AudioRoute.AudioRouteType int communicationDeviceType) { 136 mCommunicationDeviceType = communicationDeviceType; 137 } 138 overrideDestRoute(AudioRoute route)139 public void overrideDestRoute(AudioRoute route) { 140 mDestRoute = route; 141 } 142 } 143