blob: 4b9098504bc7fd51f6ece15224beb9e22c3f7995 [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 Liua98f58b2017-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 Liua98f58b2017-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 Liua98f58b2017-11-07 17:59:28 -080027import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.Collections;
33import java.util.List;
Yorke Lee4af59352015-05-13 14:14:54 -070034import java.util.Locale;
Hall Liua98f58b2017-11-07 17:59:28 -080035import java.util.Objects;
36import java.util.stream.Collectors;
Yorke Lee4af59352015-05-13 14:14:54 -070037
38/**
39 * Encapsulates the telecom audio state, including the current audio routing, supported audio
40 * routing and mute.
41 */
42public final class CallAudioState implements Parcelable {
Hall Liua98f58b2017-11-07 17:59:28 -080043 /** @hide */
44 @Retention(RetentionPolicy.SOURCE)
45 @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
46 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
61 /**
62 * Direct the audio stream through the device's earpiece or wired headset if one is
63 * connected.
64 */
65 public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
66
Christine Hallstrom4e22d6d2016-11-30 16:06:42 -080067 /**
68 * Bit mask of all possible audio routes.
69 *
70 * @hide
71 **/
72 public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
Yorke Lee4af59352015-05-13 14:14:54 -070073 ROUTE_SPEAKER;
74
75 private final boolean isMuted;
76 private final int route;
77 private final int supportedRouteMask;
Hall Liua98f58b2017-11-07 17:59:28 -080078 private final BluetoothDevice activeBluetoothDevice;
79 private final Collection<BluetoothDevice> supportedBluetoothDevices;
Yorke Lee4af59352015-05-13 14:14:54 -070080
81 /**
82 * Constructor for a {@link CallAudioState} object.
83 *
84 * @param muted {@code true} if the call is muted, {@code false} otherwise.
85 * @param route The current audio route being used.
86 * Allowed values:
87 * {@link #ROUTE_EARPIECE}
88 * {@link #ROUTE_BLUETOOTH}
89 * {@link #ROUTE_WIRED_HEADSET}
90 * {@link #ROUTE_SPEAKER}
91 * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a
92 * bitwise combination of the following values:
93 * {@link #ROUTE_EARPIECE}
94 * {@link #ROUTE_BLUETOOTH}
95 * {@link #ROUTE_WIRED_HEADSET}
96 * {@link #ROUTE_SPEAKER}
97 */
Hall Liua98f58b2017-11-07 17:59:28 -080098 public CallAudioState(boolean muted, @CallAudioRoute int route,
99 @CallAudioRoute int supportedRouteMask) {
100 this(muted, route, supportedRouteMask, null, Collections.emptyList());
101 }
102
103 /** @hide */
Hall Liuc1d95aa2018-05-11 17:14:08 -0700104 @TestApi
Hall Liua98f58b2017-11-07 17:59:28 -0800105 public CallAudioState(boolean isMuted, @CallAudioRoute int route,
106 @CallAudioRoute int supportedRouteMask,
107 @Nullable BluetoothDevice activeBluetoothDevice,
108 @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
109 this.isMuted = isMuted;
Yorke Lee4af59352015-05-13 14:14:54 -0700110 this.route = route;
111 this.supportedRouteMask = supportedRouteMask;
Hall Liua98f58b2017-11-07 17:59:28 -0800112 this.activeBluetoothDevice = activeBluetoothDevice;
113 this.supportedBluetoothDevices = supportedBluetoothDevices;
Yorke Lee4af59352015-05-13 14:14:54 -0700114 }
115
116 /** @hide */
117 public CallAudioState(CallAudioState state) {
118 isMuted = state.isMuted();
119 route = state.getRoute();
120 supportedRouteMask = state.getSupportedRouteMask();
Hall Liua98f58b2017-11-07 17:59:28 -0800121 activeBluetoothDevice = state.activeBluetoothDevice;
122 supportedBluetoothDevices = state.getSupportedBluetoothDevices();
Yorke Lee4af59352015-05-13 14:14:54 -0700123 }
124
125 /** @hide */
126 @SuppressWarnings("deprecation")
127 public CallAudioState(AudioState state) {
128 isMuted = state.isMuted();
129 route = state.getRoute();
130 supportedRouteMask = state.getSupportedRouteMask();
Hall Liua98f58b2017-11-07 17:59:28 -0800131 activeBluetoothDevice = null;
132 supportedBluetoothDevices = Collections.emptyList();
Yorke Lee4af59352015-05-13 14:14:54 -0700133 }
134
135 @Override
136 public boolean equals(Object obj) {
137 if (obj == null) {
138 return false;
139 }
140 if (!(obj instanceof CallAudioState)) {
141 return false;
142 }
143 CallAudioState state = (CallAudioState) obj;
Hall Liua98f58b2017-11-07 17:59:28 -0800144 if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
145 return false;
146 }
147 for (BluetoothDevice device : supportedBluetoothDevices) {
148 if (!state.supportedBluetoothDevices.contains(device)) {
149 return false;
150 }
151 }
152 return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
153 state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
154 state.getSupportedRouteMask();
Yorke Lee4af59352015-05-13 14:14:54 -0700155 }
156
157 @Override
158 public String toString() {
Hall Liua98f58b2017-11-07 17:59:28 -0800159 String bluetoothDeviceList = supportedBluetoothDevices.stream()
160 .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
161
Yorke Lee4af59352015-05-13 14:14:54 -0700162 return String.format(Locale.US,
Hall Liua98f58b2017-11-07 17:59:28 -0800163 "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
164 "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
Yorke Lee4af59352015-05-13 14:14:54 -0700165 isMuted,
166 audioRouteToString(route),
Hall Liua98f58b2017-11-07 17:59:28 -0800167 audioRouteToString(supportedRouteMask),
168 activeBluetoothDevice,
169 bluetoothDeviceList);
Yorke Lee4af59352015-05-13 14:14:54 -0700170 }
171
172 /**
173 * @return {@code true} if the call is muted, {@code false} otherwise.
174 */
175 public boolean isMuted() {
176 return isMuted;
177 }
178
179 /**
180 * @return The current audio route being used.
181 */
Hall Liua98f58b2017-11-07 17:59:28 -0800182 @CallAudioRoute
Yorke Lee4af59352015-05-13 14:14:54 -0700183 public int getRoute() {
184 return route;
185 }
186
187 /**
188 * @return Bit mask of all routes supported by this call.
189 */
Hall Liua98f58b2017-11-07 17:59:28 -0800190 @CallAudioRoute
Yorke Lee4af59352015-05-13 14:14:54 -0700191 public int getSupportedRouteMask() {
192 return supportedRouteMask;
193 }
194
195 /**
Hall Liua98f58b2017-11-07 17:59:28 -0800196 * @return The {@link BluetoothDevice} through which audio is being routed.
197 * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
198 */
199 public BluetoothDevice getActiveBluetoothDevice() {
200 return activeBluetoothDevice;
201 }
202
203 /**
204 * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
205 */
206 public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
207 return supportedBluetoothDevices;
208 }
209
210 /**
Yorke Lee4af59352015-05-13 14:14:54 -0700211 * Converts the provided audio route into a human readable string representation.
212 *
213 * @param route to convert into a string.
214 *
215 * @return String representation of the provided audio route.
216 */
217 public static String audioRouteToString(int route) {
218 if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
219 return "UNKNOWN";
220 }
221
222 StringBuffer buffer = new StringBuffer();
223 if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
224 listAppend(buffer, "EARPIECE");
225 }
226 if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
227 listAppend(buffer, "BLUETOOTH");
228 }
229 if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
230 listAppend(buffer, "WIRED_HEADSET");
231 }
232 if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
233 listAppend(buffer, "SPEAKER");
234 }
235
236 return buffer.toString();
237 }
238
239 /**
240 * Responsible for creating AudioState objects for deserialized Parcels.
241 */
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700242 public static final @android.annotation.NonNull Parcelable.Creator<CallAudioState> CREATOR =
Yorke Lee4af59352015-05-13 14:14:54 -0700243 new Parcelable.Creator<CallAudioState> () {
244
245 @Override
246 public CallAudioState createFromParcel(Parcel source) {
247 boolean isMuted = source.readByte() == 0 ? false : true;
248 int route = source.readInt();
249 int supportedRouteMask = source.readInt();
Hall Liua98f58b2017-11-07 17:59:28 -0800250 BluetoothDevice activeBluetoothDevice = source.readParcelable(
251 ClassLoader.getSystemClassLoader());
252 List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
253 source.readParcelableList(supportedBluetoothDevices,
254 ClassLoader.getSystemClassLoader());
255 return new CallAudioState(isMuted, route,
256 supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
Yorke Lee4af59352015-05-13 14:14:54 -0700257 }
258
259 @Override
260 public CallAudioState[] newArray(int size) {
261 return new CallAudioState[size];
262 }
263 };
264
265 /**
266 * {@inheritDoc}
267 */
268 @Override
269 public int describeContents() {
270 return 0;
271 }
272
273 /**
274 * Writes AudioState object into a serializeable Parcel.
275 */
276 @Override
277 public void writeToParcel(Parcel destination, int flags) {
278 destination.writeByte((byte) (isMuted ? 1 : 0));
279 destination.writeInt(route);
280 destination.writeInt(supportedRouteMask);
Hall Liua98f58b2017-11-07 17:59:28 -0800281 destination.writeParcelable(activeBluetoothDevice, 0);
282 destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
Yorke Lee4af59352015-05-13 14:14:54 -0700283 }
284
285 private static void listAppend(StringBuffer buffer, String str) {
286 if (buffer.length() > 0) {
287 buffer.append(", ");
288 }
289 buffer.append(str);
290 }
291}