• 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.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 import com.android.server.telecom.flags.FeatureFlags;
31 
32 import java.util.Set;
33 
34 /**
35  * Used to represent the intermediate state during audio route switching.
36  * Usually, audio route switching start with a communication device setting request to audio
37  * framework and will be completed with corresponding success broadcasts or messages. Instance of
38  * this class is responsible for tracking the pending success signals according to the original
39  * audio route and the destination audio route of this switching.
40  */
41 public class PendingAudioRoute {
42     private CallAudioRouteController mCallAudioRouteController;
43     private AudioManager mAudioManager;
44     private BluetoothRouteManager mBluetoothRouteManager;
45     private FeatureFlags mFeatureFlags;
46     /**
47      * The {@link AudioRoute} that this pending audio switching started with
48      */
49     private AudioRoute mOrigRoute;
50     /**
51      * The expected destination {@link AudioRoute} of this pending audio switching, can be changed
52      * by new switching request during the ongoing switching
53      */
54     private AudioRoute mDestRoute;
55     private Set<Pair<Integer, String>> mPendingMessages;
56     private boolean mActive;
57     /**
58      * The device that has been set for communication by Telecom
59      */
60     private @AudioRoute.AudioRouteType int mCommunicationDeviceType = AudioRoute.TYPE_INVALID;
61 
PendingAudioRoute(CallAudioRouteController controller, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager, FeatureFlags featureFlags)62     PendingAudioRoute(CallAudioRouteController controller, AudioManager audioManager,
63             BluetoothRouteManager bluetoothRouteManager, FeatureFlags featureFlags) {
64         mCallAudioRouteController = controller;
65         mAudioManager = audioManager;
66         mBluetoothRouteManager = bluetoothRouteManager;
67         mFeatureFlags = featureFlags;
68         mPendingMessages = new ArraySet<>();
69         mActive = false;
70         mCommunicationDeviceType = AudioRoute.TYPE_INVALID;
71     }
72 
73     /**
74      * Sets the originating route information, and begins the process of transitioning OUT of the
75      * originating route.
76      * Note: We also pass in whether the destination route is going to be active.  This is so that
77      * {@link AudioRoute#onOrigRouteAsPendingRoute(boolean, PendingAudioRoute, AudioManager,
78      * BluetoothRouteManager)} knows whether or not the destination route will be active or not and
79      * can determine whether or not it needs to call {@link AudioManager#clearCommunicationDevice()}
80      * or not.  To optimize audio performance we only need to clear the communication device if the
81      * end result is going to be that we are in an inactive state.
82      * @param isOriginActive Whether the origin is active.
83      * @param origRoute The origin.
84      * @param isDestActive Whether the destination will be active.
85      */
setOrigRoute(boolean isOriginActive, AudioRoute origRoute, boolean isDestActive, boolean isScoAlreadyConnected)86     void setOrigRoute(boolean isOriginActive, AudioRoute origRoute, boolean isDestActive,
87             boolean isScoAlreadyConnected) {
88         mActive = isDestActive;
89         origRoute.onOrigRouteAsPendingRoute(isOriginActive, this, mAudioManager,
90                 mBluetoothRouteManager, isScoAlreadyConnected);
91         mOrigRoute = origRoute;
92     }
93 
getOrigRoute()94     public AudioRoute getOrigRoute() {
95         return mOrigRoute;
96     }
97 
setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device, boolean isScoAlreadyConnected)98     void setDestRoute(boolean active, AudioRoute destRoute, BluetoothDevice device,
99             boolean isScoAlreadyConnected) {
100         destRoute.onDestRouteAsPendingRoute(active, this, device,
101                 mAudioManager, mBluetoothRouteManager, isScoAlreadyConnected);
102         mActive = active;
103         mDestRoute = destRoute;
104     }
105 
getDestRoute()106     public AudioRoute getDestRoute() {
107         return mDestRoute;
108     }
109 
addMessage(int message, String bluetoothDevice)110     public void addMessage(int message, String bluetoothDevice) {
111         mPendingMessages.add(new Pair<>(message, bluetoothDevice));
112     }
113 
onMessageReceived(Pair<Integer, String> message, String btAddressToExclude)114     public void onMessageReceived(Pair<Integer, String> message, String btAddressToExclude) {
115         Log.i(this, "onMessageReceived: message - %s", message);
116         if (message.first == PENDING_ROUTE_FAILED) {
117             // Fallback to base route
118             if (mFeatureFlags.telecomMetricsSupport()) {
119                 mCallAudioRouteController.fallBack(btAddressToExclude);
120             } else {
121                 mCallAudioRouteController.sendMessageWithSessionInfo(
122                         SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE, btAddressToExclude);
123             }
124             return;
125         }
126 
127         // Removes the first occurrence of the specified message from this list, if it is present.
128         mPendingMessages.remove(message);
129         evaluatePendingState();
130     }
131 
evaluatePendingState()132     public void evaluatePendingState() {
133         if (mPendingMessages.isEmpty()) {
134             mCallAudioRouteController.sendMessageWithSessionInfo(
135                     CallAudioRouteAdapter.EXIT_PENDING_ROUTE);
136         } else {
137             Log.i(this, "evaluatePendingState: mPendingMessages - %s", mPendingMessages);
138         }
139     }
140 
clearPendingMessages()141     public void clearPendingMessages() {
142         mPendingMessages.clear();
143     }
144 
clearPendingMessage(Pair<Integer, String> message)145     public void clearPendingMessage(Pair<Integer, String> message) {
146         mPendingMessages.remove(message);
147     }
148 
getPendingMessages()149     public Set<Pair<Integer, String>> getPendingMessages() {
150         return mPendingMessages;
151     }
152 
153     /**
154      * Whether the destination {@link #getDestRoute()} will be active or not.
155      * @return {@code true} if destination will be active, {@code false} otherwise.
156      */
isActive()157     public boolean isActive() {
158         return mActive;
159     }
160 
getCommunicationDeviceType()161     public @AudioRoute.AudioRouteType int getCommunicationDeviceType() {
162         return mCommunicationDeviceType;
163     }
164 
setCommunicationDeviceType( @udioRoute.AudioRouteType int communicationDeviceType)165     public void setCommunicationDeviceType(
166             @AudioRoute.AudioRouteType int communicationDeviceType) {
167         mCommunicationDeviceType = communicationDeviceType;
168     }
169 
overrideDestRoute(AudioRoute route)170     public void overrideDestRoute(AudioRoute route) {
171         mDestRoute = route;
172     }
173 
getFeatureFlags()174     public FeatureFlags getFeatureFlags() {
175         return mFeatureFlags;
176     }
177 
178     @Override
toString()179     public String toString() {
180         return "PendingAudioRoute{" +
181                 ", mOrigRoute=" + mOrigRoute +
182                 ", mDestRoute=" + mDestRoute +
183                 ", mActive=" + mActive +
184                 ", mCommunicationDeviceType=" + mCommunicationDeviceType +
185                 '}';
186     }
187 }
188