blob: 4265c6556d2a554eea675317f471425ae7c11c75 [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);
Santos Cordon766d04f2014-05-06 10:28:25 -070077 }
78
Santos Cordonfd71f4a2014-05-28 13:59:49 -070079 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -070080 new OnQueryCompleteListener() {
81 /** ${inheritDoc} */
82 @Override
83 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
84 if (cookie != null) {
85 ((Call) cookie).setCallerInfo(callerInfo, token);
86 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -070087 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -070088 };
89
90 private static final OnImageLoadCompleteListener sPhotoLoadListener =
91 new OnImageLoadCompleteListener() {
92 /** ${inheritDoc} */
93 @Override
94 public void onImageLoadComplete(
95 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
96 if (cookie != null) {
97 ((Call) cookie).setPhoto(photo, photoIcon, token);
98 }
99 }
100 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800101
Sailesh Nepal810735e2014-03-18 18:15:46 -0700102 /** True if this is an incoming call. */
103 private final boolean mIsIncoming;
104
Ben Gilad0407fb22014-01-09 16:18:41 -0800105 /**
106 * The time this call was created, typically also the time this call was added to the set
107 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
108 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
109 * certain call attempts as failed attempts.
110 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700111 private final long mCreationTimeMillis = System.currentTimeMillis();
112
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700113 /** The gateway information associated with this call. This stores the original call handle
114 * that the user is attempting to connect to via the gateway, the actual handle to dial in
115 * order to connect the call via the gateway, as well as the package name of the gateway
116 * service. */
117 private final GatewayInfo mGatewayInfo;
118
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700119 private final Subscription mSubscription;
120
Santos Cordon2174fb52014-05-29 08:22:56 -0700121 private final Handler mHandler = new Handler();
122
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700123 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800124
Santos Cordon61d0f702014-02-19 02:52:23 -0800125 /** The state of the call. */
126 private CallState mState;
127
128 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700129 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800130
Ben Gilad0407fb22014-01-09 16:18:41 -0800131 /**
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800132 * The call service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800133 */
Santos Cordonc195e362014-02-11 17:05:31 -0800134 private CallServiceWrapper mCallService;
Santos Cordon681663d2014-01-30 04:32:15 -0800135
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800136 /**
Ben Gilad61925612014-03-11 19:06:36 -0700137 * The set of call services that were attempted in the process of placing/switching this call
138 * but turned out unsuitable. Only used in the context of call switching.
139 */
140 private Set<CallServiceWrapper> mIncompatibleCallServices;
141
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700142 private boolean mIsEmergencyCall;
143
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700144 private boolean mSpeakerphoneOn;
145
Ben Gilad61925612014-03-11 19:06:36 -0700146 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700147 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
148 * See {@link android.telephony.DisconnectCause}.
149 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700150 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700151
152 /**
153 * Additional disconnect information provided by the call service.
154 */
155 private String mDisconnectMessage;
156
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700157 /** Info used by the call services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700158 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700159
160 /** The Uri to dial to perform the handoff. If this is null then handoff is not supported. */
161 private Uri mHandoffHandle;
162
163 /**
164 * References the call that is being handed off. This value is non-null for untracked calls
165 * that are being used to perform a handoff.
166 */
167 private Call mOriginalCall;
168
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700169 /**
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700170 * The descriptor for the call service that this call is being switched to, null if handoff is
171 * not in progress.
172 */
173 private CallServiceDescriptor mHandoffCallServiceDescriptor;
174
Santos Cordon766d04f2014-05-06 10:28:25 -0700175 /** Set of listeners on this call. */
176 private Set<Listener> mListeners = Sets.newHashSet();
177
Santos Cordon682fe6b2014-05-20 08:56:39 -0700178 private OutgoingCallProcessor mOutgoingCallProcessor;
179
180 // TODO(santoscordon): The repositories should be changed into singleton types.
181 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700182
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700183 /** Caller information retrieved from the latest contact query. */
184 private CallerInfo mCallerInfo;
185
186 /** The latest token used with a contact info query. */
187 private int mQueryToken = 0;
188
Ihab Awadcb387ac2014-05-28 16:49:38 -0700189 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
190 private boolean mRequestingRingback = false;
191
Santos Cordon2174fb52014-05-29 08:22:56 -0700192 /** Incoming call-info to use when direct-to-voicemail query finishes. */
193 private CallInfo mPendingDirectToVoicemailCallInfo;
194
Santos Cordona1610702014-06-04 20:22:56 -0700195 private boolean mIsConferenceCapable = false;
196
197 private boolean mIsConference = false;
198
199 private Call mParentCall = null;
200
201 private List<Call> mChildCalls = new LinkedList<>();
202
Ihab Awadff7493a2014-06-10 13:47:44 -0700203 /** Set of text message responses allowed for this call, if applicable. */
204 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
205
206 /** Whether an attempt has been made to load the text message responses. */
207 private boolean mCannedSmsResponsesLoadingStarted = false;
208
Nancy Chena65d41f2014-06-24 12:06:03 -0700209 private ICallVideoProvider mCallVideoProvider;
210
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700211 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700212 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700213 *
214 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800215 */
Santos Cordona1610702014-06-04 20:22:56 -0700216 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700217 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800218 }
219
220 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800221 * Persists the specified parameters and initializes the new instance.
222 *
223 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700224 * @param gatewayInfo Gateway information to use for the call.
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700225 * @param subscription Subscription information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700226 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800227 */
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700228 Call(Uri handle, GatewayInfo gatewayInfo, Subscription subscription,
229 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700230 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700231 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700232 mGatewayInfo = gatewayInfo;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700233 mSubscription = subscription;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700234 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700235 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700236 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800237 }
238
Santos Cordon766d04f2014-05-06 10:28:25 -0700239 void addListener(Listener listener) {
240 mListeners.add(listener);
241 }
242
243 void removeListener(Listener listener) {
244 mListeners.remove(listener);
245 }
246
Santos Cordon61d0f702014-02-19 02:52:23 -0800247 /** {@inheritDoc} */
248 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700249 String component = null;
250 if (mCallService != null && mCallService.getComponentName() != null) {
251 component = mCallService.getComponentName().flattenToShortString();
252 }
253 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800254 }
255
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800256 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700257 if (mIsConference) {
258 if (!mChildCalls.isEmpty()) {
259 // If we have child calls, just return the child call.
260 return mChildCalls.get(0).getState();
261 }
262 return CallState.ACTIVE;
263 } else {
264 return mState;
265 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800266 }
267
268 /**
269 * Sets the call state. Although there exists the notion of appropriate state transitions
270 * (see {@link CallState}), in practice those expectations break down when cellular systems
271 * misbehave and they do this very often. The result is that we do not enforce state transitions
272 * and instead keep the code resilient to unexpected state changes.
273 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700274 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700275 Preconditions.checkState(newState != CallState.DISCONNECTED ||
276 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700277 if (mState != newState) {
278 Log.v(this, "setState %s -> %s", mState, newState);
279 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700280 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700281 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800282 }
283
Ihab Awadcb387ac2014-05-28 16:49:38 -0700284 void setRequestingRingback(boolean requestingRingback) {
285 mRequestingRingback = requestingRingback;
286 for (Listener l : mListeners) {
287 l.onRequestingRingback(this, mRequestingRingback);
288 }
289 }
290
291 boolean isRequestingRingback() {
292 return mRequestingRingback;
293 }
294
Sailesh Nepalce704b92014-03-17 18:31:43 -0700295 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800296 return mHandle;
297 }
298
Sailesh Nepalce704b92014-03-17 18:31:43 -0700299 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700300 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
301 mHandle = handle;
302 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700303 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700304 startCallerInfoLookup();
305 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700306 }
307
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700308 String getName() {
309 return mCallerInfo == null ? null : mCallerInfo.name;
310 }
311
312 Bitmap getPhotoIcon() {
313 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
314 }
315
316 Drawable getPhoto() {
317 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
318 }
319
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700320 /**
321 * @param disconnectCause The reason for the disconnection, any of
322 * {@link android.telephony.DisconnectCause}.
323 * @param disconnectMessage Optional call-service-provided message about the disconnect.
324 */
325 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
326 // TODO: Consider combining this method with a setDisconnected() method that is totally
327 // separate from setState.
328 mDisconnectCause = disconnectCause;
329 mDisconnectMessage = disconnectMessage;
330 }
331
332 int getDisconnectCause() {
333 return mDisconnectCause;
334 }
335
336 String getDisconnectMessage() {
337 return mDisconnectMessage;
338 }
339
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700340 boolean isEmergencyCall() {
341 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800342 }
343
Yorke Lee33501632014-03-17 19:24:12 -0700344 /**
345 * @return The original handle this call is associated with. In-call services should use this
346 * handle when indicating in their UI the handle that is being called.
347 */
348 public Uri getOriginalHandle() {
349 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
350 return mGatewayInfo.getOriginalHandle();
351 }
352 return getHandle();
353 }
354
355 GatewayInfo getGatewayInfo() {
356 return mGatewayInfo;
357 }
358
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700359 Subscription getSubscription() {
360 return mSubscription;
361 }
362
Sailesh Nepal810735e2014-03-18 18:15:46 -0700363 boolean isIncoming() {
364 return mIsIncoming;
365 }
366
Ben Gilad0407fb22014-01-09 16:18:41 -0800367 /**
368 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700369 * period since this call was added to the set pending outgoing calls, see
370 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800371 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700372 long getAgeMillis() {
373 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800374 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800375
Yorke Leef98fb572014-03-05 10:56:55 -0800376 /**
377 * @return The time when this call object was created and added to the set of pending outgoing
378 * calls.
379 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700380 long getCreationTimeMillis() {
381 return mCreationTimeMillis;
382 }
383
384 long getConnectTimeMillis() {
385 return mConnectTimeMillis;
386 }
387
388 void setConnectTimeMillis(long connectTimeMillis) {
389 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800390 }
391
Santos Cordona1610702014-06-04 20:22:56 -0700392 boolean isConferenceCapable() {
393 return mIsConferenceCapable;
394 }
395
396 void setIsConferenceCapable(boolean isConferenceCapable) {
397 if (mIsConferenceCapable != isConferenceCapable) {
398 mIsConferenceCapable = isConferenceCapable;
399 for (Listener l : mListeners) {
400 l.onIsConferenceCapableChanged(this, mIsConferenceCapable);
401 }
402 }
403 }
404
405 Call getParentCall() {
406 return mParentCall;
407 }
408
409 List<Call> getChildCalls() {
410 return mChildCalls;
411 }
412
Santos Cordonc195e362014-02-11 17:05:31 -0800413 CallServiceWrapper getCallService() {
Santos Cordon681663d2014-01-30 04:32:15 -0800414 return mCallService;
415 }
416
Santos Cordonc195e362014-02-11 17:05:31 -0800417 void setCallService(CallServiceWrapper callService) {
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700418 setCallService(callService, null);
419 }
420
421 /**
422 * Changes the call service this call is associated with. If callToReplace is non-null then this
423 * call takes its place within the call service.
424 */
425 void setCallService(CallServiceWrapper callService, Call callToReplace) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800426 Preconditions.checkNotNull(callService);
427
Yorke Leeadee12d2014-03-13 12:08:30 -0700428 clearCallService();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800429
430 callService.incrementAssociatedCallCount();
Santos Cordon681663d2014-01-30 04:32:15 -0800431 mCallService = callService;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700432 if (callToReplace == null) {
433 mCallService.addCall(this);
434 } else {
435 mCallService.replaceCall(this, callToReplace);
436 }
Santos Cordon681663d2014-01-30 04:32:15 -0800437 }
438
439 /**
440 * Clears the associated call service.
441 */
442 void clearCallService() {
Yorke Leeadee12d2014-03-13 12:08:30 -0700443 if (mCallService != null) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700444 CallServiceWrapper callServiceTemp = mCallService;
Yorke Leeadee12d2014-03-13 12:08:30 -0700445 mCallService = null;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700446 callServiceTemp.removeCall(this);
447
448 // Decrementing the count can cause the service to unbind, which itself can trigger the
449 // service-death code. Since the service death code tries to clean up any associated
450 // calls, we need to make sure to remove that information (e.g., removeCall()) before
451 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
452 // necessary, but cleaning up mCallService prior to triggering an unbind is good to do.
453 // If you change this, make sure to update {@link clearCallServiceSelector} as well.
454 decrementAssociatedCallCount(callServiceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700455 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800456 }
457
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800458 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700459 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
460 * invoke handle[Un]SuccessfulIncomingCall.
461 *
462 * @param descriptor The relevant call-service descriptor.
463 * @param extras The optional extras passed via
464 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
465 */
466 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
467 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
468 }
469
Santos Cordon2174fb52014-05-29 08:22:56 -0700470 /**
471 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
472 * from the contacts provider. The call is not yet exposed to the user at this point and
473 * the result of the query will determine if the call is rejected or passed through to the
474 * in-call UI.
475 */
476 void handleVerifiedIncoming(CallInfo callInfo) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700477 Preconditions.checkState(callInfo.getState() == CallState.RINGING);
Santos Cordon2174fb52014-05-29 08:22:56 -0700478
479 // We do not handle incoming calls immediately when they are verified by the call service.
480 // We allow the caller-info-query code to execute first so that we can read the
481 // direct-to-voicemail property before deciding if we want to show the incoming call to the
482 // user or if we want to reject the call.
483 mPendingDirectToVoicemailCallInfo = callInfo;
484
485 // Setting the handle triggers the caller info lookup code.
Santos Cordon766d04f2014-05-06 10:28:25 -0700486 setHandle(callInfo.getHandle());
487
Santos Cordon2174fb52014-05-29 08:22:56 -0700488 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
489 // showing the user the incoming call screen.
490 mHandler.postDelayed(new Runnable() {
491 @Override
492 public void run() {
493 processDirectToVoicemail();
494 }
Santos Cordona1610702014-06-04 20:22:56 -0700495 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700496 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700497
Santos Cordon2174fb52014-05-29 08:22:56 -0700498 void processDirectToVoicemail() {
499 if (mPendingDirectToVoicemailCallInfo != null) {
500 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
501 Log.i(this, "Directing call to voicemail: %s.", this);
502 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
503 // will not need to set RINGING state prior to calling reject.
504 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700505 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700506 } else {
507 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
508 // the call state to RINGING.
509
510 // TODO(santoscordon): Replace this with state transition to RINGING.
511 for (Listener l : mListeners) {
512 l.onSuccessfulIncomingCall(this, mPendingDirectToVoicemailCallInfo);
513 }
514 }
515
516 mPendingDirectToVoicemailCallInfo = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700517 }
518 }
519
520 void handleFailedIncoming() {
521 clearCallService();
522
523 // TODO: Needs more specific disconnect error for this case.
524 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
525 setState(CallState.DISCONNECTED);
526
527 // TODO(santoscordon): Replace this with state transitions related to "connecting".
528 for (Listener l : mListeners) {
529 l.onFailedIncomingCall(this);
530 }
531 }
532
533 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700534 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
535 * through a call service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700536 */
537 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700538 Preconditions.checkState(mOutgoingCallProcessor == null);
539
540 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700541 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700542 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700543 }
544
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700545 @Override
546 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700547 // TODO(santoscordon): Replace this with state transitions related to "connecting".
548 for (Listener l : mListeners) {
549 l.onSuccessfulOutgoingCall(this);
550 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700551 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700552 }
553
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700554 @Override
555 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700556 // TODO(santoscordon): Replace this with state transitions related to "connecting".
557 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700558 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700559 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700560
561 clearCallService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700562 mOutgoingCallProcessor = null;
563 }
564
565 @Override
566 public void onOutgoingCallCancel() {
567 // TODO(santoscordon): Replace this with state transitions related to "connecting".
568 for (Listener l : mListeners) {
569 l.onCancelledOutgoingCall(this);
570 }
571
572 clearCallService();
573 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700574 }
575
576 /**
Ben Gilad61925612014-03-11 19:06:36 -0700577 * Adds the specified call service to the list of incompatible services. The set is used when
578 * attempting to switch a phone call between call services such that incompatible services can
579 * be avoided.
580 *
581 * @param callService The incompatible call service.
582 */
583 void addIncompatibleCallService(CallServiceWrapper callService) {
584 if (mIncompatibleCallServices == null) {
585 mIncompatibleCallServices = Sets.newHashSet();
586 }
587 mIncompatibleCallServices.add(callService);
588 }
589
590 /**
591 * Checks whether or not the specified callService was identified as incompatible in the
592 * context of this call.
593 *
594 * @param callService The call service to evaluate.
595 * @return True upon incompatible call services and false otherwise.
596 */
597 boolean isIncompatibleCallService(CallServiceWrapper callService) {
598 return mIncompatibleCallServices != null &&
599 mIncompatibleCallServices.contains(callService);
600 }
601
602 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700603 * Plays the specified DTMF tone.
604 */
605 void playDtmfTone(char digit) {
606 if (mCallService == null) {
607 Log.w(this, "playDtmfTone() request on a call without a call service.");
608 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700609 Log.i(this, "Send playDtmfTone to call service for call %s", this);
610 mCallService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700611 }
612 }
613
614 /**
615 * Stops playing any currently playing DTMF tone.
616 */
617 void stopDtmfTone() {
618 if (mCallService == null) {
619 Log.w(this, "stopDtmfTone() request on a call without a call service.");
620 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700621 Log.i(this, "Send stopDtmfTone to call service for call %s", this);
622 mCallService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700623 }
624 }
625
626 /**
Santos Cordon049b7b62014-01-30 05:34:26 -0800627 * Attempts to disconnect the call through the call service.
628 */
629 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700630 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700631 Log.v(this, "Aborting call %s", this);
632 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700633 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700634 Preconditions.checkNotNull(mCallService);
635
Sailesh Nepale59bb192014-04-01 18:33:59 -0700636 Log.i(this, "Send disconnect to call service for call: %s", this);
Santos Cordonc195e362014-02-11 17:05:31 -0800637 // The call isn't officially disconnected until the call service confirms that the call
638 // was actually disconnected. Only then is the association between call and call service
639 // severed, see {@link CallsManager#markCallAsDisconnected}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700640 mCallService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800641 }
642 }
643
Santos Cordon682fe6b2014-05-20 08:56:39 -0700644 void abort() {
645 if (mOutgoingCallProcessor != null) {
646 mOutgoingCallProcessor.abort();
647 }
648 }
649
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800650 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800651 * Answers the call if it is ringing.
652 */
653 void answer() {
654 Preconditions.checkNotNull(mCallService);
655
656 // Check to verify that the call is still in the ringing state. A call can change states
657 // between the time the user hits 'answer' and Telecomm receives the command.
658 if (isRinging("answer")) {
659 // At this point, we are asking the call service to answer but we don't assume that
660 // it will work. Instead, we wait until confirmation from the call service that the
661 // call is in a non-RINGING state before changing the UI. See
662 // {@link CallServiceAdapter#setActive} and other set* methods.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700663 mCallService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800664 }
665 }
666
667 /**
668 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700669 *
670 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
671 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800672 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700673 void reject(boolean rejectWithMessage, String textMessage) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800674 Preconditions.checkNotNull(mCallService);
675
676 // Check to verify that the call is still in the ringing state. A call can change states
677 // between the time the user hits 'reject' and Telecomm receives the command.
678 if (isRinging("reject")) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700679 mCallService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800680 }
681 }
682
683 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700684 * Puts the call on hold if it is currently active.
685 */
686 void hold() {
687 Preconditions.checkNotNull(mCallService);
688
689 if (mState == CallState.ACTIVE) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700690 mCallService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700691 }
692 }
693
694 /**
695 * Releases the call from hold if it is currently active.
696 */
697 void unhold() {
698 Preconditions.checkNotNull(mCallService);
699
700 if (mState == CallState.ON_HOLD) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700701 mCallService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700702 }
703 }
704
705 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800706 * @return An object containing read-only information about this call.
707 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700708 CallInfo toCallInfo(String callId) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700709 CallServiceDescriptor descriptor = null;
710 if (mCallService != null) {
711 descriptor = mCallService.getDescriptor();
712 } else if (mOriginalCall != null && mOriginalCall.mCallService != null) {
713 descriptor = mOriginalCall.mCallService.getDescriptor();
714 }
Santos Cordon571f0732014-06-25 18:13:15 -0700715 Bundle extras = mExtras;
716 if (mGatewayInfo != null && mGatewayInfo.getGatewayProviderPackageName() != null &&
717 mGatewayInfo.getOriginalHandle() != null) {
718 extras = (Bundle) mExtras.clone();
719 extras.putString(
720 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
721 mGatewayInfo.getGatewayProviderPackageName());
722 extras.putParcelable(
723 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI,
724 mGatewayInfo.getOriginalHandle());
725
726 }
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700727 return new CallInfo(callId, mState, mHandle, mGatewayInfo, mSubscription,
728 extras, descriptor);
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800729 }
730
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700731 /** Checks if this is a live call or not. */
732 boolean isAlive() {
733 switch (mState) {
734 case NEW:
735 case RINGING:
736 case DISCONNECTED:
737 case ABORTED:
738 return false;
739 default:
740 return true;
741 }
742 }
743
Santos Cordon40f78c22014-04-07 02:11:42 -0700744 boolean isActive() {
745 switch (mState) {
746 case ACTIVE:
747 case POST_DIAL:
748 case POST_DIAL_WAIT:
749 return true;
750 default:
751 return false;
752 }
753 }
754
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700755 Bundle getExtras() {
756 return mExtras;
757 }
758
759 void setExtras(Bundle extras) {
760 mExtras = extras;
761 }
762
763 Uri getHandoffHandle() {
764 return mHandoffHandle;
765 }
766
767 void setHandoffHandle(Uri handoffHandle) {
768 mHandoffHandle = handoffHandle;
769 }
770
771 Call getOriginalCall() {
772 return mOriginalCall;
773 }
774
775 void setOriginalCall(Call originalCall) {
776 mOriginalCall = originalCall;
777 }
778
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700779 CallServiceDescriptor getHandoffCallServiceDescriptor() {
780 return mHandoffCallServiceDescriptor;
781 }
782
783 void setHandoffCallServiceDescriptor(CallServiceDescriptor descriptor) {
784 mHandoffCallServiceDescriptor = descriptor;
785 }
786
Santos Cordon5ba7f272014-05-28 13:59:49 -0700787 Uri getRingtone() {
788 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
789 }
790
Evan Charlton352105c2014-06-03 14:10:54 -0700791 void onPostDialWait(String remaining) {
792 for (Listener l : mListeners) {
793 l.onPostDialWait(this, remaining);
794 }
795 }
796
797 void postDialContinue(boolean proceed) {
798 getCallService().onPostDialContinue(this, proceed);
799 }
800
Santos Cordona1610702014-06-04 20:22:56 -0700801 void conferenceInto(Call conferenceCall) {
802 if (mCallService == null) {
803 Log.w(this, "conference requested on a call without a call service.");
804 } else {
805 mCallService.conference(conferenceCall, this);
806 }
807 }
808
809 void expireConference() {
810 // The conference call expired before we got a confirmation of the conference from the
811 // call service...so start shutting down.
812 clearCallService();
813 for (Listener l : mListeners) {
814 l.onExpiredConferenceCall(this);
815 }
816 }
817
818 void confirmConference() {
819 Log.v(this, "confirming Conf call %s", mListeners);
820 for (Listener l : mListeners) {
821 l.onConfirmedConferenceCall(this);
822 }
823 }
824
825 void splitFromConference() {
826 // TODO(santoscordon): todo
827 }
828
829 void setParentCall(Call parentCall) {
830 if (parentCall == this) {
831 Log.e(this, new Exception(), "setting the parent to self");
832 return;
833 }
834 Preconditions.checkState(parentCall == null || mParentCall == null);
835
836 Call oldParent = mParentCall;
837 if (mParentCall != null) {
838 mParentCall.removeChildCall(this);
839 }
840 mParentCall = parentCall;
841 if (mParentCall != null) {
842 mParentCall.addChildCall(this);
843 }
844
845 for (Listener l : mListeners) {
846 l.onParentChanged(this);
847 }
848 }
849
850 private void addChildCall(Call call) {
851 if (!mChildCalls.contains(call)) {
852 mChildCalls.add(call);
853
854 for (Listener l : mListeners) {
855 l.onChildrenChanged(this);
856 }
857 }
858 }
859
860 private void removeChildCall(Call call) {
861 if (mChildCalls.remove(call)) {
862 for (Listener l : mListeners) {
863 l.onChildrenChanged(this);
864 }
865 }
866 }
867
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800868 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700869 * Return whether the user can respond to this {@code Call} via an SMS message.
870 *
871 * @return true if the "Respond via SMS" feature should be enabled
872 * for this incoming call.
873 *
874 * The general rule is that we *do* allow "Respond via SMS" except for
875 * the few (relatively rare) cases where we know for sure it won't
876 * work, namely:
877 * - a bogus or blank incoming number
878 * - a call from a SIP address
879 * - a "call presentation" that doesn't allow the number to be revealed
880 *
881 * In all other cases, we allow the user to respond via SMS.
882 *
883 * Note that this behavior isn't perfect; for example we have no way
884 * to detect whether the incoming call is from a landline (with most
885 * networks at least), so we still enable this feature even though
886 * SMSes to that number will silently fail.
887 */
888 boolean isRespondViaSmsCapable() {
889 if (mState != CallState.RINGING) {
890 return false;
891 }
892
893 if (getHandle() == null) {
894 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
895 // other words, the user should not be able to see the incoming phone number.
896 return false;
897 }
898
899 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
900 // The incoming number is actually a URI (i.e. a SIP address),
901 // not a regular PSTN phone number, and we can't send SMSes to
902 // SIP addresses.
903 // (TODO: That might still be possible eventually, though. Is
904 // there some SIP-specific equivalent to sending a text message?)
905 return false;
906 }
907
908 // Is there a valid SMS application on the phone?
909 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
910 true /*updateIfNeeded*/) == null) {
911 return false;
912 }
913
914 // TODO: with some carriers (in certain countries) you *can* actually
915 // tell whether a given number is a mobile phone or not. So in that
916 // case we could potentially return false here if the incoming call is
917 // from a land line.
918
919 // If none of the above special cases apply, it's OK to enable the
920 // "Respond via SMS" feature.
921 return true;
922 }
923
924 List<String> getCannedSmsResponses() {
925 return mCannedSmsResponses;
926 }
927
928 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800929 * @return True if the call is ringing, else logs the action name.
930 */
931 private boolean isRinging(String actionName) {
932 if (mState == CallState.RINGING) {
933 return true;
934 }
935
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800936 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800937 return false;
938 }
939
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800940 @SuppressWarnings("rawtypes")
941 private void decrementAssociatedCallCount(ServiceBinder binder) {
942 if (binder != null) {
943 binder.decrementAssociatedCallCount();
944 }
945 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700946
947 /**
948 * Looks up contact information based on the current handle.
949 */
950 private void startCallerInfoLookup() {
951 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
952
953 mQueryToken++; // Updated so that previous queries can no longer set the information.
954 mCallerInfo = null;
955 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700956 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700957 CallerInfoAsyncQuery.startQuery(
958 mQueryToken,
959 TelecommApp.getInstance(),
960 number,
961 sCallerInfoQueryListener,
962 this);
963 }
964 }
965
966 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700967 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700968 * that was made.
969 *
970 * @param callerInfo The new caller information to set.
971 * @param token The token used with this query.
972 */
973 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700974 Preconditions.checkNotNull(callerInfo);
975
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700976 if (mQueryToken == token) {
977 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700978 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700979
980 if (mCallerInfo.person_id != 0) {
981 Uri personUri =
982 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
983 Log.d(this, "Searching person uri %s for call %s", personUri, this);
984 ContactsAsyncHelper.startObtainPhotoAsync(
985 token,
986 TelecommApp.getInstance(),
987 personUri,
988 sPhotoLoadListener,
989 this);
990 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700991
992 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700993 }
994 }
995
996 /**
997 * Saves the specified photo information if the specified token matches that of the last query.
998 *
999 * @param photo The photo as a drawable.
1000 * @param photoIcon The photo as a small icon.
1001 * @param token The token used with this query.
1002 */
1003 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1004 if (mQueryToken == token) {
1005 mCallerInfo.cachedPhoto = photo;
1006 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001007 }
1008 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001009
1010 private void maybeLoadCannedSmsResponses() {
1011 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1012 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1013 mCannedSmsResponsesLoadingStarted = true;
1014 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1015 new Response<Void, List<String>>() {
1016 @Override
1017 public void onResult(Void request, List<String>... result) {
1018 if (result.length > 0) {
1019 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1020 mCannedSmsResponses = result[0];
1021 for (Listener l : mListeners) {
1022 l.onCannedSmsResponsesLoaded(Call.this);
1023 }
1024 }
1025 }
1026
1027 @Override
1028 public void onError(Void request, int code, String msg) {
1029 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1030 msg);
1031 }
1032 }
1033 );
1034 } else {
1035 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1036 }
1037 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001038
1039 /**
1040 * Sets speakerphone option on when call begins.
1041 */
1042 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1043 mSpeakerphoneOn = startWithSpeakerphone;
1044 }
1045
1046 /**
1047 * Returns speakerphone option.
1048 *
1049 * @return Whether or not speakerphone should be set automatically when call begins.
1050 */
1051 public boolean getStartWithSpeakerphoneOn() {
1052 return mSpeakerphoneOn;
1053 }
Andrew Leee9a77652014-06-26 13:07:57 -07001054
1055 /**
1056 * Sets a call video provider for the call.
1057 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001058 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1059 mCallVideoProvider = callVideoProvider;
1060 for (Listener l : mListeners) {
1061 l.onCallVideoProviderChanged(Call.this);
1062 }
1063 }
1064
1065 /**
1066 * @return Return the call video Provider binder.
1067 */
1068 public ICallVideoProvider getCallVideoProvider() {
1069 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001070 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001071}