blob: 2ecdb30356850630aab6a0464d37da10ded323d7 [file] [log] [blame]
Ihab Awade63fadb2014-07-09 21:52:04 -07001/*
2 * Copyright (C) 2013 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
Tyler Gunnef9f6f92014-09-12 22:16:17 -070017package android.telecom;
Ihab Awade63fadb2014-07-09 21:52:04 -070018
Santos Cordon29886d82015-04-16 15:34:07 -070019import android.annotation.SystemApi;
Mathew Inwood42346d12018-08-01 11:33:05 +010020import android.annotation.UnsupportedAppUsage;
Hall Liua98f58b52017-11-07 17:59:28 -080021import android.bluetooth.BluetoothDevice;
Tyler Gunn17933eb2019-03-05 13:58:45 -080022import android.os.Build;
Tyler Gunn876dbfb2016-03-14 15:18:07 -070023import android.os.Bundle;
Ihab Awade63fadb2014-07-09 21:52:04 -070024import android.util.ArrayMap;
25
Ihab Awade63fadb2014-07-09 21:52:04 -070026import java.util.Collections;
27import java.util.List;
28import java.util.Map;
29import java.util.Objects;
Jay Shrauner229e3822014-08-15 09:23:07 -070030import java.util.concurrent.CopyOnWriteArrayList;
Ihab Awade63fadb2014-07-09 21:52:04 -070031
32/**
33 * A unified virtual device providing a means of voice (and other) communication on a device.
Santos Cordon29886d82015-04-16 15:34:07 -070034 *
35 * @hide
36 * @deprecated Use {@link InCallService} directly instead of using this class.
Ihab Awade63fadb2014-07-09 21:52:04 -070037 */
Santos Cordon29886d82015-04-16 15:34:07 -070038@SystemApi
39@Deprecated
Ihab Awade63fadb2014-07-09 21:52:04 -070040public final class Phone {
41
42 public abstract static class Listener {
43 /**
44 * Called when the audio state changes.
45 *
46 * @param phone The {@code Phone} calling this method.
Ihab Awadb19a0bc2014-08-07 19:46:01 -070047 * @param audioState The new {@link AudioState}.
Yorke Lee4af59352015-05-13 14:14:54 -070048 *
49 * @deprecated Use {@link #onCallAudioStateChanged(Phone, CallAudioState)} instead.
Ihab Awade63fadb2014-07-09 21:52:04 -070050 */
Yorke Lee4af59352015-05-13 14:14:54 -070051 @Deprecated
Ihab Awadb19a0bc2014-08-07 19:46:01 -070052 public void onAudioStateChanged(Phone phone, AudioState audioState) { }
Ihab Awade63fadb2014-07-09 21:52:04 -070053
54 /**
Yorke Lee4af59352015-05-13 14:14:54 -070055 * Called when the audio state changes.
56 *
57 * @param phone The {@code Phone} calling this method.
58 * @param callAudioState The new {@link CallAudioState}.
59 */
60 public void onCallAudioStateChanged(Phone phone, CallAudioState callAudioState) { }
61
62 /**
Ihab Awade63fadb2014-07-09 21:52:04 -070063 * Called to bring the in-call screen to the foreground. The in-call experience should
64 * respond immediately by coming to the foreground to inform the user of the state of
65 * ongoing {@code Call}s.
66 *
67 * @param phone The {@code Phone} calling this method.
68 * @param showDialpad If true, put up the dialpad when the screen is shown.
69 */
70 public void onBringToForeground(Phone phone, boolean showDialpad) { }
71
72 /**
73 * Called when a {@code Call} has been added to this in-call session. The in-call user
74 * experience should add necessary state listeners to the specified {@code Call} and
75 * immediately start to show the user information about the existence
76 * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
77 * include this {@code Call}.
78 *
79 * @param phone The {@code Phone} calling this method.
80 * @param call A newly added {@code Call}.
81 */
82 public void onCallAdded(Phone phone, Call call) { }
83
84 /**
85 * Called when a {@code Call} has been removed from this in-call session. The in-call user
86 * experience should remove any state listeners from the specified {@code Call} and
87 * immediately stop displaying any information about this {@code Call}.
88 * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
89 *
90 * @param phone The {@code Phone} calling this method.
91 * @param call A newly removed {@code Call}.
92 */
93 public void onCallRemoved(Phone phone, Call call) { }
Santos Cordon6c912b72014-11-07 16:05:09 -080094
95 /**
96 * Called when the {@code Phone} ability to add more calls changes. If the phone cannot
97 * support more calls then {@code canAddCall} is set to {@code false}. If it can, then it
98 * is set to {@code true}.
99 *
100 * @param phone The {@code Phone} calling this method.
101 * @param canAddCall Indicates whether an additional call can be added.
102 */
103 public void onCanAddCallChanged(Phone phone, boolean canAddCall) { }
Sailesh Nepal9c2618b2016-01-23 16:28:22 -0800104
105 /**
106 * Called to silence the ringer if a ringing call exists.
107 *
108 * @param phone The {@code Phone} calling this method.
109 */
110 public void onSilenceRinger(Phone phone) { }
Ihab Awade63fadb2014-07-09 21:52:04 -0700111 }
112
Hall Liu31de23d2019-10-11 15:38:29 -0700113 // TODO: replace all usages of this with the actual R constant from Build.VERSION_CODES
114 /** @hide */
115 public static final int SDK_VERSION_R = 30;
116
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700117 // A Map allows us to track each Call by its Telecom-specified call ID
118 private final Map<String, Call> mCallByTelecomCallId = new ArrayMap<>();
Ihab Awade63fadb2014-07-09 21:52:04 -0700119
120 // A List allows us to keep the Calls in a stable iteration order so that casually developed
121 // user interface components do not incur any spurious jank
Santos Cordonf30d7e92014-08-26 09:54:33 -0700122 private final List<Call> mCalls = new CopyOnWriteArrayList<>();
Ihab Awade63fadb2014-07-09 21:52:04 -0700123
124 // An unmodifiable view of the above List can be safely shared with subclass implementations
125 private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls);
126
127 private final InCallAdapter mInCallAdapter;
128
Yorke Lee4af59352015-05-13 14:14:54 -0700129 private CallAudioState mCallAudioState;
Ihab Awade63fadb2014-07-09 21:52:04 -0700130
Jay Shrauner229e3822014-08-15 09:23:07 -0700131 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
Ihab Awade63fadb2014-07-09 21:52:04 -0700132
Santos Cordon6c912b72014-11-07 16:05:09 -0800133 private boolean mCanAddCall = true;
134
Tyler Gunnbf9c6fd2016-11-09 10:19:23 -0800135 private final String mCallingPackage;
136
Tyler Gunn159f35c2017-03-02 09:28:37 -0800137 /**
138 * The Target SDK version of the InCallService implementation.
139 */
140 private final int mTargetSdkVersion;
141
142 Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
Ihab Awade63fadb2014-07-09 21:52:04 -0700143 mInCallAdapter = adapter;
Tyler Gunnbf9c6fd2016-11-09 10:19:23 -0800144 mCallingPackage = callingPackage;
Tyler Gunn159f35c2017-03-02 09:28:37 -0800145 mTargetSdkVersion = targetSdkVersion;
Ihab Awade63fadb2014-07-09 21:52:04 -0700146 }
147
Santos Cordon88b771d2014-07-19 13:10:40 -0700148 final void internalAddCall(ParcelableCall parcelableCall) {
Hall Liu31de23d2019-10-11 15:38:29 -0700149 if (mTargetSdkVersion < SDK_VERSION_R
150 && parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
151 Log.i(this, "Skipping adding audio processing call for sdk compatibility");
152 return;
153 }
154
Shriram Ganeshddf570e2015-05-31 09:18:48 -0700155 Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
Tyler Gunn159f35c2017-03-02 09:28:37 -0800156 parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700157 mCallByTelecomCallId.put(parcelableCall.getId(), call);
Ihab Awade63fadb2014-07-09 21:52:04 -0700158 mCalls.add(call);
Santos Cordon88b771d2014-07-19 13:10:40 -0700159 checkCallTree(parcelableCall);
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700160 call.internalUpdate(parcelableCall, mCallByTelecomCallId);
Ihab Awade63fadb2014-07-09 21:52:04 -0700161 fireCallAdded(call);
Hall Liu31de23d2019-10-11 15:38:29 -0700162 }
Ihab Awade63fadb2014-07-09 21:52:04 -0700163
Ihab Awade63fadb2014-07-09 21:52:04 -0700164 final void internalRemoveCall(Call call) {
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700165 mCallByTelecomCallId.remove(call.internalGetCallId());
Ihab Awade63fadb2014-07-09 21:52:04 -0700166 mCalls.remove(call);
Tyler Gunn75958422015-04-15 14:23:42 -0700167
168 InCallService.VideoCall videoCall = call.getVideoCall();
169 if (videoCall != null) {
Andrew Lee011728f2015-04-23 15:47:06 -0700170 videoCall.destroy();
Tyler Gunn75958422015-04-15 14:23:42 -0700171 }
Ihab Awade63fadb2014-07-09 21:52:04 -0700172 fireCallRemoved(call);
173 }
174
Santos Cordon88b771d2014-07-19 13:10:40 -0700175 final void internalUpdateCall(ParcelableCall parcelableCall) {
Hall Liu31de23d2019-10-11 15:38:29 -0700176 if (mTargetSdkVersion < SDK_VERSION_R
177 && parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
178 Log.i(this, "removing audio processing call during update for sdk compatibility");
179 Call call = mCallByTelecomCallId.get(parcelableCall.getId());
180 if (call != null) {
181 internalRemoveCall(call);
182 }
183 return;
184 }
185
186 Call call = mCallByTelecomCallId.get(parcelableCall.getId());
187 if (call != null) {
188 checkCallTree(parcelableCall);
189 call.internalUpdate(parcelableCall, mCallByTelecomCallId);
190 } else {
191 // This call may have come out of audio processing. Try adding it if our target sdk
192 // version is low enough.
193 if (mTargetSdkVersion < SDK_VERSION_R) {
194 internalAddCall(parcelableCall);
195 }
196 }
197 }
Ihab Awade63fadb2014-07-09 21:52:04 -0700198
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700199 final void internalSetPostDialWait(String telecomId, String remaining) {
200 Call call = mCallByTelecomCallId.get(telecomId);
Ihab Awade63fadb2014-07-09 21:52:04 -0700201 if (call != null) {
202 call.internalSetPostDialWait(remaining);
203 }
204 }
205
Yorke Lee4af59352015-05-13 14:14:54 -0700206 final void internalCallAudioStateChanged(CallAudioState callAudioState) {
207 if (!Objects.equals(mCallAudioState, callAudioState)) {
208 mCallAudioState = callAudioState;
209 fireCallAudioStateChanged(callAudioState);
Ihab Awade63fadb2014-07-09 21:52:04 -0700210 }
211 }
212
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700213 final Call internalGetCallByTelecomId(String telecomId) {
214 return mCallByTelecomCallId.get(telecomId);
Ihab Awade63fadb2014-07-09 21:52:04 -0700215 }
216
Ihab Awade63fadb2014-07-09 21:52:04 -0700217 final void internalBringToForeground(boolean showDialpad) {
218 fireBringToForeground(showDialpad);
219 }
220
Santos Cordon6c912b72014-11-07 16:05:09 -0800221 final void internalSetCanAddCall(boolean canAddCall) {
222 if (mCanAddCall != canAddCall) {
223 mCanAddCall = canAddCall;
224 fireCanAddCallChanged(canAddCall);
225 }
226 }
227
Sailesh Nepal9c2618b2016-01-23 16:28:22 -0800228 final void internalSilenceRinger() {
229 fireSilenceRinger();
230 }
231
Tyler Gunn876dbfb2016-03-14 15:18:07 -0700232 final void internalOnConnectionEvent(String telecomId, String event, Bundle extras) {
233 Call call = mCallByTelecomCallId.get(telecomId);
234 if (call != null) {
235 call.internalOnConnectionEvent(event, extras);
236 }
237 }
238
Hall Liu95d55872017-01-25 17:12:49 -0800239 final void internalOnRttUpgradeRequest(String callId, int requestId) {
240 Call call = mCallByTelecomCallId.get(callId);
241 if (call != null) {
242 call.internalOnRttUpgradeRequest(requestId);
243 }
244 }
245
Hall Liu57006aa2017-02-06 10:49:48 -0800246 final void internalOnRttInitiationFailure(String callId, int reason) {
247 Call call = mCallByTelecomCallId.get(callId);
248 if (call != null) {
249 call.internalOnRttInitiationFailure(reason);
250 }
251 }
252
Sanket Padawe85291f62017-12-01 13:59:27 -0800253 final void internalOnHandoverFailed(String callId, int error) {
254 Call call = mCallByTelecomCallId.get(callId);
255 if (call != null) {
256 call.internalOnHandoverFailed(error);
257 }
258 }
259
Tyler Gunn858bfaf2018-01-22 15:17:54 -0800260 final void internalOnHandoverComplete(String callId) {
261 Call call = mCallByTelecomCallId.get(callId);
262 if (call != null) {
263 call.internalOnHandoverComplete();
264 }
265 }
266
Ihab Awade63fadb2014-07-09 21:52:04 -0700267 /**
Santos Cordonf30d7e92014-08-26 09:54:33 -0700268 * Called to destroy the phone and cleanup any lingering calls.
Santos Cordonf30d7e92014-08-26 09:54:33 -0700269 */
270 final void destroy() {
271 for (Call call : mCalls) {
Tyler Gunn75958422015-04-15 14:23:42 -0700272 InCallService.VideoCall videoCall = call.getVideoCall();
273 if (videoCall != null) {
Andrew Lee011728f2015-04-23 15:47:06 -0700274 videoCall.destroy();
Tyler Gunn75958422015-04-15 14:23:42 -0700275 }
Santos Cordonf30d7e92014-08-26 09:54:33 -0700276 if (call.getState() != Call.STATE_DISCONNECTED) {
277 call.internalSetDisconnected();
278 }
279 }
280 }
281
282 /**
Ihab Awade63fadb2014-07-09 21:52:04 -0700283 * Adds a listener to this {@code Phone}.
284 *
285 * @param listener A {@code Listener} object.
286 */
287 public final void addListener(Listener listener) {
288 mListeners.add(listener);
289 }
290
291 /**
292 * Removes a listener from this {@code Phone}.
293 *
294 * @param listener A {@code Listener} object.
295 */
296 public final void removeListener(Listener listener) {
Jay Shrauner229e3822014-08-15 09:23:07 -0700297 if (listener != null) {
298 mListeners.remove(listener);
299 }
Ihab Awade63fadb2014-07-09 21:52:04 -0700300 }
301
302 /**
303 * Obtains the current list of {@code Call}s to be displayed by this in-call experience.
304 *
305 * @return A list of the relevant {@code Call}s.
306 */
307 public final List<Call> getCalls() {
308 return mUnmodifiableCalls;
309 }
310
311 /**
Santos Cordon6c912b72014-11-07 16:05:09 -0800312 * Returns if the {@code Phone} can support additional calls.
313 *
314 * @return Whether the phone supports adding more calls.
315 */
316 public final boolean canAddCall() {
317 return mCanAddCall;
318 }
319
320 /**
Ihab Awade63fadb2014-07-09 21:52:04 -0700321 * Sets the microphone mute state. When this request is honored, there will be change to
322 * the {@link #getAudioState()}.
323 *
324 * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
325 */
326 public final void setMuted(boolean state) {
327 mInCallAdapter.mute(state);
328 }
329
330 /**
331 * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will
332 * be change to the {@link #getAudioState()}.
333 *
334 * @param route The audio route to use.
335 */
336 public final void setAudioRoute(int route) {
337 mInCallAdapter.setAudioRoute(route);
338 }
339
340 /**
Hall Liua98f58b52017-11-07 17:59:28 -0800341 * Request audio routing to a specific bluetooth device. Calling this method may result in
342 * the device routing audio to a different bluetooth device than the one specified. A list of
343 * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
344 *
345 * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
346 * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
347 */
348 public void requestBluetoothAudio(String bluetoothAddress) {
349 mInCallAdapter.requestBluetoothAudio(bluetoothAddress);
350 }
351
352 /**
Yorke Lee0d6ea712014-07-28 14:39:23 -0700353 * Turns the proximity sensor on. When this request is made, the proximity sensor will
354 * become active, and the touch screen and display will be turned off when the user's face
355 * is detected to be in close proximity to the screen. This operation is a no-op on devices
356 * that do not have a proximity sensor.
Tyler Gunn17933eb2019-03-05 13:58:45 -0800357 * <p>
358 * This API does not actually turn on the proximity sensor; apps should do this on their own if
359 * required.
Yorke Lee22244d02015-04-14 12:34:28 -0700360 * @hide
Yorke Lee0d6ea712014-07-28 14:39:23 -0700361 */
Tyler Gunn17933eb2019-03-05 13:58:45 -0800362 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196)
Yorke Lee0d6ea712014-07-28 14:39:23 -0700363 public final void setProximitySensorOn() {
364 mInCallAdapter.turnProximitySensorOn();
365 }
366
367 /**
368 * Turns the proximity sensor off. When this request is made, the proximity sensor will
369 * become inactive, and no longer affect the touch screen and display. This operation is a
370 * no-op on devices that do not have a proximity sensor.
371 *
372 * @param screenOnImmediately If true, the screen will be turned on immediately if it was
373 * previously off. Otherwise, the screen will only be turned on after the proximity sensor
374 * is no longer triggered.
Tyler Gunn17933eb2019-03-05 13:58:45 -0800375 * <p>
376 * This API does not actually turn of the proximity sensor; apps should do this on their own if
377 * required.
Yorke Lee22244d02015-04-14 12:34:28 -0700378 * @hide
Yorke Lee0d6ea712014-07-28 14:39:23 -0700379 */
Tyler Gunn17933eb2019-03-05 13:58:45 -0800380 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196)
Yorke Lee0d6ea712014-07-28 14:39:23 -0700381 public final void setProximitySensorOff(boolean screenOnImmediately) {
382 mInCallAdapter.turnProximitySensorOff(screenOnImmediately);
383 }
384
385 /**
Ihab Awade63fadb2014-07-09 21:52:04 -0700386 * Obtains the current phone call audio state of the {@code Phone}.
387 *
388 * @return An object encapsulating the audio state.
Yorke Lee4af59352015-05-13 14:14:54 -0700389 * @deprecated Use {@link #getCallAudioState()} instead.
Ihab Awade63fadb2014-07-09 21:52:04 -0700390 */
Yorke Lee4af59352015-05-13 14:14:54 -0700391 @Deprecated
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700392 public final AudioState getAudioState() {
Yorke Lee4af59352015-05-13 14:14:54 -0700393 return new AudioState(mCallAudioState);
394 }
395
396 /**
397 * Obtains the current phone call audio state of the {@code Phone}.
398 *
399 * @return An object encapsulating the audio state.
400 */
401 public final CallAudioState getCallAudioState() {
402 return mCallAudioState;
Ihab Awade63fadb2014-07-09 21:52:04 -0700403 }
404
405 private void fireCallAdded(Call call) {
Jay Shrauner229e3822014-08-15 09:23:07 -0700406 for (Listener listener : mListeners) {
407 listener.onCallAdded(this, call);
Ihab Awade63fadb2014-07-09 21:52:04 -0700408 }
409 }
410
411 private void fireCallRemoved(Call call) {
Jay Shrauner229e3822014-08-15 09:23:07 -0700412 for (Listener listener : mListeners) {
413 listener.onCallRemoved(this, call);
Ihab Awade63fadb2014-07-09 21:52:04 -0700414 }
415 }
416
Yorke Lee4af59352015-05-13 14:14:54 -0700417 private void fireCallAudioStateChanged(CallAudioState audioState) {
Jay Shrauner229e3822014-08-15 09:23:07 -0700418 for (Listener listener : mListeners) {
Yorke Lee4af59352015-05-13 14:14:54 -0700419 listener.onCallAudioStateChanged(this, audioState);
420 listener.onAudioStateChanged(this, new AudioState(audioState));
Ihab Awade63fadb2014-07-09 21:52:04 -0700421 }
422 }
423
424 private void fireBringToForeground(boolean showDialpad) {
Jay Shrauner229e3822014-08-15 09:23:07 -0700425 for (Listener listener : mListeners) {
426 listener.onBringToForeground(this, showDialpad);
Ihab Awade63fadb2014-07-09 21:52:04 -0700427 }
428 }
429
Santos Cordon6c912b72014-11-07 16:05:09 -0800430 private void fireCanAddCallChanged(boolean canAddCall) {
431 for (Listener listener : mListeners) {
432 listener.onCanAddCallChanged(this, canAddCall);
433 }
434 }
435
Sailesh Nepal9c2618b2016-01-23 16:28:22 -0800436 private void fireSilenceRinger() {
437 for (Listener listener : mListeners) {
438 listener.onSilenceRinger(this);
439 }
440 }
441
Santos Cordon88b771d2014-07-19 13:10:40 -0700442 private void checkCallTree(ParcelableCall parcelableCall) {
Santos Cordon88b771d2014-07-19 13:10:40 -0700443 if (parcelableCall.getChildCallIds() != null) {
444 for (int i = 0; i < parcelableCall.getChildCallIds().size(); i++) {
Tyler Gunnef9f6f92014-09-12 22:16:17 -0700445 if (!mCallByTelecomCallId.containsKey(parcelableCall.getChildCallIds().get(i))) {
Santos Cordon88b771d2014-07-19 13:10:40 -0700446 Log.wtf(this, "ParcelableCall %s has nonexistent child %s",
447 parcelableCall.getId(), parcelableCall.getChildCallIds().get(i));
Ihab Awade63fadb2014-07-09 21:52:04 -0700448 }
449 }
450 }
451 }
452}