blob: 2be335554cb014cb964cdaecc4e7d25ed759c954 [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 Awad98a55602014-06-30 21:27:28 -070030import android.telecomm.PhoneAccount;
Ihab Awadff7493a2014-06-10 13:47:44 -070031import android.telecomm.Response;
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 Cordon64c7e962014-07-02 15:15:27 -070078 void onCallerInfoChanged(Call call);
79 }
80
81 abstract static class ListenerBase implements Listener {
82 @Override
83 public void onSuccessfulOutgoingCall(Call call) {}
84 @Override
85 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
86 @Override
87 public void onCancelledOutgoingCall(Call call) {}
88 @Override
89 public void onSuccessfulIncomingCall(Call call, CallInfo callInfo) {}
90 @Override
91 public void onFailedIncomingCall(Call call) {}
92 @Override
93 public void onRequestingRingback(Call call, boolean requestingRingback) {}
94 @Override
95 public void onPostDialWait(Call call, String remaining) {}
96 @Override
97 public void onIsConferenceCapableChanged(Call call, boolean isConferenceCapable) {}
98 @Override
99 public void onExpiredConferenceCall(Call call) {}
100 @Override
101 public void onConfirmedConferenceCall(Call call) {}
102 @Override
103 public void onParentChanged(Call call) {}
104 @Override
105 public void onChildrenChanged(Call call) {}
106 @Override
107 public void onCannedSmsResponsesLoaded(Call call) {}
108 @Override
109 public void onCallVideoProviderChanged(Call call) {}
110 @Override
111 public void onFeaturesChanged(Call call) {}
112 @Override
113 public void onCallerInfoChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700114 }
115
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700116 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700117 new OnQueryCompleteListener() {
118 /** ${inheritDoc} */
119 @Override
120 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
121 if (cookie != null) {
122 ((Call) cookie).setCallerInfo(callerInfo, token);
123 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700124 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700125 };
126
127 private static final OnImageLoadCompleteListener sPhotoLoadListener =
128 new OnImageLoadCompleteListener() {
129 /** ${inheritDoc} */
130 @Override
131 public void onImageLoadComplete(
132 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
133 if (cookie != null) {
134 ((Call) cookie).setPhoto(photo, photoIcon, token);
135 }
136 }
137 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800138
Sailesh Nepal810735e2014-03-18 18:15:46 -0700139 /** True if this is an incoming call. */
140 private final boolean mIsIncoming;
141
Ben Gilad0407fb22014-01-09 16:18:41 -0800142 /**
143 * The time this call was created, typically also the time this call was added to the set
144 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
145 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
146 * certain call attempts as failed attempts.
147 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700148 private final long mCreationTimeMillis = System.currentTimeMillis();
149
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700150 /** The gateway information associated with this call. This stores the original call handle
151 * that the user is attempting to connect to via the gateway, the actual handle to dial in
152 * order to connect the call via the gateway, as well as the package name of the gateway
153 * service. */
154 private final GatewayInfo mGatewayInfo;
155
Ihab Awad98a55602014-06-30 21:27:28 -0700156 private final PhoneAccount mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700157
Santos Cordon2174fb52014-05-29 08:22:56 -0700158 private final Handler mHandler = new Handler();
159
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700160 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800161
Santos Cordon61d0f702014-02-19 02:52:23 -0800162 /** The state of the call. */
163 private CallState mState;
164
165 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700166 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800167
Ben Gilad0407fb22014-01-09 16:18:41 -0800168 /**
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800169 * The call service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800170 */
Santos Cordonc195e362014-02-11 17:05:31 -0800171 private CallServiceWrapper mCallService;
Santos Cordon681663d2014-01-30 04:32:15 -0800172
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800173 /**
Ben Gilad61925612014-03-11 19:06:36 -0700174 * The set of call services that were attempted in the process of placing/switching this call
175 * but turned out unsuitable. Only used in the context of call switching.
176 */
177 private Set<CallServiceWrapper> mIncompatibleCallServices;
178
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700179 private boolean mIsEmergencyCall;
180
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700181 private boolean mSpeakerphoneOn;
182
Ben Gilad61925612014-03-11 19:06:36 -0700183 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700184 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
185 * See {@link android.telephony.DisconnectCause}.
186 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700187 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700188
189 /**
190 * Additional disconnect information provided by the call service.
191 */
192 private String mDisconnectMessage;
193
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700194 /** Info used by the call services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700195 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700196
Santos Cordon766d04f2014-05-06 10:28:25 -0700197 /** Set of listeners on this call. */
198 private Set<Listener> mListeners = Sets.newHashSet();
199
Santos Cordon682fe6b2014-05-20 08:56:39 -0700200 private OutgoingCallProcessor mOutgoingCallProcessor;
201
202 // TODO(santoscordon): The repositories should be changed into singleton types.
203 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700204
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700205 /** Caller information retrieved from the latest contact query. */
206 private CallerInfo mCallerInfo;
207
208 /** The latest token used with a contact info query. */
209 private int mQueryToken = 0;
210
Ihab Awadcb387ac2014-05-28 16:49:38 -0700211 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
212 private boolean mRequestingRingback = false;
213
Santos Cordon2174fb52014-05-29 08:22:56 -0700214 /** Incoming call-info to use when direct-to-voicemail query finishes. */
215 private CallInfo mPendingDirectToVoicemailCallInfo;
216
Santos Cordona1610702014-06-04 20:22:56 -0700217 private boolean mIsConferenceCapable = false;
218
219 private boolean mIsConference = false;
220
221 private Call mParentCall = null;
222
223 private List<Call> mChildCalls = new LinkedList<>();
224
Ihab Awadff7493a2014-06-10 13:47:44 -0700225 /** Set of text message responses allowed for this call, if applicable. */
226 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
227
228 /** Whether an attempt has been made to load the text message responses. */
229 private boolean mCannedSmsResponsesLoadingStarted = false;
230
Nancy Chena65d41f2014-06-24 12:06:03 -0700231 private ICallVideoProvider mCallVideoProvider;
232
Tyler Gunne19cc002014-07-01 11:32:53 -0700233 /** Features associated with the call which the InCall UI may wish to show icons for. */
234 private int mFeatures;
235
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700236 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700237 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700238 *
239 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800240 */
Santos Cordona1610702014-06-04 20:22:56 -0700241 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700242 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800243 }
244
245 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800246 * Persists the specified parameters and initializes the new instance.
247 *
248 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700249 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700250 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700251 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800252 */
Ihab Awad98a55602014-06-30 21:27:28 -0700253 Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700254 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700255 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700256 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700257 mGatewayInfo = gatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -0700258 mAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700259 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700260 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700261 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800262 }
263
Santos Cordon766d04f2014-05-06 10:28:25 -0700264 void addListener(Listener listener) {
265 mListeners.add(listener);
266 }
267
268 void removeListener(Listener listener) {
269 mListeners.remove(listener);
270 }
271
Santos Cordon61d0f702014-02-19 02:52:23 -0800272 /** {@inheritDoc} */
273 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700274 String component = null;
275 if (mCallService != null && mCallService.getComponentName() != null) {
276 component = mCallService.getComponentName().flattenToShortString();
277 }
278 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800279 }
280
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800281 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700282 if (mIsConference) {
283 if (!mChildCalls.isEmpty()) {
284 // If we have child calls, just return the child call.
285 return mChildCalls.get(0).getState();
286 }
287 return CallState.ACTIVE;
288 } else {
289 return mState;
290 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800291 }
292
293 /**
294 * Sets the call state. Although there exists the notion of appropriate state transitions
295 * (see {@link CallState}), in practice those expectations break down when cellular systems
296 * misbehave and they do this very often. The result is that we do not enforce state transitions
297 * and instead keep the code resilient to unexpected state changes.
298 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700299 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700300 Preconditions.checkState(newState != CallState.DISCONNECTED ||
301 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700302 if (mState != newState) {
303 Log.v(this, "setState %s -> %s", mState, newState);
304 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700305 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700306 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800307 }
308
Ihab Awadcb387ac2014-05-28 16:49:38 -0700309 void setRequestingRingback(boolean requestingRingback) {
310 mRequestingRingback = requestingRingback;
311 for (Listener l : mListeners) {
312 l.onRequestingRingback(this, mRequestingRingback);
313 }
314 }
315
316 boolean isRequestingRingback() {
317 return mRequestingRingback;
318 }
319
Sailesh Nepalce704b92014-03-17 18:31:43 -0700320 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800321 return mHandle;
322 }
323
Sailesh Nepalce704b92014-03-17 18:31:43 -0700324 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700325 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
326 mHandle = handle;
327 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700328 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700329 startCallerInfoLookup();
330 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700331 }
332
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700333 String getName() {
334 return mCallerInfo == null ? null : mCallerInfo.name;
335 }
336
337 Bitmap getPhotoIcon() {
338 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
339 }
340
341 Drawable getPhoto() {
342 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
343 }
344
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700345 /**
346 * @param disconnectCause The reason for the disconnection, any of
347 * {@link android.telephony.DisconnectCause}.
348 * @param disconnectMessage Optional call-service-provided message about the disconnect.
349 */
350 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
351 // TODO: Consider combining this method with a setDisconnected() method that is totally
352 // separate from setState.
353 mDisconnectCause = disconnectCause;
354 mDisconnectMessage = disconnectMessage;
355 }
356
357 int getDisconnectCause() {
358 return mDisconnectCause;
359 }
360
361 String getDisconnectMessage() {
362 return mDisconnectMessage;
363 }
364
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700365 boolean isEmergencyCall() {
366 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800367 }
368
Yorke Lee33501632014-03-17 19:24:12 -0700369 /**
370 * @return The original handle this call is associated with. In-call services should use this
371 * handle when indicating in their UI the handle that is being called.
372 */
373 public Uri getOriginalHandle() {
374 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
375 return mGatewayInfo.getOriginalHandle();
376 }
377 return getHandle();
378 }
379
380 GatewayInfo getGatewayInfo() {
381 return mGatewayInfo;
382 }
383
Ihab Awad98a55602014-06-30 21:27:28 -0700384 PhoneAccount getAccount() {
385 return mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700386 }
387
Sailesh Nepal810735e2014-03-18 18:15:46 -0700388 boolean isIncoming() {
389 return mIsIncoming;
390 }
391
Ben Gilad0407fb22014-01-09 16:18:41 -0800392 /**
393 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700394 * period since this call was added to the set pending outgoing calls, see
395 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800396 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700397 long getAgeMillis() {
398 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800399 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800400
Yorke Leef98fb572014-03-05 10:56:55 -0800401 /**
402 * @return The time when this call object was created and added to the set of pending outgoing
403 * calls.
404 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700405 long getCreationTimeMillis() {
406 return mCreationTimeMillis;
407 }
408
409 long getConnectTimeMillis() {
410 return mConnectTimeMillis;
411 }
412
413 void setConnectTimeMillis(long connectTimeMillis) {
414 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800415 }
416
Santos Cordona1610702014-06-04 20:22:56 -0700417 boolean isConferenceCapable() {
418 return mIsConferenceCapable;
419 }
420
421 void setIsConferenceCapable(boolean isConferenceCapable) {
422 if (mIsConferenceCapable != isConferenceCapable) {
423 mIsConferenceCapable = isConferenceCapable;
424 for (Listener l : mListeners) {
425 l.onIsConferenceCapableChanged(this, mIsConferenceCapable);
426 }
427 }
428 }
429
430 Call getParentCall() {
431 return mParentCall;
432 }
433
434 List<Call> getChildCalls() {
435 return mChildCalls;
436 }
437
Santos Cordonc195e362014-02-11 17:05:31 -0800438 CallServiceWrapper getCallService() {
Santos Cordon681663d2014-01-30 04:32:15 -0800439 return mCallService;
440 }
441
Santos Cordonc195e362014-02-11 17:05:31 -0800442 void setCallService(CallServiceWrapper callService) {
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700443 setCallService(callService, null);
444 }
445
446 /**
447 * Changes the call service this call is associated with. If callToReplace is non-null then this
448 * call takes its place within the call service.
449 */
450 void setCallService(CallServiceWrapper callService, Call callToReplace) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800451 Preconditions.checkNotNull(callService);
452
Yorke Leeadee12d2014-03-13 12:08:30 -0700453 clearCallService();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800454
455 callService.incrementAssociatedCallCount();
Santos Cordon681663d2014-01-30 04:32:15 -0800456 mCallService = callService;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700457 if (callToReplace == null) {
458 mCallService.addCall(this);
459 } else {
460 mCallService.replaceCall(this, callToReplace);
461 }
Santos Cordon681663d2014-01-30 04:32:15 -0800462 }
463
464 /**
465 * Clears the associated call service.
466 */
467 void clearCallService() {
Yorke Leeadee12d2014-03-13 12:08:30 -0700468 if (mCallService != null) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700469 CallServiceWrapper callServiceTemp = mCallService;
Yorke Leeadee12d2014-03-13 12:08:30 -0700470 mCallService = null;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700471 callServiceTemp.removeCall(this);
472
473 // Decrementing the count can cause the service to unbind, which itself can trigger the
474 // service-death code. Since the service death code tries to clean up any associated
475 // calls, we need to make sure to remove that information (e.g., removeCall()) before
476 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
477 // necessary, but cleaning up mCallService prior to triggering an unbind is good to do.
478 // If you change this, make sure to update {@link clearCallServiceSelector} as well.
479 decrementAssociatedCallCount(callServiceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700480 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800481 }
482
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800483 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700484 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
485 * invoke handle[Un]SuccessfulIncomingCall.
486 *
487 * @param descriptor The relevant call-service descriptor.
488 * @param extras The optional extras passed via
489 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
490 */
491 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
492 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
493 }
494
Santos Cordon2174fb52014-05-29 08:22:56 -0700495 /**
496 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
497 * from the contacts provider. The call is not yet exposed to the user at this point and
498 * the result of the query will determine if the call is rejected or passed through to the
499 * in-call UI.
500 */
501 void handleVerifiedIncoming(CallInfo callInfo) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700502 Preconditions.checkState(callInfo.getState() == CallState.RINGING);
Santos Cordon2174fb52014-05-29 08:22:56 -0700503
504 // We do not handle incoming calls immediately when they are verified by the call service.
505 // We allow the caller-info-query code to execute first so that we can read the
506 // direct-to-voicemail property before deciding if we want to show the incoming call to the
507 // user or if we want to reject the call.
508 mPendingDirectToVoicemailCallInfo = callInfo;
509
510 // Setting the handle triggers the caller info lookup code.
Santos Cordon766d04f2014-05-06 10:28:25 -0700511 setHandle(callInfo.getHandle());
512
Santos Cordon2174fb52014-05-29 08:22:56 -0700513 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
514 // showing the user the incoming call screen.
515 mHandler.postDelayed(new Runnable() {
516 @Override
517 public void run() {
518 processDirectToVoicemail();
519 }
Santos Cordona1610702014-06-04 20:22:56 -0700520 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700521 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700522
Santos Cordon2174fb52014-05-29 08:22:56 -0700523 void processDirectToVoicemail() {
524 if (mPendingDirectToVoicemailCallInfo != null) {
525 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
526 Log.i(this, "Directing call to voicemail: %s.", this);
527 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
528 // will not need to set RINGING state prior to calling reject.
529 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700530 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700531 } else {
532 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
533 // the call state to RINGING.
534
535 // TODO(santoscordon): Replace this with state transition to RINGING.
536 for (Listener l : mListeners) {
537 l.onSuccessfulIncomingCall(this, mPendingDirectToVoicemailCallInfo);
538 }
539 }
540
541 mPendingDirectToVoicemailCallInfo = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700542 }
543 }
544
545 void handleFailedIncoming() {
546 clearCallService();
547
548 // TODO: Needs more specific disconnect error for this case.
549 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
550 setState(CallState.DISCONNECTED);
551
552 // TODO(santoscordon): Replace this with state transitions related to "connecting".
553 for (Listener l : mListeners) {
554 l.onFailedIncomingCall(this);
555 }
556 }
557
558 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700559 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
560 * through a call service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700561 */
562 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700563 Preconditions.checkState(mOutgoingCallProcessor == null);
564
565 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700566 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700567 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700568 }
569
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700570 @Override
571 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700572 // TODO(santoscordon): Replace this with state transitions related to "connecting".
573 for (Listener l : mListeners) {
574 l.onSuccessfulOutgoingCall(this);
575 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700576 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700577 }
578
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700579 @Override
580 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700581 // TODO(santoscordon): Replace this with state transitions related to "connecting".
582 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700583 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700584 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700585
586 clearCallService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700587 mOutgoingCallProcessor = null;
588 }
589
590 @Override
591 public void onOutgoingCallCancel() {
592 // TODO(santoscordon): Replace this with state transitions related to "connecting".
593 for (Listener l : mListeners) {
594 l.onCancelledOutgoingCall(this);
595 }
596
597 clearCallService();
598 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700599 }
600
601 /**
Ben Gilad61925612014-03-11 19:06:36 -0700602 * Adds the specified call service to the list of incompatible services. The set is used when
603 * attempting to switch a phone call between call services such that incompatible services can
604 * be avoided.
605 *
606 * @param callService The incompatible call service.
607 */
608 void addIncompatibleCallService(CallServiceWrapper callService) {
609 if (mIncompatibleCallServices == null) {
610 mIncompatibleCallServices = Sets.newHashSet();
611 }
612 mIncompatibleCallServices.add(callService);
613 }
614
615 /**
616 * Checks whether or not the specified callService was identified as incompatible in the
617 * context of this call.
618 *
619 * @param callService The call service to evaluate.
620 * @return True upon incompatible call services and false otherwise.
621 */
622 boolean isIncompatibleCallService(CallServiceWrapper callService) {
623 return mIncompatibleCallServices != null &&
624 mIncompatibleCallServices.contains(callService);
625 }
626
627 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700628 * Plays the specified DTMF tone.
629 */
630 void playDtmfTone(char digit) {
631 if (mCallService == null) {
632 Log.w(this, "playDtmfTone() request on a call without a call service.");
633 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700634 Log.i(this, "Send playDtmfTone to call service for call %s", this);
635 mCallService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700636 }
637 }
638
639 /**
640 * Stops playing any currently playing DTMF tone.
641 */
642 void stopDtmfTone() {
643 if (mCallService == null) {
644 Log.w(this, "stopDtmfTone() request on a call without a call service.");
645 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700646 Log.i(this, "Send stopDtmfTone to call service for call %s", this);
647 mCallService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700648 }
649 }
650
651 /**
Santos Cordon049b7b62014-01-30 05:34:26 -0800652 * Attempts to disconnect the call through the call service.
653 */
654 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700655 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700656 Log.v(this, "Aborting call %s", this);
657 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700658 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700659 Preconditions.checkNotNull(mCallService);
660
Sailesh Nepale59bb192014-04-01 18:33:59 -0700661 Log.i(this, "Send disconnect to call service for call: %s", this);
Santos Cordonc195e362014-02-11 17:05:31 -0800662 // The call isn't officially disconnected until the call service confirms that the call
663 // was actually disconnected. Only then is the association between call and call service
664 // severed, see {@link CallsManager#markCallAsDisconnected}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700665 mCallService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800666 }
667 }
668
Santos Cordon682fe6b2014-05-20 08:56:39 -0700669 void abort() {
670 if (mOutgoingCallProcessor != null) {
671 mOutgoingCallProcessor.abort();
672 }
673 }
674
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800675 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800676 * Answers the call if it is ringing.
677 */
678 void answer() {
679 Preconditions.checkNotNull(mCallService);
680
681 // Check to verify that the call is still in the ringing state. A call can change states
682 // between the time the user hits 'answer' and Telecomm receives the command.
683 if (isRinging("answer")) {
684 // At this point, we are asking the call service to answer but we don't assume that
685 // it will work. Instead, we wait until confirmation from the call service that the
686 // call is in a non-RINGING state before changing the UI. See
687 // {@link CallServiceAdapter#setActive} and other set* methods.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700688 mCallService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800689 }
690 }
691
692 /**
693 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700694 *
695 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
696 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800697 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700698 void reject(boolean rejectWithMessage, String textMessage) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800699 Preconditions.checkNotNull(mCallService);
700
701 // Check to verify that the call is still in the ringing state. A call can change states
702 // between the time the user hits 'reject' and Telecomm receives the command.
703 if (isRinging("reject")) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700704 mCallService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800705 }
706 }
707
708 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700709 * Puts the call on hold if it is currently active.
710 */
711 void hold() {
712 Preconditions.checkNotNull(mCallService);
713
714 if (mState == CallState.ACTIVE) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700715 mCallService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700716 }
717 }
718
719 /**
720 * Releases the call from hold if it is currently active.
721 */
722 void unhold() {
723 Preconditions.checkNotNull(mCallService);
724
725 if (mState == CallState.ON_HOLD) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700726 mCallService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700727 }
728 }
729
730 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800731 * @return An object containing read-only information about this call.
732 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700733 CallInfo toCallInfo(String callId) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700734 CallServiceDescriptor descriptor = null;
735 if (mCallService != null) {
736 descriptor = mCallService.getDescriptor();
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700737 }
Santos Cordon571f0732014-06-25 18:13:15 -0700738 Bundle extras = mExtras;
739 if (mGatewayInfo != null && mGatewayInfo.getGatewayProviderPackageName() != null &&
740 mGatewayInfo.getOriginalHandle() != null) {
741 extras = (Bundle) mExtras.clone();
742 extras.putString(
743 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
744 mGatewayInfo.getGatewayProviderPackageName());
745 extras.putParcelable(
746 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI,
747 mGatewayInfo.getOriginalHandle());
748
749 }
Ihab Awad98a55602014-06-30 21:27:28 -0700750 return new CallInfo(callId, mState, mHandle, mGatewayInfo, mAccount,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700751 extras, descriptor);
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800752 }
753
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700754 /** Checks if this is a live call or not. */
755 boolean isAlive() {
756 switch (mState) {
757 case NEW:
758 case RINGING:
759 case DISCONNECTED:
760 case ABORTED:
761 return false;
762 default:
763 return true;
764 }
765 }
766
Santos Cordon40f78c22014-04-07 02:11:42 -0700767 boolean isActive() {
768 switch (mState) {
769 case ACTIVE:
770 case POST_DIAL:
771 case POST_DIAL_WAIT:
772 return true;
773 default:
774 return false;
775 }
776 }
777
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700778 Bundle getExtras() {
779 return mExtras;
780 }
781
782 void setExtras(Bundle extras) {
783 mExtras = extras;
784 }
785
Santos Cordon5ba7f272014-05-28 13:59:49 -0700786 Uri getRingtone() {
787 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
788 }
789
Evan Charlton352105c2014-06-03 14:10:54 -0700790 void onPostDialWait(String remaining) {
791 for (Listener l : mListeners) {
792 l.onPostDialWait(this, remaining);
793 }
794 }
795
796 void postDialContinue(boolean proceed) {
797 getCallService().onPostDialContinue(this, proceed);
798 }
799
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700800 void phoneAccountClicked() {
801 getCallService().onPhoneAccountClicked(this);
802 }
803
Santos Cordona1610702014-06-04 20:22:56 -0700804 void conferenceInto(Call conferenceCall) {
805 if (mCallService == null) {
806 Log.w(this, "conference requested on a call without a call service.");
807 } else {
808 mCallService.conference(conferenceCall, this);
809 }
810 }
811
812 void expireConference() {
813 // The conference call expired before we got a confirmation of the conference from the
814 // call service...so start shutting down.
815 clearCallService();
816 for (Listener l : mListeners) {
817 l.onExpiredConferenceCall(this);
818 }
819 }
820
821 void confirmConference() {
822 Log.v(this, "confirming Conf call %s", mListeners);
823 for (Listener l : mListeners) {
824 l.onConfirmedConferenceCall(this);
825 }
826 }
827
828 void splitFromConference() {
829 // TODO(santoscordon): todo
830 }
831
832 void setParentCall(Call parentCall) {
833 if (parentCall == this) {
834 Log.e(this, new Exception(), "setting the parent to self");
835 return;
836 }
837 Preconditions.checkState(parentCall == null || mParentCall == null);
838
839 Call oldParent = mParentCall;
840 if (mParentCall != null) {
841 mParentCall.removeChildCall(this);
842 }
843 mParentCall = parentCall;
844 if (mParentCall != null) {
845 mParentCall.addChildCall(this);
846 }
847
848 for (Listener l : mListeners) {
849 l.onParentChanged(this);
850 }
851 }
852
853 private void addChildCall(Call call) {
854 if (!mChildCalls.contains(call)) {
855 mChildCalls.add(call);
856
857 for (Listener l : mListeners) {
858 l.onChildrenChanged(this);
859 }
860 }
861 }
862
863 private void removeChildCall(Call call) {
864 if (mChildCalls.remove(call)) {
865 for (Listener l : mListeners) {
866 l.onChildrenChanged(this);
867 }
868 }
869 }
870
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800871 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700872 * Return whether the user can respond to this {@code Call} via an SMS message.
873 *
874 * @return true if the "Respond via SMS" feature should be enabled
875 * for this incoming call.
876 *
877 * The general rule is that we *do* allow "Respond via SMS" except for
878 * the few (relatively rare) cases where we know for sure it won't
879 * work, namely:
880 * - a bogus or blank incoming number
881 * - a call from a SIP address
882 * - a "call presentation" that doesn't allow the number to be revealed
883 *
884 * In all other cases, we allow the user to respond via SMS.
885 *
886 * Note that this behavior isn't perfect; for example we have no way
887 * to detect whether the incoming call is from a landline (with most
888 * networks at least), so we still enable this feature even though
889 * SMSes to that number will silently fail.
890 */
891 boolean isRespondViaSmsCapable() {
892 if (mState != CallState.RINGING) {
893 return false;
894 }
895
896 if (getHandle() == null) {
897 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
898 // other words, the user should not be able to see the incoming phone number.
899 return false;
900 }
901
902 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
903 // The incoming number is actually a URI (i.e. a SIP address),
904 // not a regular PSTN phone number, and we can't send SMSes to
905 // SIP addresses.
906 // (TODO: That might still be possible eventually, though. Is
907 // there some SIP-specific equivalent to sending a text message?)
908 return false;
909 }
910
911 // Is there a valid SMS application on the phone?
912 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
913 true /*updateIfNeeded*/) == null) {
914 return false;
915 }
916
917 // TODO: with some carriers (in certain countries) you *can* actually
918 // tell whether a given number is a mobile phone or not. So in that
919 // case we could potentially return false here if the incoming call is
920 // from a land line.
921
922 // If none of the above special cases apply, it's OK to enable the
923 // "Respond via SMS" feature.
924 return true;
925 }
926
927 List<String> getCannedSmsResponses() {
928 return mCannedSmsResponses;
929 }
930
931 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800932 * @return True if the call is ringing, else logs the action name.
933 */
934 private boolean isRinging(String actionName) {
935 if (mState == CallState.RINGING) {
936 return true;
937 }
938
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800939 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800940 return false;
941 }
942
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800943 @SuppressWarnings("rawtypes")
944 private void decrementAssociatedCallCount(ServiceBinder binder) {
945 if (binder != null) {
946 binder.decrementAssociatedCallCount();
947 }
948 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700949
950 /**
951 * Looks up contact information based on the current handle.
952 */
953 private void startCallerInfoLookup() {
954 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
955
956 mQueryToken++; // Updated so that previous queries can no longer set the information.
957 mCallerInfo = null;
958 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700959 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700960 CallerInfoAsyncQuery.startQuery(
961 mQueryToken,
962 TelecommApp.getInstance(),
963 number,
964 sCallerInfoQueryListener,
965 this);
966 }
967 }
968
969 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700970 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700971 * that was made.
972 *
973 * @param callerInfo The new caller information to set.
974 * @param token The token used with this query.
975 */
976 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700977 Preconditions.checkNotNull(callerInfo);
978
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700979 if (mQueryToken == token) {
980 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700981 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700982
983 if (mCallerInfo.person_id != 0) {
984 Uri personUri =
985 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
986 Log.d(this, "Searching person uri %s for call %s", personUri, this);
987 ContactsAsyncHelper.startObtainPhotoAsync(
988 token,
989 TelecommApp.getInstance(),
990 personUri,
991 sPhotoLoadListener,
992 this);
Santos Cordon64c7e962014-07-02 15:15:27 -0700993 } else {
994 for (Listener l : mListeners) {
995 l.onCallerInfoChanged(this);
996 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700997 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700998
999 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001000 }
1001 }
1002
1003 /**
1004 * Saves the specified photo information if the specified token matches that of the last query.
1005 *
1006 * @param photo The photo as a drawable.
1007 * @param photoIcon The photo as a small icon.
1008 * @param token The token used with this query.
1009 */
1010 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1011 if (mQueryToken == token) {
1012 mCallerInfo.cachedPhoto = photo;
1013 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -07001014
1015 for (Listener l : mListeners) {
1016 l.onCallerInfoChanged(this);
1017 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001018 }
1019 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001020
1021 private void maybeLoadCannedSmsResponses() {
1022 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1023 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1024 mCannedSmsResponsesLoadingStarted = true;
1025 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1026 new Response<Void, List<String>>() {
1027 @Override
1028 public void onResult(Void request, List<String>... result) {
1029 if (result.length > 0) {
1030 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1031 mCannedSmsResponses = result[0];
1032 for (Listener l : mListeners) {
1033 l.onCannedSmsResponsesLoaded(Call.this);
1034 }
1035 }
1036 }
1037
1038 @Override
1039 public void onError(Void request, int code, String msg) {
1040 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1041 msg);
1042 }
1043 }
1044 );
1045 } else {
1046 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1047 }
1048 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001049
1050 /**
1051 * Sets speakerphone option on when call begins.
1052 */
1053 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1054 mSpeakerphoneOn = startWithSpeakerphone;
1055 }
1056
1057 /**
1058 * Returns speakerphone option.
1059 *
1060 * @return Whether or not speakerphone should be set automatically when call begins.
1061 */
1062 public boolean getStartWithSpeakerphoneOn() {
1063 return mSpeakerphoneOn;
1064 }
Andrew Leee9a77652014-06-26 13:07:57 -07001065
1066 /**
1067 * Sets a call video provider for the call.
1068 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001069 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1070 mCallVideoProvider = callVideoProvider;
1071 for (Listener l : mListeners) {
1072 l.onCallVideoProviderChanged(Call.this);
1073 }
1074 }
1075
1076 /**
1077 * @return Return the call video Provider binder.
1078 */
1079 public ICallVideoProvider getCallVideoProvider() {
1080 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001081 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001082
1083 /**
1084 * Returns the features of this call.
1085 *
1086 * @return The features of this call.
1087 */
1088 public int getFeatures() {
1089 return mFeatures;
1090 }
1091
1092 /**
1093 * Set the features associated with the call and notify any listeners of the change.
1094 *
1095 * @param features The features.
1096 */
1097 public void setFeatures(int features) {
1098 Log.d(this, "setFeatures: %d", features);
1099 mFeatures = features;
1100 for (Listener l : mListeners) {
1101 l.onFeaturesChanged(Call.this);
1102 }
1103 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001104}