blob: 995957c90f3fb8ecba6939b579eb908a465788ed [file] [log] [blame]
Ben Gilad0407fb22014-01-09 16:18:41 -08001/*
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
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Sailesh Nepalce704b92014-03-17 18:31:43 -070019import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070020import android.os.Bundle;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080021import android.telecomm.CallInfo;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070022import android.telecomm.CallServiceDescriptor;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080023import android.telecomm.CallState;
Yorke Lee33501632014-03-17 19:24:12 -070024import android.telecomm.GatewayInfo;
Santos Cordon766d04f2014-05-06 10:28:25 -070025import android.telecomm.TelecommConstants;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070026import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070027import android.telephony.PhoneNumberUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080028
Ben Gilad61925612014-03-11 19:06:36 -070029import com.google.android.collect.Sets;
Santos Cordon61d0f702014-02-19 02:52:23 -080030import com.google.common.base.Preconditions;
31
Sailesh Nepal91990782014-03-08 17:45:52 -080032import java.util.Locale;
Ben Gilad61925612014-03-11 19:06:36 -070033import java.util.Set;
Ben Gilad0407fb22014-01-09 16:18:41 -080034
Ben Gilad2495d572014-01-09 17:26:19 -080035/**
36 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
37 * from the time the call intent was received by Telecomm (vs. the time the call was
38 * connected etc).
39 */
Ben Gilad0407fb22014-01-09 16:18:41 -080040final class Call {
Santos Cordon766d04f2014-05-06 10:28:25 -070041
42 /**
43 * Listener for events on the call.
44 */
45 interface Listener {
46 void onSuccessfulOutgoingCall(Call call);
47 void onFailedOutgoingCall(Call call, boolean isAborted);
48 void onSuccessfulIncomingCall(Call call, CallInfo callInfo);
49 void onFailedIncomingCall(Call call);
50 }
51
Ben Gilad0407fb22014-01-09 16:18:41 -080052 /** Additional contact information beyond handle above, optional. */
Ben Gilad0bf5b912014-01-28 17:55:57 -080053 private final ContactInfo mContactInfo;
Ben Gilad0407fb22014-01-09 16:18:41 -080054
Sailesh Nepal810735e2014-03-18 18:15:46 -070055 /** True if this is an incoming call. */
56 private final boolean mIsIncoming;
57
Ben Gilad0407fb22014-01-09 16:18:41 -080058 /**
59 * The time this call was created, typically also the time this call was added to the set
60 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
61 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
62 * certain call attempts as failed attempts.
63 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -070064 private final long mCreationTimeMillis = System.currentTimeMillis();
65
66 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -080067
Santos Cordon61d0f702014-02-19 02:52:23 -080068 /** The state of the call. */
69 private CallState mState;
70
71 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -070072 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -080073
Yorke Lee33501632014-03-17 19:24:12 -070074 /** The gateway information associated with this call. This stores the original call handle
75 * that the user is attempting to connect to via the gateway, the actual handle to dial in
76 * order to connect the call via the gateway, as well as the package name of the gateway
77 * service. */
78 private final GatewayInfo mGatewayInfo;
79
Ben Gilad0407fb22014-01-09 16:18:41 -080080 /**
Ben Gilad8e55d1d2014-02-26 16:25:56 -080081 * The call service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -080082 */
Santos Cordonc195e362014-02-11 17:05:31 -080083 private CallServiceWrapper mCallService;
Santos Cordon681663d2014-01-30 04:32:15 -080084
Ben Gilad8e55d1d2014-02-26 16:25:56 -080085 /**
86 * The call-service selector for this call.
Ben Gilad8e55d1d2014-02-26 16:25:56 -080087 */
Sailesh Nepal18386a82014-03-19 10:22:40 -070088 private CallServiceSelectorWrapper mCallServiceSelector;
Ben Gilad8e55d1d2014-02-26 16:25:56 -080089
Santos Cordon0b03b4b2014-01-29 18:01:59 -080090 /**
Ben Gilad61925612014-03-11 19:06:36 -070091 * The set of call services that were attempted in the process of placing/switching this call
92 * but turned out unsuitable. Only used in the context of call switching.
93 */
94 private Set<CallServiceWrapper> mIncompatibleCallServices;
95
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070096 private boolean mIsEmergencyCall;
97
Ben Gilad61925612014-03-11 19:06:36 -070098 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -070099 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
100 * See {@link android.telephony.DisconnectCause}.
101 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700102 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700103
104 /**
105 * Additional disconnect information provided by the call service.
106 */
107 private String mDisconnectMessage;
108
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700109 /** Info used by the call services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700110 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700111
112 /** The Uri to dial to perform the handoff. If this is null then handoff is not supported. */
113 private Uri mHandoffHandle;
114
115 /**
116 * References the call that is being handed off. This value is non-null for untracked calls
117 * that are being used to perform a handoff.
118 */
119 private Call mOriginalCall;
120
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700121 /**
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700122 * The descriptor for the call service that this call is being switched to, null if handoff is
123 * not in progress.
124 */
125 private CallServiceDescriptor mHandoffCallServiceDescriptor;
126
Santos Cordon766d04f2014-05-06 10:28:25 -0700127 /** Set of listeners on this call. */
128 private Set<Listener> mListeners = Sets.newHashSet();
129
Santos Cordon682fe6b2014-05-20 08:56:39 -0700130 private OutgoingCallProcessor mOutgoingCallProcessor;
131
132 // TODO(santoscordon): The repositories should be changed into singleton types.
133 private CallServiceRepository mCallServiceRepository;
134 private CallServiceSelectorRepository mSelectorRepository;
135
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700136 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700137 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700138 *
139 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800140 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700141 Call(boolean isIncoming) {
Yorke Lee33501632014-03-17 19:24:12 -0700142 this(null, null, null, isIncoming);
Santos Cordon493e8f22014-02-19 03:15:12 -0800143 }
144
145 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800146 * Persists the specified parameters and initializes the new instance.
147 *
148 * @param handle The handle to dial.
149 * @param contactInfo Information about the entity being called.
Yorke Lee33501632014-03-17 19:24:12 -0700150 * @param gatewayInfo Gateway information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700151 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800152 */
Yorke Lee33501632014-03-17 19:24:12 -0700153 Call(Uri handle, ContactInfo contactInfo, GatewayInfo gatewayInfo, boolean isIncoming) {
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800154 mState = CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700155 setHandle(handle);
Ben Gilad0407fb22014-01-09 16:18:41 -0800156 mContactInfo = contactInfo;
Yorke Lee33501632014-03-17 19:24:12 -0700157 mGatewayInfo = gatewayInfo;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700158 mIsIncoming = isIncoming;
Ben Gilad0407fb22014-01-09 16:18:41 -0800159 }
160
Santos Cordon766d04f2014-05-06 10:28:25 -0700161 void addListener(Listener listener) {
162 mListeners.add(listener);
163 }
164
165 void removeListener(Listener listener) {
166 mListeners.remove(listener);
167 }
168
Santos Cordon61d0f702014-02-19 02:52:23 -0800169 /** {@inheritDoc} */
170 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700171 String component = null;
172 if (mCallService != null && mCallService.getComponentName() != null) {
173 component = mCallService.getComponentName().flattenToShortString();
174 }
175 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800176 }
177
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800178 CallState getState() {
179 return mState;
180 }
181
182 /**
183 * Sets the call state. Although there exists the notion of appropriate state transitions
184 * (see {@link CallState}), in practice those expectations break down when cellular systems
185 * misbehave and they do this very often. The result is that we do not enforce state transitions
186 * and instead keep the code resilient to unexpected state changes.
187 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700188 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700189 Preconditions.checkState(newState != CallState.DISCONNECTED ||
190 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700191 if (mState != newState) {
192 Log.v(this, "setState %s -> %s", mState, newState);
193 mState = newState;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700194 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800195 }
196
Sailesh Nepalce704b92014-03-17 18:31:43 -0700197 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800198 return mHandle;
199 }
200
Sailesh Nepalce704b92014-03-17 18:31:43 -0700201 void setHandle(Uri handle) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800202 mHandle = handle;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700203 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
204 mHandle.getSchemeSpecificPart(), TelecommApp.getInstance());
205 }
206
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700207 /**
208 * @param disconnectCause The reason for the disconnection, any of
209 * {@link android.telephony.DisconnectCause}.
210 * @param disconnectMessage Optional call-service-provided message about the disconnect.
211 */
212 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
213 // TODO: Consider combining this method with a setDisconnected() method that is totally
214 // separate from setState.
215 mDisconnectCause = disconnectCause;
216 mDisconnectMessage = disconnectMessage;
217 }
218
219 int getDisconnectCause() {
220 return mDisconnectCause;
221 }
222
223 String getDisconnectMessage() {
224 return mDisconnectMessage;
225 }
226
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700227 boolean isEmergencyCall() {
228 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800229 }
230
Yorke Lee33501632014-03-17 19:24:12 -0700231 /**
232 * @return The original handle this call is associated with. In-call services should use this
233 * handle when indicating in their UI the handle that is being called.
234 */
235 public Uri getOriginalHandle() {
236 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
237 return mGatewayInfo.getOriginalHandle();
238 }
239 return getHandle();
240 }
241
242 GatewayInfo getGatewayInfo() {
243 return mGatewayInfo;
244 }
245
Ben Gilad0bf5b912014-01-28 17:55:57 -0800246 ContactInfo getContactInfo() {
247 return mContactInfo;
248 }
249
Sailesh Nepal810735e2014-03-18 18:15:46 -0700250 boolean isIncoming() {
251 return mIsIncoming;
252 }
253
Ben Gilad0407fb22014-01-09 16:18:41 -0800254 /**
255 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700256 * period since this call was added to the set pending outgoing calls, see
257 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800258 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700259 long getAgeMillis() {
260 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800261 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800262
Yorke Leef98fb572014-03-05 10:56:55 -0800263 /**
264 * @return The time when this call object was created and added to the set of pending outgoing
265 * calls.
266 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700267 long getCreationTimeMillis() {
268 return mCreationTimeMillis;
269 }
270
271 long getConnectTimeMillis() {
272 return mConnectTimeMillis;
273 }
274
275 void setConnectTimeMillis(long connectTimeMillis) {
276 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800277 }
278
Santos Cordonc195e362014-02-11 17:05:31 -0800279 CallServiceWrapper getCallService() {
Santos Cordon681663d2014-01-30 04:32:15 -0800280 return mCallService;
281 }
282
Santos Cordonc195e362014-02-11 17:05:31 -0800283 void setCallService(CallServiceWrapper callService) {
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700284 setCallService(callService, null);
285 }
286
287 /**
288 * Changes the call service this call is associated with. If callToReplace is non-null then this
289 * call takes its place within the call service.
290 */
291 void setCallService(CallServiceWrapper callService, Call callToReplace) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800292 Preconditions.checkNotNull(callService);
293
Yorke Leeadee12d2014-03-13 12:08:30 -0700294 clearCallService();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800295
296 callService.incrementAssociatedCallCount();
Santos Cordon681663d2014-01-30 04:32:15 -0800297 mCallService = callService;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700298 if (callToReplace == null) {
299 mCallService.addCall(this);
300 } else {
301 mCallService.replaceCall(this, callToReplace);
302 }
Santos Cordon681663d2014-01-30 04:32:15 -0800303 }
304
305 /**
306 * Clears the associated call service.
307 */
308 void clearCallService() {
Yorke Leeadee12d2014-03-13 12:08:30 -0700309 if (mCallService != null) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700310 CallServiceWrapper callServiceTemp = mCallService;
Yorke Leeadee12d2014-03-13 12:08:30 -0700311 mCallService = null;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700312 callServiceTemp.removeCall(this);
313
314 // Decrementing the count can cause the service to unbind, which itself can trigger the
315 // service-death code. Since the service death code tries to clean up any associated
316 // calls, we need to make sure to remove that information (e.g., removeCall()) before
317 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
318 // necessary, but cleaning up mCallService prior to triggering an unbind is good to do.
319 // If you change this, make sure to update {@link clearCallServiceSelector} as well.
320 decrementAssociatedCallCount(callServiceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700321 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800322 }
323
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700324 CallServiceSelectorWrapper getCallServiceSelector() {
325 return mCallServiceSelector;
326 }
327
Sailesh Nepal18386a82014-03-19 10:22:40 -0700328 void setCallServiceSelector(CallServiceSelectorWrapper selector) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800329 Preconditions.checkNotNull(selector);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700330
331 clearCallServiceSelector();
332
Santos Cordonc499c1c2014-04-14 17:13:14 -0700333 selector.incrementAssociatedCallCount();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800334 mCallServiceSelector = selector;
Sailesh Nepale59bb192014-04-01 18:33:59 -0700335 mCallServiceSelector.addCall(this);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800336 }
337
338 void clearCallServiceSelector() {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700339 if (mCallServiceSelector != null) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700340 CallServiceSelectorWrapper selectorTemp = mCallServiceSelector;
Sailesh Nepale59bb192014-04-01 18:33:59 -0700341 mCallServiceSelector = null;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700342 selectorTemp.removeCall(this);
343
344 // See comment on {@link #clearCallService}.
345 decrementAssociatedCallCount(selectorTemp);
Sailesh Nepale59bb192014-04-01 18:33:59 -0700346 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800347 }
348
349 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700350 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
351 * invoke handle[Un]SuccessfulIncomingCall.
352 *
353 * @param descriptor The relevant call-service descriptor.
354 * @param extras The optional extras passed via
355 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
356 */
357 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
358 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
359 }
360
361 void handleSuccessfulIncoming(CallInfo callInfo) {
362 Preconditions.checkState(callInfo.getState() == CallState.RINGING);
363 setHandle(callInfo.getHandle());
364
365 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing the call
366 // state to RINGING.
367
368 // TODO(santoscordon): Replace this with state transitions related to "connecting".
369 for (Listener l : mListeners) {
370 l.onSuccessfulIncomingCall(this, callInfo);
371 }
372 }
373
374 void handleFailedIncoming() {
375 clearCallService();
376
377 // TODO: Needs more specific disconnect error for this case.
378 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
379 setState(CallState.DISCONNECTED);
380
381 // TODO(santoscordon): Replace this with state transitions related to "connecting".
382 for (Listener l : mListeners) {
383 l.onFailedIncomingCall(this);
384 }
385 }
386
387 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700388 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
389 * through a call service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700390 */
391 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700392 Preconditions.checkState(mOutgoingCallProcessor == null);
393
394 mOutgoingCallProcessor = new OutgoingCallProcessor(
395 this,
396 Switchboard.getInstance().getCallServiceRepository(),
397 Switchboard.getInstance().getSelectorRepository(),
398 new AsyncResultCallback<Boolean>() {
399 @Override
400 public void onResult(Boolean wasCallPlaced) {
401 if (wasCallPlaced) {
402 handleSuccessfulOutgoing();
403 } else {
404 handleFailedOutgoing(mOutgoingCallProcessor.isAborted());
405 }
406 mOutgoingCallProcessor = null;
407 }
408 });
409 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700410 }
411
412 void handleSuccessfulOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700413 setState(CallState.DIALING);
414
Santos Cordon766d04f2014-05-06 10:28:25 -0700415 // TODO(santoscordon): Replace this with state transitions related to "connecting".
416 for (Listener l : mListeners) {
417 l.onSuccessfulOutgoingCall(this);
418 }
419 }
420
421 void handleFailedOutgoing(boolean isAborted) {
422 // TODO(santoscordon): Replace this with state transitions related to "connecting".
423 for (Listener l : mListeners) {
424 l.onFailedOutgoingCall(this, isAborted);
425 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700426
427 clearCallService();
428 clearCallServiceSelector();
Santos Cordon766d04f2014-05-06 10:28:25 -0700429 }
430
431 /**
Ben Gilad61925612014-03-11 19:06:36 -0700432 * Adds the specified call service to the list of incompatible services. The set is used when
433 * attempting to switch a phone call between call services such that incompatible services can
434 * be avoided.
435 *
436 * @param callService The incompatible call service.
437 */
438 void addIncompatibleCallService(CallServiceWrapper callService) {
439 if (mIncompatibleCallServices == null) {
440 mIncompatibleCallServices = Sets.newHashSet();
441 }
442 mIncompatibleCallServices.add(callService);
443 }
444
445 /**
446 * Checks whether or not the specified callService was identified as incompatible in the
447 * context of this call.
448 *
449 * @param callService The call service to evaluate.
450 * @return True upon incompatible call services and false otherwise.
451 */
452 boolean isIncompatibleCallService(CallServiceWrapper callService) {
453 return mIncompatibleCallServices != null &&
454 mIncompatibleCallServices.contains(callService);
455 }
456
457 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700458 * Plays the specified DTMF tone.
459 */
460 void playDtmfTone(char digit) {
461 if (mCallService == null) {
462 Log.w(this, "playDtmfTone() request on a call without a call service.");
463 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700464 Log.i(this, "Send playDtmfTone to call service for call %s", this);
465 mCallService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700466 }
467 }
468
469 /**
470 * Stops playing any currently playing DTMF tone.
471 */
472 void stopDtmfTone() {
473 if (mCallService == null) {
474 Log.w(this, "stopDtmfTone() request on a call without a call service.");
475 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700476 Log.i(this, "Send stopDtmfTone to call service for call %s", this);
477 mCallService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700478 }
479 }
480
481 /**
Santos Cordon049b7b62014-01-30 05:34:26 -0800482 * Attempts to disconnect the call through the call service.
483 */
484 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700485 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700486 Log.v(this, "Aborting call %s", this);
487 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700488 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700489 Preconditions.checkNotNull(mCallService);
490
Sailesh Nepale59bb192014-04-01 18:33:59 -0700491 Log.i(this, "Send disconnect to call service for call: %s", this);
Santos Cordonc195e362014-02-11 17:05:31 -0800492 // The call isn't officially disconnected until the call service confirms that the call
493 // was actually disconnected. Only then is the association between call and call service
494 // severed, see {@link CallsManager#markCallAsDisconnected}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700495 mCallService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800496 }
497 }
498
Santos Cordon682fe6b2014-05-20 08:56:39 -0700499 void abort() {
500 if (mOutgoingCallProcessor != null) {
501 mOutgoingCallProcessor.abort();
502 }
503 }
504
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800505 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800506 * Answers the call if it is ringing.
507 */
508 void answer() {
509 Preconditions.checkNotNull(mCallService);
510
511 // Check to verify that the call is still in the ringing state. A call can change states
512 // between the time the user hits 'answer' and Telecomm receives the command.
513 if (isRinging("answer")) {
514 // At this point, we are asking the call service to answer but we don't assume that
515 // it will work. Instead, we wait until confirmation from the call service that the
516 // call is in a non-RINGING state before changing the UI. See
517 // {@link CallServiceAdapter#setActive} and other set* methods.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700518 mCallService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800519 }
520 }
521
522 /**
523 * Rejects the call if it is ringing.
524 */
525 void reject() {
526 Preconditions.checkNotNull(mCallService);
527
528 // Check to verify that the call is still in the ringing state. A call can change states
529 // between the time the user hits 'reject' and Telecomm receives the command.
530 if (isRinging("reject")) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700531 mCallService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800532 }
533 }
534
535 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700536 * Puts the call on hold if it is currently active.
537 */
538 void hold() {
539 Preconditions.checkNotNull(mCallService);
540
541 if (mState == CallState.ACTIVE) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700542 mCallService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700543 }
544 }
545
546 /**
547 * Releases the call from hold if it is currently active.
548 */
549 void unhold() {
550 Preconditions.checkNotNull(mCallService);
551
552 if (mState == CallState.ON_HOLD) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700553 mCallService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700554 }
555 }
556
557 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800558 * @return An object containing read-only information about this call.
559 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700560 CallInfo toCallInfo(String callId) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700561 CallServiceDescriptor descriptor = null;
562 if (mCallService != null) {
563 descriptor = mCallService.getDescriptor();
564 } else if (mOriginalCall != null && mOriginalCall.mCallService != null) {
565 descriptor = mOriginalCall.mCallService.getDescriptor();
566 }
567 return new CallInfo(callId, mState, mHandle, mGatewayInfo, mExtras, descriptor);
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800568 }
569
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700570 /** Checks if this is a live call or not. */
571 boolean isAlive() {
572 switch (mState) {
573 case NEW:
574 case RINGING:
575 case DISCONNECTED:
576 case ABORTED:
577 return false;
578 default:
579 return true;
580 }
581 }
582
Santos Cordon40f78c22014-04-07 02:11:42 -0700583 boolean isActive() {
584 switch (mState) {
585 case ACTIVE:
586 case POST_DIAL:
587 case POST_DIAL_WAIT:
588 return true;
589 default:
590 return false;
591 }
592 }
593
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700594 Bundle getExtras() {
595 return mExtras;
596 }
597
598 void setExtras(Bundle extras) {
599 mExtras = extras;
600 }
601
602 Uri getHandoffHandle() {
603 return mHandoffHandle;
604 }
605
606 void setHandoffHandle(Uri handoffHandle) {
607 mHandoffHandle = handoffHandle;
608 }
609
610 Call getOriginalCall() {
611 return mOriginalCall;
612 }
613
614 void setOriginalCall(Call originalCall) {
615 mOriginalCall = originalCall;
616 }
617
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700618 CallServiceDescriptor getHandoffCallServiceDescriptor() {
619 return mHandoffCallServiceDescriptor;
620 }
621
622 void setHandoffCallServiceDescriptor(CallServiceDescriptor descriptor) {
623 mHandoffCallServiceDescriptor = descriptor;
624 }
625
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800626 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800627 * @return True if the call is ringing, else logs the action name.
628 */
629 private boolean isRinging(String actionName) {
630 if (mState == CallState.RINGING) {
631 return true;
632 }
633
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800634 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800635 return false;
636 }
637
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800638 @SuppressWarnings("rawtypes")
639 private void decrementAssociatedCallCount(ServiceBinder binder) {
640 if (binder != null) {
641 binder.decrementAssociatedCallCount();
642 }
643 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800644}