blob: 389df80497df2bee0b656a7cfe2c9e593c9acc00 [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 Jia22c8b962021-10-28 17:04:29 -070044 @IntDef(value = {ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER,
45 ROUTE_EXTERNAL},
Hall Liua98f58b52017-11-07 17:59:28 -080046 flag=true)
47 public @interface CallAudioRoute {}
48
Yorke Lee4af59352015-05-13 14:14:54 -070049 /** Direct the audio stream through the device's earpiece. */
50 public static final int ROUTE_EARPIECE = 0x00000001;
51
52 /** Direct the audio stream through Bluetooth. */
53 public static final int ROUTE_BLUETOOTH = 0x00000002;
54
55 /** Direct the audio stream through a wired headset. */
56 public static final int ROUTE_WIRED_HEADSET = 0x00000004;
57
58 /** Direct the audio stream through the device's speakerphone. */
59 public static final int ROUTE_SPEAKER = 0x00000008;
60
Grace Jia22c8b962021-10-28 17:04:29 -070061 /** Direct the audio stream through another device. */
62 public static final int ROUTE_EXTERNAL = 0x00000010;
63
Yorke Lee4af59352015-05-13 14:14:54 -070064 /**
65 * Direct the audio stream through the device's earpiece or wired headset if one is
66 * connected.
67 */
68 public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
69
Christine Hallstrom4e22d6d2016-11-30 16:06:42 -080070 /**
71 * Bit mask of all possible audio routes.
72 *
73 * @hide
74 **/
75 public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
Grace Jia22c8b962021-10-28 17:04:29 -070076 ROUTE_SPEAKER | ROUTE_EXTERNAL;
Yorke Lee4af59352015-05-13 14:14:54 -070077
78 private final boolean isMuted;
79 private final int route;
80 private final int supportedRouteMask;
Hall Liua98f58b52017-11-07 17:59:28 -080081 private final BluetoothDevice activeBluetoothDevice;
82 private final Collection<BluetoothDevice> supportedBluetoothDevices;
Yorke Lee4af59352015-05-13 14:14:54 -070083
84 /**
85 * Constructor for a {@link CallAudioState} object.
86 *
87 * @param muted {@code true} if the call is muted, {@code false} otherwise.
88 * @param route The current audio route being used.
89 * Allowed values:
90 * {@link #ROUTE_EARPIECE}
91 * {@link #ROUTE_BLUETOOTH}
92 * {@link #ROUTE_WIRED_HEADSET}
93 * {@link #ROUTE_SPEAKER}
94 * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a
95 * bitwise combination of the following values:
96 * {@link #ROUTE_EARPIECE}
97 * {@link #ROUTE_BLUETOOTH}
98 * {@link #ROUTE_WIRED_HEADSET}
99 * {@link #ROUTE_SPEAKER}
100 */
Hall Liua98f58b52017-11-07 17:59:28 -0800101 public CallAudioState(boolean muted, @CallAudioRoute int route,
102 @CallAudioRoute int supportedRouteMask) {
103 this(muted, route, supportedRouteMask, null, Collections.emptyList());
104 }
105
106 /** @hide */
Hall Liuc1d95aa2018-05-11 17:14:08 -0700107 @TestApi
Hall Liua98f58b52017-11-07 17:59:28 -0800108 public CallAudioState(boolean isMuted, @CallAudioRoute int route,
109 @CallAudioRoute int supportedRouteMask,
110 @Nullable BluetoothDevice activeBluetoothDevice,
111 @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
112 this.isMuted = isMuted;
Yorke Lee4af59352015-05-13 14:14:54 -0700113 this.route = route;
114 this.supportedRouteMask = supportedRouteMask;
Hall Liua98f58b52017-11-07 17:59:28 -0800115 this.activeBluetoothDevice = activeBluetoothDevice;
116 this.supportedBluetoothDevices = supportedBluetoothDevices;
Yorke Lee4af59352015-05-13 14:14:54 -0700117 }
118
119 /** @hide */
120 public CallAudioState(CallAudioState state) {
121 isMuted = state.isMuted();
122 route = state.getRoute();
123 supportedRouteMask = state.getSupportedRouteMask();
Hall Liua98f58b52017-11-07 17:59:28 -0800124 activeBluetoothDevice = state.activeBluetoothDevice;
125 supportedBluetoothDevices = state.getSupportedBluetoothDevices();
Yorke Lee4af59352015-05-13 14:14:54 -0700126 }
127
128 /** @hide */
129 @SuppressWarnings("deprecation")
130 public CallAudioState(AudioState state) {
131 isMuted = state.isMuted();
132 route = state.getRoute();
133 supportedRouteMask = state.getSupportedRouteMask();
Hall Liua98f58b52017-11-07 17:59:28 -0800134 activeBluetoothDevice = null;
135 supportedBluetoothDevices = Collections.emptyList();
Yorke Lee4af59352015-05-13 14:14:54 -0700136 }
137
138 @Override
139 public boolean equals(Object obj) {
140 if (obj == null) {
141 return false;
142 }
143 if (!(obj instanceof CallAudioState)) {
144 return false;
145 }
146 CallAudioState state = (CallAudioState) obj;
Hall Liua98f58b52017-11-07 17:59:28 -0800147 if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
148 return false;
149 }
150 for (BluetoothDevice device : supportedBluetoothDevices) {
151 if (!state.supportedBluetoothDevices.contains(device)) {
152 return false;
153 }
154 }
155 return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
156 state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
157 state.getSupportedRouteMask();
Yorke Lee4af59352015-05-13 14:14:54 -0700158 }
159
160 @Override
161 public String toString() {
Hall Liua98f58b52017-11-07 17:59:28 -0800162 String bluetoothDeviceList = supportedBluetoothDevices.stream()
163 .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
164
Yorke Lee4af59352015-05-13 14:14:54 -0700165 return String.format(Locale.US,
Hall Liua98f58b52017-11-07 17:59:28 -0800166 "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
167 "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
Yorke Lee4af59352015-05-13 14:14:54 -0700168 isMuted,
169 audioRouteToString(route),
Hall Liua98f58b52017-11-07 17:59:28 -0800170 audioRouteToString(supportedRouteMask),
171 activeBluetoothDevice,
172 bluetoothDeviceList);
Yorke Lee4af59352015-05-13 14:14:54 -0700173 }
174
175 /**
176 * @return {@code true} if the call is muted, {@code false} otherwise.
177 */
178 public boolean isMuted() {
179 return isMuted;
180 }
181
182 /**
183 * @return The current audio route being used.
184 */
Hall Liua98f58b52017-11-07 17:59:28 -0800185 @CallAudioRoute
Yorke Lee4af59352015-05-13 14:14:54 -0700186 public int getRoute() {
187 return route;
188 }
189
190 /**
191 * @return Bit mask of all routes supported by this call.
192 */
Hall Liua98f58b52017-11-07 17:59:28 -0800193 @CallAudioRoute
Yorke Lee4af59352015-05-13 14:14:54 -0700194 public int getSupportedRouteMask() {
Grace Jia22c8b962021-10-28 17:04:29 -0700195 if (route == ROUTE_EXTERNAL) {
196 return ROUTE_EXTERNAL;
197 } else {
198 return supportedRouteMask;
199 }
Yorke Lee4af59352015-05-13 14:14:54 -0700200 }
201
202 /**
Hall Liua98f58b52017-11-07 17:59:28 -0800203 * @return The {@link BluetoothDevice} through which audio is being routed.
204 * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
205 */
206 public BluetoothDevice getActiveBluetoothDevice() {
207 return activeBluetoothDevice;
208 }
209
210 /**
211 * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
212 */
213 public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
214 return supportedBluetoothDevices;
215 }
216
217 /**
Yorke Lee4af59352015-05-13 14:14:54 -0700218 * Converts the provided audio route into a human readable string representation.
219 *
220 * @param route to convert into a string.
221 *
222 * @return String representation of the provided audio route.
223 */
224 public static String audioRouteToString(int route) {
225 if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
226 return "UNKNOWN";
227 }
228
229 StringBuffer buffer = new StringBuffer();
230 if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
231 listAppend(buffer, "EARPIECE");
232 }
233 if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
234 listAppend(buffer, "BLUETOOTH");
235 }
236 if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
237 listAppend(buffer, "WIRED_HEADSET");
238 }
239 if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
240 listAppend(buffer, "SPEAKER");
241 }
242
Grace Jia22c8b962021-10-28 17:04:29 -0700243 if ((route & ROUTE_EXTERNAL) == ROUTE_EXTERNAL) {
244 listAppend(buffer, "EXTERNAL");
245 }
246
Yorke Lee4af59352015-05-13 14:14:54 -0700247 return buffer.toString();
248 }
249
250 /**
251 * Responsible for creating AudioState objects for deserialized Parcels.
252 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700253 public static final @android.annotation.NonNull Parcelable.Creator<CallAudioState> CREATOR =
Yorke Lee4af59352015-05-13 14:14:54 -0700254 new Parcelable.Creator<CallAudioState> () {
255
256 @Override
257 public CallAudioState createFromParcel(Parcel source) {
258 boolean isMuted = source.readByte() == 0 ? false : true;
259 int route = source.readInt();
260 int supportedRouteMask = source.readInt();
Hall Liua98f58b52017-11-07 17:59:28 -0800261 BluetoothDevice activeBluetoothDevice = source.readParcelable(
Bernardo Rufino1a5cb382022-01-14 17:35:36 +0000262 ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
Hall Liua98f58b52017-11-07 17:59:28 -0800263 List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
264 source.readParcelableList(supportedBluetoothDevices,
Bernardo Rufino1a5cb382022-01-14 17:35:36 +0000265 ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
Hall Liua98f58b52017-11-07 17:59:28 -0800266 return new CallAudioState(isMuted, route,
267 supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
Yorke Lee4af59352015-05-13 14:14:54 -0700268 }
269
270 @Override
271 public CallAudioState[] newArray(int size) {
272 return new CallAudioState[size];
273 }
274 };
275
276 /**
277 * {@inheritDoc}
278 */
279 @Override
280 public int describeContents() {
281 return 0;
282 }
283
284 /**
285 * Writes AudioState object into a serializeable Parcel.
286 */
287 @Override
288 public void writeToParcel(Parcel destination, int flags) {
289 destination.writeByte((byte) (isMuted ? 1 : 0));
290 destination.writeInt(route);
291 destination.writeInt(supportedRouteMask);
Hall Liua98f58b52017-11-07 17:59:28 -0800292 destination.writeParcelable(activeBluetoothDevice, 0);
293 destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
Yorke Lee4af59352015-05-13 14:14:54 -0700294 }
295
296 private static void listAppend(StringBuffer buffer, String str) {
297 if (buffer.length() > 0) {
298 buffer.append(", ");
299 }
300 buffer.append(str);
301 }
302}