blob: c7cc1bd88bdfc0a6ffe5ef0604248b93ec7b2d97 [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 /**
Hall Liua98f58b52017-11-07 17:59:28 -0800202 * @return The {@link BluetoothDevice} through which audio is being routed.
203 * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
204 */
205 public BluetoothDevice getActiveBluetoothDevice() {
206 return activeBluetoothDevice;
207 }
208
209 /**
210 * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
211 */
212 public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
213 return supportedBluetoothDevices;
214 }
215
216 /**
Yorke Lee4af59352015-05-13 14:14:54 -0700217 * Converts the provided audio route into a human readable string representation.
218 *
219 * @param route to convert into a string.
220 *
221 * @return String representation of the provided audio route.
222 */
223 public static String audioRouteToString(int route) {
224 if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
225 return "UNKNOWN";
226 }
227
228 StringBuffer buffer = new StringBuffer();
229 if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
230 listAppend(buffer, "EARPIECE");
231 }
232 if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
233 listAppend(buffer, "BLUETOOTH");
234 }
235 if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
236 listAppend(buffer, "WIRED_HEADSET");
237 }
238 if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
239 listAppend(buffer, "SPEAKER");
240 }
Grace Jiaef5a4cc2022-12-13 11:08:55 -0800241 if ((route & ROUTE_STREAMING) == ROUTE_STREAMING) {
242 listAppend(buffer, "STREAMING");
243 }
Yorke Lee4af59352015-05-13 14:14:54 -0700244
245 return buffer.toString();
246 }
247
248 /**
249 * Responsible for creating AudioState objects for deserialized Parcels.
250 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700251 public static final @android.annotation.NonNull Parcelable.Creator<CallAudioState> CREATOR =
Yorke Lee4af59352015-05-13 14:14:54 -0700252 new Parcelable.Creator<CallAudioState> () {
253
254 @Override
255 public CallAudioState createFromParcel(Parcel source) {
256 boolean isMuted = source.readByte() == 0 ? false : true;
257 int route = source.readInt();
258 int supportedRouteMask = source.readInt();
Hall Liua98f58b52017-11-07 17:59:28 -0800259 BluetoothDevice activeBluetoothDevice = source.readParcelable(
Bernardo Rufino1a5cb382022-01-14 17:35:36 +0000260 ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
Hall Liua98f58b52017-11-07 17:59:28 -0800261 List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
262 source.readParcelableList(supportedBluetoothDevices,
Bernardo Rufino1a5cb382022-01-14 17:35:36 +0000263 ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
Hall Liua98f58b52017-11-07 17:59:28 -0800264 return new CallAudioState(isMuted, route,
265 supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
Yorke Lee4af59352015-05-13 14:14:54 -0700266 }
267
268 @Override
269 public CallAudioState[] newArray(int size) {
270 return new CallAudioState[size];
271 }
272 };
273
274 /**
275 * {@inheritDoc}
276 */
277 @Override
278 public int describeContents() {
279 return 0;
280 }
281
282 /**
283 * Writes AudioState object into a serializeable Parcel.
284 */
285 @Override
286 public void writeToParcel(Parcel destination, int flags) {
287 destination.writeByte((byte) (isMuted ? 1 : 0));
288 destination.writeInt(route);
289 destination.writeInt(supportedRouteMask);
Hall Liua98f58b52017-11-07 17:59:28 -0800290 destination.writeParcelable(activeBluetoothDevice, 0);
291 destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
Yorke Lee4af59352015-05-13 14:14:54 -0700292 }
293
294 private static void listAppend(StringBuffer buffer, String str) {
295 if (buffer.length() > 0) {
296 buffer.append(", ");
297 }
298 buffer.append(str);
299 }
300}