blob: ddd4b1d93e8e55038e2bcc50ffb315d9262c7adb [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
Santos Cordon99c8a6f2014-05-28 18:28:47 -070019import android.content.ContentUris;
20import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070022import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070023import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070024import android.os.Handler;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070025import android.provider.ContactsContract.Contacts;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080026import android.telecomm.CallInfo;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070027import android.telecomm.CallServiceDescriptor;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080028import android.telecomm.CallState;
Yorke Lee33501632014-03-17 19:24:12 -070029import android.telecomm.GatewayInfo;
Ihab Awadff7493a2014-06-10 13:47:44 -070030import android.telecomm.Response;
Nancy Chen77d2d0e2014-06-24 12:06:03 -070031import android.telecomm.Subscription;
Santos Cordon766d04f2014-05-06 10:28:25 -070032import android.telecomm.TelecommConstants;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070033import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070034import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070035import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080036
Nancy Chena65d41f2014-06-24 12:06:03 -070037import com.android.internal.telecomm.ICallVideoProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070038import com.android.internal.telephony.CallerInfo;
39import com.android.internal.telephony.CallerInfoAsyncQuery;
40import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070041import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070042import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080043import com.google.common.base.Preconditions;
Ihab Awadff7493a2014-06-10 13:47:44 -070044import com.google.common.collect.Sets;
Santos Cordon61d0f702014-02-19 02:52:23 -080045
Ihab Awadff7493a2014-06-10 13:47:44 -070046import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070047import java.util.LinkedList;
48import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080049import java.util.Locale;
Ben Gilad61925612014-03-11 19:06:36 -070050import java.util.Set;
Ben Gilad0407fb22014-01-09 16:18:41 -080051
Ben Gilad2495d572014-01-09 17:26:19 -080052/**
53 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
54 * from the time the call intent was received by Telecomm (vs. the time the call was
55 * connected etc).
56 */
Sailesh Nepal5a73b032014-06-25 15:53:21 -070057final class Call implements OutgoingCallResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070058
59 /**
60 * Listener for events on the call.
61 */
62 interface Listener {
63 void onSuccessfulOutgoingCall(Call call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -070064 void onFailedOutgoingCall(Call call, int errorCode, String errorMsg);
65 void onCancelledOutgoingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070066 void onSuccessfulIncomingCall(Call call, CallInfo callInfo);
67 void onFailedIncomingCall(Call call);
Ihab Awadcb387ac2014-05-28 16:49:38 -070068 void onRequestingRingback(Call call, boolean requestingRingback);
Evan Charlton352105c2014-06-03 14:10:54 -070069 void onPostDialWait(Call call, String remaining);
Santos Cordona1610702014-06-04 20:22:56 -070070 void onIsConferenceCapableChanged(Call call, boolean isConferenceCapable);
71 void onExpiredConferenceCall(Call call);
72 void onConfirmedConferenceCall(Call call);
73 void onParentChanged(Call call);
74 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -070075 void onCannedSmsResponsesLoaded(Call call);
Nancy Chena65d41f2014-06-24 12:06:03 -070076 void onCallVideoProviderChanged(Call call);
Tyler Gunne19cc002014-07-01 11:32:53 -070077 void onFeaturesChanged(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070078 }
79
Santos Cordonfd71f4a2014-05-28 13:59:49 -070080 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -070081 new OnQueryCompleteListener() {
82 /** ${inheritDoc} */
83 @Override
84 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
85 if (cookie != null) {
86 ((Call) cookie).setCallerInfo(callerInfo, token);
87 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -070088 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -070089 };
90
91 private static final OnImageLoadCompleteListener sPhotoLoadListener =
92 new OnImageLoadCompleteListener() {
93 /** ${inheritDoc} */
94 @Override
95 public void onImageLoadComplete(
96 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
97 if (cookie != null) {
98 ((Call) cookie).setPhoto(photo, photoIcon, token);
99 }
100 }
101 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800102
Sailesh Nepal810735e2014-03-18 18:15:46 -0700103 /** True if this is an incoming call. */
104 private final boolean mIsIncoming;
105
Ben Gilad0407fb22014-01-09 16:18:41 -0800106 /**
107 * The time this call was created, typically also the time this call was added to the set
108 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
109 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
110 * certain call attempts as failed attempts.
111 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700112 private final long mCreationTimeMillis = System.currentTimeMillis();
113
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700114 /** The gateway information associated with this call. This stores the original call handle
115 * that the user is attempting to connect to via the gateway, the actual handle to dial in
116 * order to connect the call via the gateway, as well as the package name of the gateway
117 * service. */
118 private final GatewayInfo mGatewayInfo;
119
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700120 private final Subscription mSubscription;
121
Santos Cordon2174fb52014-05-29 08:22:56 -0700122 private final Handler mHandler = new Handler();
123
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700124 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800125
Santos Cordon61d0f702014-02-19 02:52:23 -0800126 /** The state of the call. */
127 private CallState mState;
128
129 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700130 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800131
Ben Gilad0407fb22014-01-09 16:18:41 -0800132 /**
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800133 * The call service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800134 */
Santos Cordonc195e362014-02-11 17:05:31 -0800135 private CallServiceWrapper mCallService;
Santos Cordon681663d2014-01-30 04:32:15 -0800136
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800137 /**
Ben Gilad61925612014-03-11 19:06:36 -0700138 * The set of call services that were attempted in the process of placing/switching this call
139 * but turned out unsuitable. Only used in the context of call switching.
140 */
141 private Set<CallServiceWrapper> mIncompatibleCallServices;
142
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700143 private boolean mIsEmergencyCall;
144
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700145 private boolean mSpeakerphoneOn;
146
Ben Gilad61925612014-03-11 19:06:36 -0700147 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700148 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
149 * See {@link android.telephony.DisconnectCause}.
150 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700151 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700152
153 /**
154 * Additional disconnect information provided by the call service.
155 */
156 private String mDisconnectMessage;
157
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700158 /** Info used by the call services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700159 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700160
161 /** The Uri to dial to perform the handoff. If this is null then handoff is not supported. */
162 private Uri mHandoffHandle;
163
164 /**
165 * References the call that is being handed off. This value is non-null for untracked calls
166 * that are being used to perform a handoff.
167 */
168 private Call mOriginalCall;
169
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700170 /**
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700171 * The descriptor for the call service that this call is being switched to, null if handoff is
172 * not in progress.
173 */
174 private CallServiceDescriptor mHandoffCallServiceDescriptor;
175
Santos Cordon766d04f2014-05-06 10:28:25 -0700176 /** Set of listeners on this call. */
177 private Set<Listener> mListeners = Sets.newHashSet();
178
Santos Cordon682fe6b2014-05-20 08:56:39 -0700179 private OutgoingCallProcessor mOutgoingCallProcessor;
180
181 // TODO(santoscordon): The repositories should be changed into singleton types.
182 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700183
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700184 /** Caller information retrieved from the latest contact query. */
185 private CallerInfo mCallerInfo;
186
187 /** The latest token used with a contact info query. */
188 private int mQueryToken = 0;
189
Ihab Awadcb387ac2014-05-28 16:49:38 -0700190 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
191 private boolean mRequestingRingback = false;
192
Santos Cordon2174fb52014-05-29 08:22:56 -0700193 /** Incoming call-info to use when direct-to-voicemail query finishes. */
194 private CallInfo mPendingDirectToVoicemailCallInfo;
195
Santos Cordona1610702014-06-04 20:22:56 -0700196 private boolean mIsConferenceCapable = false;
197
198 private boolean mIsConference = false;
199
200 private Call mParentCall = null;
201
202 private List<Call> mChildCalls = new LinkedList<>();
203
Ihab Awadff7493a2014-06-10 13:47:44 -0700204 /** Set of text message responses allowed for this call, if applicable. */
205 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
206
207 /** Whether an attempt has been made to load the text message responses. */
208 private boolean mCannedSmsResponsesLoadingStarted = false;
209
Nancy Chena65d41f2014-06-24 12:06:03 -0700210 private ICallVideoProvider mCallVideoProvider;
211
Tyler Gunne19cc002014-07-01 11:32:53 -0700212 /** Features associated with the call which the InCall UI may wish to show icons for. */
213 private int mFeatures;
214
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700215 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700216 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700217 *
218 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800219 */
Santos Cordona1610702014-06-04 20:22:56 -0700220 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700221 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800222 }
223
224 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800225 * Persists the specified parameters and initializes the new instance.
226 *
227 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700228 * @param gatewayInfo Gateway information to use for the call.
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700229 * @param subscription Subscription information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700230 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800231 */
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700232 Call(Uri handle, GatewayInfo gatewayInfo, Subscription subscription,
233 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700234 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700235 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700236 mGatewayInfo = gatewayInfo;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700237 mSubscription = subscription;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700238 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700239 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700240 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800241 }
242
Santos Cordon766d04f2014-05-06 10:28:25 -0700243 void addListener(Listener listener) {
244 mListeners.add(listener);
245 }
246
247 void removeListener(Listener listener) {
248 mListeners.remove(listener);
249 }
250
Santos Cordon61d0f702014-02-19 02:52:23 -0800251 /** {@inheritDoc} */
252 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700253 String component = null;
254 if (mCallService != null && mCallService.getComponentName() != null) {
255 component = mCallService.getComponentName().flattenToShortString();
256 }
257 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800258 }
259
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800260 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700261 if (mIsConference) {
262 if (!mChildCalls.isEmpty()) {
263 // If we have child calls, just return the child call.
264 return mChildCalls.get(0).getState();
265 }
266 return CallState.ACTIVE;
267 } else {
268 return mState;
269 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800270 }
271
272 /**
273 * Sets the call state. Although there exists the notion of appropriate state transitions
274 * (see {@link CallState}), in practice those expectations break down when cellular systems
275 * misbehave and they do this very often. The result is that we do not enforce state transitions
276 * and instead keep the code resilient to unexpected state changes.
277 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700278 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700279 Preconditions.checkState(newState != CallState.DISCONNECTED ||
280 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700281 if (mState != newState) {
282 Log.v(this, "setState %s -> %s", mState, newState);
283 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700284 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700285 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800286 }
287
Ihab Awadcb387ac2014-05-28 16:49:38 -0700288 void setRequestingRingback(boolean requestingRingback) {
289 mRequestingRingback = requestingRingback;
290 for (Listener l : mListeners) {
291 l.onRequestingRingback(this, mRequestingRingback);
292 }
293 }
294
295 boolean isRequestingRingback() {
296 return mRequestingRingback;
297 }
298
Sailesh Nepalce704b92014-03-17 18:31:43 -0700299 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800300 return mHandle;
301 }
302
Sailesh Nepalce704b92014-03-17 18:31:43 -0700303 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700304 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
305 mHandle = handle;
306 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700307 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700308 startCallerInfoLookup();
309 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700310 }
311
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700312 String getName() {
313 return mCallerInfo == null ? null : mCallerInfo.name;
314 }
315
316 Bitmap getPhotoIcon() {
317 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
318 }
319
320 Drawable getPhoto() {
321 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
322 }
323
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700324 /**
325 * @param disconnectCause The reason for the disconnection, any of
326 * {@link android.telephony.DisconnectCause}.
327 * @param disconnectMessage Optional call-service-provided message about the disconnect.
328 */
329 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
330 // TODO: Consider combining this method with a setDisconnected() method that is totally
331 // separate from setState.
332 mDisconnectCause = disconnectCause;
333 mDisconnectMessage = disconnectMessage;
334 }
335
336 int getDisconnectCause() {
337 return mDisconnectCause;
338 }
339
340 String getDisconnectMessage() {
341 return mDisconnectMessage;
342 }
343
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700344 boolean isEmergencyCall() {
345 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800346 }
347
Yorke Lee33501632014-03-17 19:24:12 -0700348 /**
349 * @return The original handle this call is associated with. In-call services should use this
350 * handle when indicating in their UI the handle that is being called.
351 */
352 public Uri getOriginalHandle() {
353 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
354 return mGatewayInfo.getOriginalHandle();
355 }
356 return getHandle();
357 }
358
359 GatewayInfo getGatewayInfo() {
360 return mGatewayInfo;
361 }
362
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700363 Subscription getSubscription() {
364 return mSubscription;
365 }
366
Sailesh Nepal810735e2014-03-18 18:15:46 -0700367 boolean isIncoming() {
368 return mIsIncoming;
369 }
370
Ben Gilad0407fb22014-01-09 16:18:41 -0800371 /**
372 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700373 * period since this call was added to the set pending outgoing calls, see
374 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800375 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700376 long getAgeMillis() {
377 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800378 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800379
Yorke Leef98fb572014-03-05 10:56:55 -0800380 /**
381 * @return The time when this call object was created and added to the set of pending outgoing
382 * calls.
383 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700384 long getCreationTimeMillis() {
385 return mCreationTimeMillis;
386 }
387
388 long getConnectTimeMillis() {
389 return mConnectTimeMillis;
390 }
391
392 void setConnectTimeMillis(long connectTimeMillis) {
393 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800394 }
395
Santos Cordona1610702014-06-04 20:22:56 -0700396 boolean isConferenceCapable() {
397 return mIsConferenceCapable;
398 }
399
400 void setIsConferenceCapable(boolean isConferenceCapable) {
401 if (mIsConferenceCapable != isConferenceCapable) {
402 mIsConferenceCapable = isConferenceCapable;
403 for (Listener l : mListeners) {
404 l.onIsConferenceCapableChanged(this, mIsConferenceCapable);
405 }
406 }
407 }
408
409 Call getParentCall() {
410 return mParentCall;
411 }
412
413 List<Call> getChildCalls() {
414 return mChildCalls;
415 }
416
Santos Cordonc195e362014-02-11 17:05:31 -0800417 CallServiceWrapper getCallService() {
Santos Cordon681663d2014-01-30 04:32:15 -0800418 return mCallService;
419 }
420
Santos Cordonc195e362014-02-11 17:05:31 -0800421 void setCallService(CallServiceWrapper callService) {
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700422 setCallService(callService, null);
423 }
424
425 /**
426 * Changes the call service this call is associated with. If callToReplace is non-null then this
427 * call takes its place within the call service.
428 */
429 void setCallService(CallServiceWrapper callService, Call callToReplace) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800430 Preconditions.checkNotNull(callService);
431
Yorke Leeadee12d2014-03-13 12:08:30 -0700432 clearCallService();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800433
434 callService.incrementAssociatedCallCount();
Santos Cordon681663d2014-01-30 04:32:15 -0800435 mCallService = callService;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700436 if (callToReplace == null) {
437 mCallService.addCall(this);
438 } else {
439 mCallService.replaceCall(this, callToReplace);
440 }
Santos Cordon681663d2014-01-30 04:32:15 -0800441 }
442
443 /**
444 * Clears the associated call service.
445 */
446 void clearCallService() {
Yorke Leeadee12d2014-03-13 12:08:30 -0700447 if (mCallService != null) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700448 CallServiceWrapper callServiceTemp = mCallService;
Yorke Leeadee12d2014-03-13 12:08:30 -0700449 mCallService = null;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700450 callServiceTemp.removeCall(this);
451
452 // Decrementing the count can cause the service to unbind, which itself can trigger the
453 // service-death code. Since the service death code tries to clean up any associated
454 // calls, we need to make sure to remove that information (e.g., removeCall()) before
455 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
456 // necessary, but cleaning up mCallService prior to triggering an unbind is good to do.
457 // If you change this, make sure to update {@link clearCallServiceSelector} as well.
458 decrementAssociatedCallCount(callServiceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700459 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800460 }
461
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800462 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700463 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
464 * invoke handle[Un]SuccessfulIncomingCall.
465 *
466 * @param descriptor The relevant call-service descriptor.
467 * @param extras The optional extras passed via
468 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
469 */
470 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
471 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
472 }
473
Santos Cordon2174fb52014-05-29 08:22:56 -0700474 /**
475 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
476 * from the contacts provider. The call is not yet exposed to the user at this point and
477 * the result of the query will determine if the call is rejected or passed through to the
478 * in-call UI.
479 */
480 void handleVerifiedIncoming(CallInfo callInfo) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700481 Preconditions.checkState(callInfo.getState() == CallState.RINGING);
Santos Cordon2174fb52014-05-29 08:22:56 -0700482
483 // We do not handle incoming calls immediately when they are verified by the call service.
484 // We allow the caller-info-query code to execute first so that we can read the
485 // direct-to-voicemail property before deciding if we want to show the incoming call to the
486 // user or if we want to reject the call.
487 mPendingDirectToVoicemailCallInfo = callInfo;
488
489 // Setting the handle triggers the caller info lookup code.
Santos Cordon766d04f2014-05-06 10:28:25 -0700490 setHandle(callInfo.getHandle());
491
Santos Cordon2174fb52014-05-29 08:22:56 -0700492 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
493 // showing the user the incoming call screen.
494 mHandler.postDelayed(new Runnable() {
495 @Override
496 public void run() {
497 processDirectToVoicemail();
498 }
Santos Cordona1610702014-06-04 20:22:56 -0700499 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700500 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700501
Santos Cordon2174fb52014-05-29 08:22:56 -0700502 void processDirectToVoicemail() {
503 if (mPendingDirectToVoicemailCallInfo != null) {
504 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
505 Log.i(this, "Directing call to voicemail: %s.", this);
506 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
507 // will not need to set RINGING state prior to calling reject.
508 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700509 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700510 } else {
511 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
512 // the call state to RINGING.
513
514 // TODO(santoscordon): Replace this with state transition to RINGING.
515 for (Listener l : mListeners) {
516 l.onSuccessfulIncomingCall(this, mPendingDirectToVoicemailCallInfo);
517 }
518 }
519
520 mPendingDirectToVoicemailCallInfo = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700521 }
522 }
523
524 void handleFailedIncoming() {
525 clearCallService();
526
527 // TODO: Needs more specific disconnect error for this case.
528 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
529 setState(CallState.DISCONNECTED);
530
531 // TODO(santoscordon): Replace this with state transitions related to "connecting".
532 for (Listener l : mListeners) {
533 l.onFailedIncomingCall(this);
534 }
535 }
536
537 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700538 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
539 * through a call service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700540 */
541 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700542 Preconditions.checkState(mOutgoingCallProcessor == null);
543
544 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700545 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700546 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700547 }
548
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700549 @Override
550 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700551 // TODO(santoscordon): Replace this with state transitions related to "connecting".
552 for (Listener l : mListeners) {
553 l.onSuccessfulOutgoingCall(this);
554 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700555 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700556 }
557
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700558 @Override
559 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700560 // TODO(santoscordon): Replace this with state transitions related to "connecting".
561 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700562 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700563 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700564
565 clearCallService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700566 mOutgoingCallProcessor = null;
567 }
568
569 @Override
570 public void onOutgoingCallCancel() {
571 // TODO(santoscordon): Replace this with state transitions related to "connecting".
572 for (Listener l : mListeners) {
573 l.onCancelledOutgoingCall(this);
574 }
575
576 clearCallService();
577 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700578 }
579
580 /**
Ben Gilad61925612014-03-11 19:06:36 -0700581 * Adds the specified call service to the list of incompatible services. The set is used when
582 * attempting to switch a phone call between call services such that incompatible services can
583 * be avoided.
584 *
585 * @param callService The incompatible call service.
586 */
587 void addIncompatibleCallService(CallServiceWrapper callService) {
588 if (mIncompatibleCallServices == null) {
589 mIncompatibleCallServices = Sets.newHashSet();
590 }
591 mIncompatibleCallServices.add(callService);
592 }
593
594 /**
595 * Checks whether or not the specified callService was identified as incompatible in the
596 * context of this call.
597 *
598 * @param callService The call service to evaluate.
599 * @return True upon incompatible call services and false otherwise.
600 */
601 boolean isIncompatibleCallService(CallServiceWrapper callService) {
602 return mIncompatibleCallServices != null &&
603 mIncompatibleCallServices.contains(callService);
604 }
605
606 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700607 * Plays the specified DTMF tone.
608 */
609 void playDtmfTone(char digit) {
610 if (mCallService == null) {
611 Log.w(this, "playDtmfTone() request on a call without a call service.");
612 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700613 Log.i(this, "Send playDtmfTone to call service for call %s", this);
614 mCallService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700615 }
616 }
617
618 /**
619 * Stops playing any currently playing DTMF tone.
620 */
621 void stopDtmfTone() {
622 if (mCallService == null) {
623 Log.w(this, "stopDtmfTone() request on a call without a call service.");
624 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700625 Log.i(this, "Send stopDtmfTone to call service for call %s", this);
626 mCallService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700627 }
628 }
629
630 /**
Santos Cordon049b7b62014-01-30 05:34:26 -0800631 * Attempts to disconnect the call through the call service.
632 */
633 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700634 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700635 Log.v(this, "Aborting call %s", this);
636 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700637 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700638 Preconditions.checkNotNull(mCallService);
639
Sailesh Nepale59bb192014-04-01 18:33:59 -0700640 Log.i(this, "Send disconnect to call service for call: %s", this);
Santos Cordonc195e362014-02-11 17:05:31 -0800641 // The call isn't officially disconnected until the call service confirms that the call
642 // was actually disconnected. Only then is the association between call and call service
643 // severed, see {@link CallsManager#markCallAsDisconnected}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700644 mCallService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800645 }
646 }
647
Santos Cordon682fe6b2014-05-20 08:56:39 -0700648 void abort() {
649 if (mOutgoingCallProcessor != null) {
650 mOutgoingCallProcessor.abort();
651 }
652 }
653
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800654 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800655 * Answers the call if it is ringing.
656 */
657 void answer() {
658 Preconditions.checkNotNull(mCallService);
659
660 // Check to verify that the call is still in the ringing state. A call can change states
661 // between the time the user hits 'answer' and Telecomm receives the command.
662 if (isRinging("answer")) {
663 // At this point, we are asking the call service to answer but we don't assume that
664 // it will work. Instead, we wait until confirmation from the call service that the
665 // call is in a non-RINGING state before changing the UI. See
666 // {@link CallServiceAdapter#setActive} and other set* methods.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700667 mCallService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800668 }
669 }
670
671 /**
672 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700673 *
674 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
675 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800676 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700677 void reject(boolean rejectWithMessage, String textMessage) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800678 Preconditions.checkNotNull(mCallService);
679
680 // Check to verify that the call is still in the ringing state. A call can change states
681 // between the time the user hits 'reject' and Telecomm receives the command.
682 if (isRinging("reject")) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700683 mCallService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800684 }
685 }
686
687 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700688 * Puts the call on hold if it is currently active.
689 */
690 void hold() {
691 Preconditions.checkNotNull(mCallService);
692
693 if (mState == CallState.ACTIVE) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700694 mCallService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700695 }
696 }
697
698 /**
699 * Releases the call from hold if it is currently active.
700 */
701 void unhold() {
702 Preconditions.checkNotNull(mCallService);
703
704 if (mState == CallState.ON_HOLD) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700705 mCallService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700706 }
707 }
708
709 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800710 * @return An object containing read-only information about this call.
711 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700712 CallInfo toCallInfo(String callId) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700713 CallServiceDescriptor descriptor = null;
714 if (mCallService != null) {
715 descriptor = mCallService.getDescriptor();
716 } else if (mOriginalCall != null && mOriginalCall.mCallService != null) {
717 descriptor = mOriginalCall.mCallService.getDescriptor();
718 }
Santos Cordon571f0732014-06-25 18:13:15 -0700719 Bundle extras = mExtras;
720 if (mGatewayInfo != null && mGatewayInfo.getGatewayProviderPackageName() != null &&
721 mGatewayInfo.getOriginalHandle() != null) {
722 extras = (Bundle) mExtras.clone();
723 extras.putString(
724 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
725 mGatewayInfo.getGatewayProviderPackageName());
726 extras.putParcelable(
727 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI,
728 mGatewayInfo.getOriginalHandle());
729
730 }
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700731 return new CallInfo(callId, mState, mHandle, mGatewayInfo, mSubscription,
732 extras, descriptor);
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800733 }
734
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700735 /** Checks if this is a live call or not. */
736 boolean isAlive() {
737 switch (mState) {
738 case NEW:
739 case RINGING:
740 case DISCONNECTED:
741 case ABORTED:
742 return false;
743 default:
744 return true;
745 }
746 }
747
Santos Cordon40f78c22014-04-07 02:11:42 -0700748 boolean isActive() {
749 switch (mState) {
750 case ACTIVE:
751 case POST_DIAL:
752 case POST_DIAL_WAIT:
753 return true;
754 default:
755 return false;
756 }
757 }
758
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700759 Bundle getExtras() {
760 return mExtras;
761 }
762
763 void setExtras(Bundle extras) {
764 mExtras = extras;
765 }
766
767 Uri getHandoffHandle() {
768 return mHandoffHandle;
769 }
770
771 void setHandoffHandle(Uri handoffHandle) {
772 mHandoffHandle = handoffHandle;
773 }
774
775 Call getOriginalCall() {
776 return mOriginalCall;
777 }
778
779 void setOriginalCall(Call originalCall) {
780 mOriginalCall = originalCall;
781 }
782
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700783 CallServiceDescriptor getHandoffCallServiceDescriptor() {
784 return mHandoffCallServiceDescriptor;
785 }
786
787 void setHandoffCallServiceDescriptor(CallServiceDescriptor descriptor) {
788 mHandoffCallServiceDescriptor = descriptor;
789 }
790
Santos Cordon5ba7f272014-05-28 13:59:49 -0700791 Uri getRingtone() {
792 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
793 }
794
Evan Charlton352105c2014-06-03 14:10:54 -0700795 void onPostDialWait(String remaining) {
796 for (Listener l : mListeners) {
797 l.onPostDialWait(this, remaining);
798 }
799 }
800
801 void postDialContinue(boolean proceed) {
802 getCallService().onPostDialContinue(this, proceed);
803 }
804
Santos Cordona1610702014-06-04 20:22:56 -0700805 void conferenceInto(Call conferenceCall) {
806 if (mCallService == null) {
807 Log.w(this, "conference requested on a call without a call service.");
808 } else {
809 mCallService.conference(conferenceCall, this);
810 }
811 }
812
813 void expireConference() {
814 // The conference call expired before we got a confirmation of the conference from the
815 // call service...so start shutting down.
816 clearCallService();
817 for (Listener l : mListeners) {
818 l.onExpiredConferenceCall(this);
819 }
820 }
821
822 void confirmConference() {
823 Log.v(this, "confirming Conf call %s", mListeners);
824 for (Listener l : mListeners) {
825 l.onConfirmedConferenceCall(this);
826 }
827 }
828
829 void splitFromConference() {
830 // TODO(santoscordon): todo
831 }
832
833 void setParentCall(Call parentCall) {
834 if (parentCall == this) {
835 Log.e(this, new Exception(), "setting the parent to self");
836 return;
837 }
838 Preconditions.checkState(parentCall == null || mParentCall == null);
839
840 Call oldParent = mParentCall;
841 if (mParentCall != null) {
842 mParentCall.removeChildCall(this);
843 }
844 mParentCall = parentCall;
845 if (mParentCall != null) {
846 mParentCall.addChildCall(this);
847 }
848
849 for (Listener l : mListeners) {
850 l.onParentChanged(this);
851 }
852 }
853
854 private void addChildCall(Call call) {
855 if (!mChildCalls.contains(call)) {
856 mChildCalls.add(call);
857
858 for (Listener l : mListeners) {
859 l.onChildrenChanged(this);
860 }
861 }
862 }
863
864 private void removeChildCall(Call call) {
865 if (mChildCalls.remove(call)) {
866 for (Listener l : mListeners) {
867 l.onChildrenChanged(this);
868 }
869 }
870 }
871
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800872 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700873 * Return whether the user can respond to this {@code Call} via an SMS message.
874 *
875 * @return true if the "Respond via SMS" feature should be enabled
876 * for this incoming call.
877 *
878 * The general rule is that we *do* allow "Respond via SMS" except for
879 * the few (relatively rare) cases where we know for sure it won't
880 * work, namely:
881 * - a bogus or blank incoming number
882 * - a call from a SIP address
883 * - a "call presentation" that doesn't allow the number to be revealed
884 *
885 * In all other cases, we allow the user to respond via SMS.
886 *
887 * Note that this behavior isn't perfect; for example we have no way
888 * to detect whether the incoming call is from a landline (with most
889 * networks at least), so we still enable this feature even though
890 * SMSes to that number will silently fail.
891 */
892 boolean isRespondViaSmsCapable() {
893 if (mState != CallState.RINGING) {
894 return false;
895 }
896
897 if (getHandle() == null) {
898 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
899 // other words, the user should not be able to see the incoming phone number.
900 return false;
901 }
902
903 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
904 // The incoming number is actually a URI (i.e. a SIP address),
905 // not a regular PSTN phone number, and we can't send SMSes to
906 // SIP addresses.
907 // (TODO: That might still be possible eventually, though. Is
908 // there some SIP-specific equivalent to sending a text message?)
909 return false;
910 }
911
912 // Is there a valid SMS application on the phone?
913 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
914 true /*updateIfNeeded*/) == null) {
915 return false;
916 }
917
918 // TODO: with some carriers (in certain countries) you *can* actually
919 // tell whether a given number is a mobile phone or not. So in that
920 // case we could potentially return false here if the incoming call is
921 // from a land line.
922
923 // If none of the above special cases apply, it's OK to enable the
924 // "Respond via SMS" feature.
925 return true;
926 }
927
928 List<String> getCannedSmsResponses() {
929 return mCannedSmsResponses;
930 }
931
932 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800933 * @return True if the call is ringing, else logs the action name.
934 */
935 private boolean isRinging(String actionName) {
936 if (mState == CallState.RINGING) {
937 return true;
938 }
939
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800940 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800941 return false;
942 }
943
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800944 @SuppressWarnings("rawtypes")
945 private void decrementAssociatedCallCount(ServiceBinder binder) {
946 if (binder != null) {
947 binder.decrementAssociatedCallCount();
948 }
949 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700950
951 /**
952 * Looks up contact information based on the current handle.
953 */
954 private void startCallerInfoLookup() {
955 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
956
957 mQueryToken++; // Updated so that previous queries can no longer set the information.
958 mCallerInfo = null;
959 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700960 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700961 CallerInfoAsyncQuery.startQuery(
962 mQueryToken,
963 TelecommApp.getInstance(),
964 number,
965 sCallerInfoQueryListener,
966 this);
967 }
968 }
969
970 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700971 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700972 * that was made.
973 *
974 * @param callerInfo The new caller information to set.
975 * @param token The token used with this query.
976 */
977 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700978 Preconditions.checkNotNull(callerInfo);
979
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700980 if (mQueryToken == token) {
981 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700982 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700983
984 if (mCallerInfo.person_id != 0) {
985 Uri personUri =
986 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
987 Log.d(this, "Searching person uri %s for call %s", personUri, this);
988 ContactsAsyncHelper.startObtainPhotoAsync(
989 token,
990 TelecommApp.getInstance(),
991 personUri,
992 sPhotoLoadListener,
993 this);
994 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700995
996 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700997 }
998 }
999
1000 /**
1001 * Saves the specified photo information if the specified token matches that of the last query.
1002 *
1003 * @param photo The photo as a drawable.
1004 * @param photoIcon The photo as a small icon.
1005 * @param token The token used with this query.
1006 */
1007 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1008 if (mQueryToken == token) {
1009 mCallerInfo.cachedPhoto = photo;
1010 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001011 }
1012 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001013
1014 private void maybeLoadCannedSmsResponses() {
1015 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1016 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1017 mCannedSmsResponsesLoadingStarted = true;
1018 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1019 new Response<Void, List<String>>() {
1020 @Override
1021 public void onResult(Void request, List<String>... result) {
1022 if (result.length > 0) {
1023 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1024 mCannedSmsResponses = result[0];
1025 for (Listener l : mListeners) {
1026 l.onCannedSmsResponsesLoaded(Call.this);
1027 }
1028 }
1029 }
1030
1031 @Override
1032 public void onError(Void request, int code, String msg) {
1033 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1034 msg);
1035 }
1036 }
1037 );
1038 } else {
1039 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1040 }
1041 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001042
1043 /**
1044 * Sets speakerphone option on when call begins.
1045 */
1046 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1047 mSpeakerphoneOn = startWithSpeakerphone;
1048 }
1049
1050 /**
1051 * Returns speakerphone option.
1052 *
1053 * @return Whether or not speakerphone should be set automatically when call begins.
1054 */
1055 public boolean getStartWithSpeakerphoneOn() {
1056 return mSpeakerphoneOn;
1057 }
Andrew Leee9a77652014-06-26 13:07:57 -07001058
1059 /**
1060 * Sets a call video provider for the call.
1061 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001062 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1063 mCallVideoProvider = callVideoProvider;
1064 for (Listener l : mListeners) {
1065 l.onCallVideoProviderChanged(Call.this);
1066 }
1067 }
1068
1069 /**
1070 * @return Return the call video Provider binder.
1071 */
1072 public ICallVideoProvider getCallVideoProvider() {
1073 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001074 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001075
1076 /**
1077 * Returns the features of this call.
1078 *
1079 * @return The features of this call.
1080 */
1081 public int getFeatures() {
1082 return mFeatures;
1083 }
1084
1085 /**
1086 * Set the features associated with the call and notify any listeners of the change.
1087 *
1088 * @param features The features.
1089 */
1090 public void setFeatures(int features) {
1091 Log.d(this, "setFeatures: %d", features);
1092 mFeatures = features;
1093 for (Listener l : mListeners) {
1094 l.onFeaturesChanged(Call.this);
1095 }
1096 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001097}