blob: 49e9232ad535d0f5aefdfd360ade848159847b6f [file] [log] [blame]
Yorke Lee4af59352015-05-13 14:14:54 -07001/*
2 * Copyright (C) 2014 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
17package android.telecom;
18
Hall Liua98f58b52017-11-07 17:59:28 -080019import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
Hall Liuc1d95aa2018-05-11 17:14:08 -070022import android.annotation.TestApi;
Hall Liua98f58b52017-11-07 17:59:28 -080023import android.bluetooth.BluetoothDevice;
Yorke Lee4af59352015-05-13 14:14:54 -070024import android.os.Parcel;
25import android.os.Parcelable;
26
Hall Liua98f58b52017-11-07 17:59:28 -080027import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.util.ArrayList;
Hall Liua98f58b52017-11-07 17:59:28 -080030import java.util.Collection;
31import java.util.Collections;
32import java.util.List;
Yorke Lee4af59352015-05-13 14:14:54 -070033import java.util.Locale;
Hall Liua98f58b52017-11-07 17:59:28 -080034import java.util.Objects;
35import java.util.stream.Collectors;
Yorke Lee4af59352015-05-13 14:14:54 -070036
37/**
38 * Encapsulates the telecom audio state, including the current audio routing, supported audio
39 * routing and mute.
40 */
41public final class CallAudioState implements Parcelable {
Hall Liua98f58b52017-11-07 17:59:28 -080042 /** @hide */
43 @Retention(RetentionPolicy.SOURCE)
Grace Jia88c05c02022-02-17 21:32:21 +000044 @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
Hall Liua98f58b52017-11-07 17:59:28 -080045 flag=true)
46 public @interface CallAudioRoute {}
47
Yorke Lee4af59352015-05-13 14:14:54 -070048 /** Direct the audio stream through the device's earpiece. */
49 public static final int ROUTE_EARPIECE = 0x00000001;
50
51 /** Direct the audio stream through Bluetooth. */
52 public static final int ROUTE_BLUETOOTH = 0x00000002;
53
54 /** Direct the audio stream through a wired headset. */
55 public static final int ROUTE_WIRED_HEADSET = 0x00000004;
56
57 /** Direct the audio stream through the device's speakerphone. */
58 public static final int ROUTE_SPEAKER = 0x00000008;
59
Grace Jiaef5a4cc2022-12-13 11:08:55 -080060 /** Direct the audio stream through another device. */
61 public static final int ROUTE_STREAMING = 0x00000010;
62
Yorke Lee4af59352015-05-13 14:14:54 -070063 /**
64 * Direct the audio stream through the device's earpiece or wired headset if one is
65 * connected.
66 */
67 public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
68
Christine Hallstrom4e22d6d2016-11-30 16:06:42 -080069 /**
70 * Bit mask of all possible audio routes.
71 *
72 * @hide
73 **/
74 public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
Grace Jiaef5a4cc2022-12-13 11:08:55 -080075 ROUTE_SPEAKER | ROUTE_STREAMING;
Yorke Lee4af59352015-05-13 14:14:54 -070076
77 private final boolean isMuted;
78 private final int route;
79 private final int supportedRouteMask;
Hall Liua98f58b52017-11-07 17:59:28 -080080 private final BluetoothDevice activeBluetoothDevice;
81 private final Collection<BluetoothDevice> supportedBluetoothDevices;
Yorke Lee4af59352015-05-13 14:14:54 -070082
83 /**
84 * Constructor for a {@link CallAudioState} object.
85 *
86 * @param muted {@code true} if the call is muted, {@code false} otherwise.
87 * @param route The current audio route being used.
88 * Allowed values:
89 * {@link #ROUTE_EARPIECE}
90 * {@link #ROUTE_BLUETOOTH}
91 * {@link #ROUTE_WIRED_HEADSET}
92 * {@link #ROUTE_SPEAKER}
93 * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a
94 * bitwise combination of the following values:
95 * {@link #ROUTE_EARPIECE}
96 * {@link #ROUTE_BLUETOOTH}
97 * {@link #ROUTE_WIRED_HEADSET}
98 * {@link #ROUTE_SPEAKER}
99 */
Hall Liua98f58b52017-11-07 17:59:28 -0800100 public CallAudioState(boolean muted, @CallAudioRoute int route,
101 @CallAudioRoute int supportedRouteMask) {
102 this(muted, route, supportedRouteMask, null, Collections.emptyList());
103 }
104
105 /** @hide */
Hall Liuc1d95aa2018-05-11 17:14:08 -0700106 @TestApi
Hall Liua98f58b52017-11-07 17:59:28 -0800107 public CallAudioState(boolean isMuted, @CallAudioRoute int route,
108 @CallAudioRoute int supportedRouteMask,
109 @Nullable BluetoothDevice activeBluetoothDevice,
110 @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
111 this.isMuted = isMuted;
Yorke Lee4af59352015-05-13 14:14:54 -0700112 this.route = route;
113 this.supportedRouteMask = supportedRouteMask;
Hall Liua98f58b52017-11-07 17:59:28 -0800114 this.activeBluetoothDevice = activeBluetoothDevice;
115 this.supportedBluetoothDevices = supportedBluetoothDevices;
Yorke Lee4af59352015-05-13 14:14:54 -0700116 }
117
118 /** @hide */
119 public CallAudioState(CallAudioState state) {
120 isMuted = state.isMuted();
121 route = state.getRoute();
122 supportedRouteMask = state.getSupportedRouteMask();
Hall Liua98f58b52017-11-07 17:59:28 -0800123 activeBluetoothDevice = state.activeBluetoothDevice;
124 supportedBluetoothDevices = state.getSupportedBluetoothDevices();
Yorke Lee4af59352015-05-13 14:14:54 -0700125 }
126
127 /** @hide */
128 @SuppressWarnings("deprecation")
129 public CallAudioState(AudioState state) {
130 isMuted = state.isMuted();
131 route = state.getRoute();
132 supportedRouteMask = state.getSupportedRouteMask();
Hall Liua98f58b52017-11-07 17:59:28 -0800133 activeBluetoothDevice = null;
134 supportedBluetoothDevices = Collections.emptyList();
Yorke Lee4af59352015-05-13 14:14:54 -0700135 }
136
137 @Override
138 public boolean equals(Object obj) {
139 if (obj == null) {
140 return false;
141 }
142 if (!(obj instanceof CallAudioState)) {
143 return false;
144 }
145 CallAudioState state = (CallAudioState) obj;
Hall Liua98f58b52017-11-07 17:59:28 -0800146 if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
147 return false;
148 }
149 for (BluetoothDevice device : supportedBluetoothDevices) {
150 if (!state.supportedBluetoothDevices.contains(device)) {
151 return false;
152 }
153 }
154 return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
155 state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
156 state.getSupportedRouteMask();
Yorke Lee4af59352015-05-13 14:14:54 -0700157 }
158
159 @Override
160 public String toString() {
Hall Liua98f58b52017-11-07 17:59:28 -0800161 String bluetoothDeviceList = supportedBluetoothDevices.stream()
162 .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
163
Yorke Lee4af59352015-05-13 14:14:54 -0700164 return String.format(Locale.US,
Hall Liua98f58b52017-11-07 17:59:28 -0800165 "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
166 "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
Yorke Lee4af59352015-05-13 14:14:54 -0700167 isMuted,
168 audioRouteToString(route),
Hall Liua98f58b52017-11-07 17:59:28 -0800169 audioRouteToString(supportedRouteMask),
170 activeBluetoothDevice,
171 bluetoothDeviceList);
Yorke Lee4af59352015-05-13 14:14:54 -0700172 }
173
174 /**
175 * @return {@code true} if the call is muted, {@code false} otherwise.
176 */
177 public boolean isMuted() {
178 return isMuted;
179 }
180
181 /**
182 * @return The current audio route being used.
183 */
Hall Liua98f58b52017-11-07 17:59:28 -0800184 @CallAudioRoute
Yorke Lee4af59352015-05-13 14:14:54 -0700185 public int getRoute() {
186 return route;
187 }
188
189 /**
190 * @return Bit mask of all routes supported by this call.
191 */
Hall Liua98f58b52017-11-07 17:59:28 -0800192 @CallAudioRoute
Yorke Lee4af59352015-05-13 14:14:54 -0700193 public int getSupportedRouteMask() {
Grace Jiaef5a4cc2022-12-13 11:08:55 -0800194 if (route == ROUTE_STREAMING) {
195 return ROUTE_STREAMING;
196 } else {
197 return supportedRouteMask;
198 }
Yorke Lee4af59352015-05-13 14:14:54 -0700199 }
200
201 /**
xiaotonjf61f8e72023-12-06 16:08:55 -0800202 * @return Bit mask of all routes supported by this call, won't be changed by streaming state.
203 *
204 * @hide
205 */
206 @CallAudioRoute
207 public int getRawSupportedRouteMask() {
208 return supportedRouteMask;
209 }
210
211 /**
Hall Liua98f58b52017-11-07 17:59:28 -0800212 * @return The {@link BluetoothDevice} through which audio is being routed.
213 * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
214 */
215 public BluetoothDevice getActiveBluetoothDevice() {
216 return activeBluetoothDevice;
217 }
218
219 /**
220 * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
221 */
222 public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
223 return supportedBluetoothDevices;
224 }
225
226 /**
Yorke Lee4af59352015-05-13 14:14:54 -0700227 * Converts the provided audio route into a human readable string representation.
228 *
229 * @param route to convert into a string.
230 *
231 * @return String representation of the provided audio route.
232 */
233 public static String audioRouteToString(int route) {
234 if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
235 return "UNKNOWN";
236 }
237
238 StringBuffer buffer = new StringBuffer();
239 if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
240 listAppend(buffer, "EARPIECE");
241 }
242 if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
243 listAppend(buffer, "BLUETOOTH");
244 }
245 if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
246 listAppend(buffer, "WIRED_HEADSET");
247 }
248 if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
249 listAppend(buffer, "SPEAKER");
250 }
Grace Jiaef5a4cc2022-12-13 11:08:55 -0800251 if ((route & ROUTE_STREAMING) == ROUTE_STREAMING) {
252 listAppend(buffer, "STREAMING");
253 }
Yorke Lee4af59352015-05-13 14:14:54 -0700254
255 return buffer.toString();
256 }
257
258 /**
259 * Responsible for creating AudioState objects for deserialized Parcels.
260 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700261 public static final @android.annotation.NonNull Parcelable.Creator<CallAudioState> CREATOR =
Yorke Lee4af59352015-05-13 14:14:54 -0700262 new Parcelable.Creator<CallAudioState> () {
263
264 @Override
265 public CallAudioState createFromParcel(Parcel source) {
266 boolean isMuted = source.readByte() == 0 ? false : true;
267 int route = source.readInt();
268 int supportedRouteMask = source.readInt();
Hall Liua98f58b52017-11-07 17:59:28 -0800269 BluetoothDevice activeBluetoothDevice = source.readParcelable(
Bernardo Rufino1a5cb382022-01-14 17:35:36 +0000270 ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
Hall Liua98f58b52017-11-07 17:59:28 -0800271 List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
272 source.readParcelableList(supportedBluetoothDevices,
Bernardo Rufino1a5cb382022-01-14 17:35:36 +0000273 ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
Hall Liua98f58b52017-11-07 17:59:28 -0800274 return new CallAudioState(isMuted, route,
275 supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
Yorke Lee4af59352015-05-13 14:14:54 -0700276 }
277
278 @Override
279 public CallAudioState[] newArray(int size) {
280 return new CallAudioState[size];
281 }
282 };
283
284 /**
285 * {@inheritDoc}
286 */
287 @Override
288 public int describeContents() {
289 return 0;
290 }
291
292 /**
293 * Writes AudioState object into a serializeable Parcel.
294 */
295 @Override
296 public void writeToParcel(Parcel destination, int flags) {
297 destination.writeByte((byte) (isMuted ? 1 : 0));
298 destination.writeInt(route);
299 destination.writeInt(supportedRouteMask);
Hall Liua98f58b52017-11-07 17:59:28 -0800300 destination.writeParcelable(activeBluetoothDevice, 0);
301 destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
Yorke Lee4af59352015-05-13 14:14:54 -0700302 }
303
304 private static void listAppend(StringBuffer buffer, String str) {
305 if (buffer.length() > 0) {
306 buffer.append(", ");
307 }
308 buffer.append(str);
309 }
310}