blob: cb9353fdb0f11130a7f479aae1aef3406817e4ae [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
197 /** The Uri to dial to perform the handoff. If this is null then handoff is not supported. */
198 private Uri mHandoffHandle;
199
200 /**
201 * References the call that is being handed off. This value is non-null for untracked calls
202 * that are being used to perform a handoff.
203 */
204 private Call mOriginalCall;
205
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700206 /**
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700207 * The descriptor for the call service that this call is being switched to, null if handoff is
208 * not in progress.
209 */
210 private CallServiceDescriptor mHandoffCallServiceDescriptor;
211
Santos Cordon766d04f2014-05-06 10:28:25 -0700212 /** Set of listeners on this call. */
213 private Set<Listener> mListeners = Sets.newHashSet();
214
Santos Cordon682fe6b2014-05-20 08:56:39 -0700215 private OutgoingCallProcessor mOutgoingCallProcessor;
216
217 // TODO(santoscordon): The repositories should be changed into singleton types.
218 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700219
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700220 /** Caller information retrieved from the latest contact query. */
221 private CallerInfo mCallerInfo;
222
223 /** The latest token used with a contact info query. */
224 private int mQueryToken = 0;
225
Ihab Awadcb387ac2014-05-28 16:49:38 -0700226 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
227 private boolean mRequestingRingback = false;
228
Santos Cordon2174fb52014-05-29 08:22:56 -0700229 /** Incoming call-info to use when direct-to-voicemail query finishes. */
230 private CallInfo mPendingDirectToVoicemailCallInfo;
231
Santos Cordona1610702014-06-04 20:22:56 -0700232 private boolean mIsConferenceCapable = false;
233
234 private boolean mIsConference = false;
235
236 private Call mParentCall = null;
237
238 private List<Call> mChildCalls = new LinkedList<>();
239
Ihab Awadff7493a2014-06-10 13:47:44 -0700240 /** Set of text message responses allowed for this call, if applicable. */
241 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
242
243 /** Whether an attempt has been made to load the text message responses. */
244 private boolean mCannedSmsResponsesLoadingStarted = false;
245
Nancy Chena65d41f2014-06-24 12:06:03 -0700246 private ICallVideoProvider mCallVideoProvider;
247
Tyler Gunne19cc002014-07-01 11:32:53 -0700248 /** Features associated with the call which the InCall UI may wish to show icons for. */
249 private int mFeatures;
250
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700251 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700252 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700253 *
254 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800255 */
Santos Cordona1610702014-06-04 20:22:56 -0700256 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700257 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800258 }
259
260 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800261 * Persists the specified parameters and initializes the new instance.
262 *
263 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700264 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700265 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700266 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800267 */
Ihab Awad98a55602014-06-30 21:27:28 -0700268 Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700269 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700270 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700271 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700272 mGatewayInfo = gatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -0700273 mAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700274 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700275 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700276 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800277 }
278
Santos Cordon766d04f2014-05-06 10:28:25 -0700279 void addListener(Listener listener) {
280 mListeners.add(listener);
281 }
282
283 void removeListener(Listener listener) {
284 mListeners.remove(listener);
285 }
286
Santos Cordon61d0f702014-02-19 02:52:23 -0800287 /** {@inheritDoc} */
288 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700289 String component = null;
290 if (mCallService != null && mCallService.getComponentName() != null) {
291 component = mCallService.getComponentName().flattenToShortString();
292 }
293 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800294 }
295
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800296 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700297 if (mIsConference) {
298 if (!mChildCalls.isEmpty()) {
299 // If we have child calls, just return the child call.
300 return mChildCalls.get(0).getState();
301 }
302 return CallState.ACTIVE;
303 } else {
304 return mState;
305 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800306 }
307
308 /**
309 * Sets the call state. Although there exists the notion of appropriate state transitions
310 * (see {@link CallState}), in practice those expectations break down when cellular systems
311 * misbehave and they do this very often. The result is that we do not enforce state transitions
312 * and instead keep the code resilient to unexpected state changes.
313 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700314 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700315 Preconditions.checkState(newState != CallState.DISCONNECTED ||
316 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700317 if (mState != newState) {
318 Log.v(this, "setState %s -> %s", mState, newState);
319 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700320 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700321 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800322 }
323
Ihab Awadcb387ac2014-05-28 16:49:38 -0700324 void setRequestingRingback(boolean requestingRingback) {
325 mRequestingRingback = requestingRingback;
326 for (Listener l : mListeners) {
327 l.onRequestingRingback(this, mRequestingRingback);
328 }
329 }
330
331 boolean isRequestingRingback() {
332 return mRequestingRingback;
333 }
334
Sailesh Nepalce704b92014-03-17 18:31:43 -0700335 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800336 return mHandle;
337 }
338
Sailesh Nepalce704b92014-03-17 18:31:43 -0700339 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700340 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
341 mHandle = handle;
342 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700343 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700344 startCallerInfoLookup();
345 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700346 }
347
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700348 String getName() {
349 return mCallerInfo == null ? null : mCallerInfo.name;
350 }
351
352 Bitmap getPhotoIcon() {
353 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
354 }
355
356 Drawable getPhoto() {
357 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
358 }
359
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700360 /**
361 * @param disconnectCause The reason for the disconnection, any of
362 * {@link android.telephony.DisconnectCause}.
363 * @param disconnectMessage Optional call-service-provided message about the disconnect.
364 */
365 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
366 // TODO: Consider combining this method with a setDisconnected() method that is totally
367 // separate from setState.
368 mDisconnectCause = disconnectCause;
369 mDisconnectMessage = disconnectMessage;
370 }
371
372 int getDisconnectCause() {
373 return mDisconnectCause;
374 }
375
376 String getDisconnectMessage() {
377 return mDisconnectMessage;
378 }
379
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700380 boolean isEmergencyCall() {
381 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800382 }
383
Yorke Lee33501632014-03-17 19:24:12 -0700384 /**
385 * @return The original handle this call is associated with. In-call services should use this
386 * handle when indicating in their UI the handle that is being called.
387 */
388 public Uri getOriginalHandle() {
389 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
390 return mGatewayInfo.getOriginalHandle();
391 }
392 return getHandle();
393 }
394
395 GatewayInfo getGatewayInfo() {
396 return mGatewayInfo;
397 }
398
Ihab Awad98a55602014-06-30 21:27:28 -0700399 PhoneAccount getAccount() {
400 return mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700401 }
402
Sailesh Nepal810735e2014-03-18 18:15:46 -0700403 boolean isIncoming() {
404 return mIsIncoming;
405 }
406
Ben Gilad0407fb22014-01-09 16:18:41 -0800407 /**
408 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700409 * period since this call was added to the set pending outgoing calls, see
410 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800411 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700412 long getAgeMillis() {
413 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800414 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800415
Yorke Leef98fb572014-03-05 10:56:55 -0800416 /**
417 * @return The time when this call object was created and added to the set of pending outgoing
418 * calls.
419 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700420 long getCreationTimeMillis() {
421 return mCreationTimeMillis;
422 }
423
424 long getConnectTimeMillis() {
425 return mConnectTimeMillis;
426 }
427
428 void setConnectTimeMillis(long connectTimeMillis) {
429 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800430 }
431
Santos Cordona1610702014-06-04 20:22:56 -0700432 boolean isConferenceCapable() {
433 return mIsConferenceCapable;
434 }
435
436 void setIsConferenceCapable(boolean isConferenceCapable) {
437 if (mIsConferenceCapable != isConferenceCapable) {
438 mIsConferenceCapable = isConferenceCapable;
439 for (Listener l : mListeners) {
440 l.onIsConferenceCapableChanged(this, mIsConferenceCapable);
441 }
442 }
443 }
444
445 Call getParentCall() {
446 return mParentCall;
447 }
448
449 List<Call> getChildCalls() {
450 return mChildCalls;
451 }
452
Santos Cordonc195e362014-02-11 17:05:31 -0800453 CallServiceWrapper getCallService() {
Santos Cordon681663d2014-01-30 04:32:15 -0800454 return mCallService;
455 }
456
Santos Cordonc195e362014-02-11 17:05:31 -0800457 void setCallService(CallServiceWrapper callService) {
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700458 setCallService(callService, null);
459 }
460
461 /**
462 * Changes the call service this call is associated with. If callToReplace is non-null then this
463 * call takes its place within the call service.
464 */
465 void setCallService(CallServiceWrapper callService, Call callToReplace) {
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800466 Preconditions.checkNotNull(callService);
467
Yorke Leeadee12d2014-03-13 12:08:30 -0700468 clearCallService();
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800469
470 callService.incrementAssociatedCallCount();
Santos Cordon681663d2014-01-30 04:32:15 -0800471 mCallService = callService;
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700472 if (callToReplace == null) {
473 mCallService.addCall(this);
474 } else {
475 mCallService.replaceCall(this, callToReplace);
476 }
Santos Cordon681663d2014-01-30 04:32:15 -0800477 }
478
479 /**
480 * Clears the associated call service.
481 */
482 void clearCallService() {
Yorke Leeadee12d2014-03-13 12:08:30 -0700483 if (mCallService != null) {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700484 CallServiceWrapper callServiceTemp = mCallService;
Yorke Leeadee12d2014-03-13 12:08:30 -0700485 mCallService = null;
Santos Cordonc499c1c2014-04-14 17:13:14 -0700486 callServiceTemp.removeCall(this);
487
488 // Decrementing the count can cause the service to unbind, which itself can trigger the
489 // service-death code. Since the service death code tries to clean up any associated
490 // calls, we need to make sure to remove that information (e.g., removeCall()) before
491 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
492 // necessary, but cleaning up mCallService prior to triggering an unbind is good to do.
493 // If you change this, make sure to update {@link clearCallServiceSelector} as well.
494 decrementAssociatedCallCount(callServiceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700495 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800496 }
497
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800498 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700499 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
500 * invoke handle[Un]SuccessfulIncomingCall.
501 *
502 * @param descriptor The relevant call-service descriptor.
503 * @param extras The optional extras passed via
504 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
505 */
506 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
507 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
508 }
509
Santos Cordon2174fb52014-05-29 08:22:56 -0700510 /**
511 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
512 * from the contacts provider. The call is not yet exposed to the user at this point and
513 * the result of the query will determine if the call is rejected or passed through to the
514 * in-call UI.
515 */
516 void handleVerifiedIncoming(CallInfo callInfo) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700517 Preconditions.checkState(callInfo.getState() == CallState.RINGING);
Santos Cordon2174fb52014-05-29 08:22:56 -0700518
519 // We do not handle incoming calls immediately when they are verified by the call service.
520 // We allow the caller-info-query code to execute first so that we can read the
521 // direct-to-voicemail property before deciding if we want to show the incoming call to the
522 // user or if we want to reject the call.
523 mPendingDirectToVoicemailCallInfo = callInfo;
524
525 // Setting the handle triggers the caller info lookup code.
Santos Cordon766d04f2014-05-06 10:28:25 -0700526 setHandle(callInfo.getHandle());
527
Santos Cordon2174fb52014-05-29 08:22:56 -0700528 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
529 // showing the user the incoming call screen.
530 mHandler.postDelayed(new Runnable() {
531 @Override
532 public void run() {
533 processDirectToVoicemail();
534 }
Santos Cordona1610702014-06-04 20:22:56 -0700535 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700536 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700537
Santos Cordon2174fb52014-05-29 08:22:56 -0700538 void processDirectToVoicemail() {
539 if (mPendingDirectToVoicemailCallInfo != null) {
540 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
541 Log.i(this, "Directing call to voicemail: %s.", this);
542 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
543 // will not need to set RINGING state prior to calling reject.
544 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700545 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700546 } else {
547 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
548 // the call state to RINGING.
549
550 // TODO(santoscordon): Replace this with state transition to RINGING.
551 for (Listener l : mListeners) {
552 l.onSuccessfulIncomingCall(this, mPendingDirectToVoicemailCallInfo);
553 }
554 }
555
556 mPendingDirectToVoicemailCallInfo = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700557 }
558 }
559
560 void handleFailedIncoming() {
561 clearCallService();
562
563 // TODO: Needs more specific disconnect error for this case.
564 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
565 setState(CallState.DISCONNECTED);
566
567 // TODO(santoscordon): Replace this with state transitions related to "connecting".
568 for (Listener l : mListeners) {
569 l.onFailedIncomingCall(this);
570 }
571 }
572
573 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700574 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
575 * through a call service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700576 */
577 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700578 Preconditions.checkState(mOutgoingCallProcessor == null);
579
580 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700581 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700582 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700583 }
584
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700585 @Override
586 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700587 // TODO(santoscordon): Replace this with state transitions related to "connecting".
588 for (Listener l : mListeners) {
589 l.onSuccessfulOutgoingCall(this);
590 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700591 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700592 }
593
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700594 @Override
595 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700596 // TODO(santoscordon): Replace this with state transitions related to "connecting".
597 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700598 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700599 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700600
601 clearCallService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700602 mOutgoingCallProcessor = null;
603 }
604
605 @Override
606 public void onOutgoingCallCancel() {
607 // TODO(santoscordon): Replace this with state transitions related to "connecting".
608 for (Listener l : mListeners) {
609 l.onCancelledOutgoingCall(this);
610 }
611
612 clearCallService();
613 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700614 }
615
616 /**
Ben Gilad61925612014-03-11 19:06:36 -0700617 * Adds the specified call service to the list of incompatible services. The set is used when
618 * attempting to switch a phone call between call services such that incompatible services can
619 * be avoided.
620 *
621 * @param callService The incompatible call service.
622 */
623 void addIncompatibleCallService(CallServiceWrapper callService) {
624 if (mIncompatibleCallServices == null) {
625 mIncompatibleCallServices = Sets.newHashSet();
626 }
627 mIncompatibleCallServices.add(callService);
628 }
629
630 /**
631 * Checks whether or not the specified callService was identified as incompatible in the
632 * context of this call.
633 *
634 * @param callService The call service to evaluate.
635 * @return True upon incompatible call services and false otherwise.
636 */
637 boolean isIncompatibleCallService(CallServiceWrapper callService) {
638 return mIncompatibleCallServices != null &&
639 mIncompatibleCallServices.contains(callService);
640 }
641
642 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700643 * Plays the specified DTMF tone.
644 */
645 void playDtmfTone(char digit) {
646 if (mCallService == null) {
647 Log.w(this, "playDtmfTone() request on a call without a call service.");
648 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700649 Log.i(this, "Send playDtmfTone to call service for call %s", this);
650 mCallService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700651 }
652 }
653
654 /**
655 * Stops playing any currently playing DTMF tone.
656 */
657 void stopDtmfTone() {
658 if (mCallService == null) {
659 Log.w(this, "stopDtmfTone() request on a call without a call service.");
660 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700661 Log.i(this, "Send stopDtmfTone to call service for call %s", this);
662 mCallService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700663 }
664 }
665
666 /**
Santos Cordon049b7b62014-01-30 05:34:26 -0800667 * Attempts to disconnect the call through the call service.
668 */
669 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700670 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700671 Log.v(this, "Aborting call %s", this);
672 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700673 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700674 Preconditions.checkNotNull(mCallService);
675
Sailesh Nepale59bb192014-04-01 18:33:59 -0700676 Log.i(this, "Send disconnect to call service for call: %s", this);
Santos Cordonc195e362014-02-11 17:05:31 -0800677 // The call isn't officially disconnected until the call service confirms that the call
678 // was actually disconnected. Only then is the association between call and call service
679 // severed, see {@link CallsManager#markCallAsDisconnected}.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700680 mCallService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800681 }
682 }
683
Santos Cordon682fe6b2014-05-20 08:56:39 -0700684 void abort() {
685 if (mOutgoingCallProcessor != null) {
686 mOutgoingCallProcessor.abort();
687 }
688 }
689
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800690 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800691 * Answers the call if it is ringing.
692 */
693 void answer() {
694 Preconditions.checkNotNull(mCallService);
695
696 // Check to verify that the call is still in the ringing state. A call can change states
697 // between the time the user hits 'answer' and Telecomm receives the command.
698 if (isRinging("answer")) {
699 // At this point, we are asking the call service to answer but we don't assume that
700 // it will work. Instead, we wait until confirmation from the call service that the
701 // call is in a non-RINGING state before changing the UI. See
702 // {@link CallServiceAdapter#setActive} and other set* methods.
Sailesh Nepale59bb192014-04-01 18:33:59 -0700703 mCallService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800704 }
705 }
706
707 /**
708 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700709 *
710 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
711 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800712 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700713 void reject(boolean rejectWithMessage, String textMessage) {
Santos Cordon61d0f702014-02-19 02:52:23 -0800714 Preconditions.checkNotNull(mCallService);
715
716 // Check to verify that the call is still in the ringing state. A call can change states
717 // between the time the user hits 'reject' and Telecomm receives the command.
718 if (isRinging("reject")) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700719 mCallService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800720 }
721 }
722
723 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700724 * Puts the call on hold if it is currently active.
725 */
726 void hold() {
727 Preconditions.checkNotNull(mCallService);
728
729 if (mState == CallState.ACTIVE) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700730 mCallService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700731 }
732 }
733
734 /**
735 * Releases the call from hold if it is currently active.
736 */
737 void unhold() {
738 Preconditions.checkNotNull(mCallService);
739
740 if (mState == CallState.ON_HOLD) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700741 mCallService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700742 }
743 }
744
745 /**
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800746 * @return An object containing read-only information about this call.
747 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700748 CallInfo toCallInfo(String callId) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700749 CallServiceDescriptor descriptor = null;
750 if (mCallService != null) {
751 descriptor = mCallService.getDescriptor();
752 } else if (mOriginalCall != null && mOriginalCall.mCallService != null) {
753 descriptor = mOriginalCall.mCallService.getDescriptor();
754 }
Santos Cordon571f0732014-06-25 18:13:15 -0700755 Bundle extras = mExtras;
756 if (mGatewayInfo != null && mGatewayInfo.getGatewayProviderPackageName() != null &&
757 mGatewayInfo.getOriginalHandle() != null) {
758 extras = (Bundle) mExtras.clone();
759 extras.putString(
760 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_PROVIDER_PACKAGE,
761 mGatewayInfo.getGatewayProviderPackageName());
762 extras.putParcelable(
763 NewOutgoingCallIntentBroadcaster.EXTRA_GATEWAY_ORIGINAL_URI,
764 mGatewayInfo.getOriginalHandle());
765
766 }
Ihab Awad98a55602014-06-30 21:27:28 -0700767 return new CallInfo(callId, mState, mHandle, mGatewayInfo, mAccount,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700768 extras, descriptor);
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800769 }
770
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700771 /** Checks if this is a live call or not. */
772 boolean isAlive() {
773 switch (mState) {
774 case NEW:
775 case RINGING:
776 case DISCONNECTED:
777 case ABORTED:
778 return false;
779 default:
780 return true;
781 }
782 }
783
Santos Cordon40f78c22014-04-07 02:11:42 -0700784 boolean isActive() {
785 switch (mState) {
786 case ACTIVE:
787 case POST_DIAL:
788 case POST_DIAL_WAIT:
789 return true;
790 default:
791 return false;
792 }
793 }
794
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700795 Bundle getExtras() {
796 return mExtras;
797 }
798
799 void setExtras(Bundle extras) {
800 mExtras = extras;
801 }
802
803 Uri getHandoffHandle() {
804 return mHandoffHandle;
805 }
806
807 void setHandoffHandle(Uri handoffHandle) {
808 mHandoffHandle = handoffHandle;
809 }
810
811 Call getOriginalCall() {
812 return mOriginalCall;
813 }
814
815 void setOriginalCall(Call originalCall) {
816 mOriginalCall = originalCall;
817 }
818
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700819 CallServiceDescriptor getHandoffCallServiceDescriptor() {
820 return mHandoffCallServiceDescriptor;
821 }
822
823 void setHandoffCallServiceDescriptor(CallServiceDescriptor descriptor) {
824 mHandoffCallServiceDescriptor = descriptor;
825 }
826
Santos Cordon5ba7f272014-05-28 13:59:49 -0700827 Uri getRingtone() {
828 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
829 }
830
Evan Charlton352105c2014-06-03 14:10:54 -0700831 void onPostDialWait(String remaining) {
832 for (Listener l : mListeners) {
833 l.onPostDialWait(this, remaining);
834 }
835 }
836
837 void postDialContinue(boolean proceed) {
838 getCallService().onPostDialContinue(this, proceed);
839 }
840
Santos Cordona1610702014-06-04 20:22:56 -0700841 void conferenceInto(Call conferenceCall) {
842 if (mCallService == null) {
843 Log.w(this, "conference requested on a call without a call service.");
844 } else {
845 mCallService.conference(conferenceCall, this);
846 }
847 }
848
849 void expireConference() {
850 // The conference call expired before we got a confirmation of the conference from the
851 // call service...so start shutting down.
852 clearCallService();
853 for (Listener l : mListeners) {
854 l.onExpiredConferenceCall(this);
855 }
856 }
857
858 void confirmConference() {
859 Log.v(this, "confirming Conf call %s", mListeners);
860 for (Listener l : mListeners) {
861 l.onConfirmedConferenceCall(this);
862 }
863 }
864
865 void splitFromConference() {
866 // TODO(santoscordon): todo
867 }
868
869 void setParentCall(Call parentCall) {
870 if (parentCall == this) {
871 Log.e(this, new Exception(), "setting the parent to self");
872 return;
873 }
874 Preconditions.checkState(parentCall == null || mParentCall == null);
875
876 Call oldParent = mParentCall;
877 if (mParentCall != null) {
878 mParentCall.removeChildCall(this);
879 }
880 mParentCall = parentCall;
881 if (mParentCall != null) {
882 mParentCall.addChildCall(this);
883 }
884
885 for (Listener l : mListeners) {
886 l.onParentChanged(this);
887 }
888 }
889
890 private void addChildCall(Call call) {
891 if (!mChildCalls.contains(call)) {
892 mChildCalls.add(call);
893
894 for (Listener l : mListeners) {
895 l.onChildrenChanged(this);
896 }
897 }
898 }
899
900 private void removeChildCall(Call call) {
901 if (mChildCalls.remove(call)) {
902 for (Listener l : mListeners) {
903 l.onChildrenChanged(this);
904 }
905 }
906 }
907
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800908 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700909 * Return whether the user can respond to this {@code Call} via an SMS message.
910 *
911 * @return true if the "Respond via SMS" feature should be enabled
912 * for this incoming call.
913 *
914 * The general rule is that we *do* allow "Respond via SMS" except for
915 * the few (relatively rare) cases where we know for sure it won't
916 * work, namely:
917 * - a bogus or blank incoming number
918 * - a call from a SIP address
919 * - a "call presentation" that doesn't allow the number to be revealed
920 *
921 * In all other cases, we allow the user to respond via SMS.
922 *
923 * Note that this behavior isn't perfect; for example we have no way
924 * to detect whether the incoming call is from a landline (with most
925 * networks at least), so we still enable this feature even though
926 * SMSes to that number will silently fail.
927 */
928 boolean isRespondViaSmsCapable() {
929 if (mState != CallState.RINGING) {
930 return false;
931 }
932
933 if (getHandle() == null) {
934 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
935 // other words, the user should not be able to see the incoming phone number.
936 return false;
937 }
938
939 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
940 // The incoming number is actually a URI (i.e. a SIP address),
941 // not a regular PSTN phone number, and we can't send SMSes to
942 // SIP addresses.
943 // (TODO: That might still be possible eventually, though. Is
944 // there some SIP-specific equivalent to sending a text message?)
945 return false;
946 }
947
948 // Is there a valid SMS application on the phone?
949 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
950 true /*updateIfNeeded*/) == null) {
951 return false;
952 }
953
954 // TODO: with some carriers (in certain countries) you *can* actually
955 // tell whether a given number is a mobile phone or not. So in that
956 // case we could potentially return false here if the incoming call is
957 // from a land line.
958
959 // If none of the above special cases apply, it's OK to enable the
960 // "Respond via SMS" feature.
961 return true;
962 }
963
964 List<String> getCannedSmsResponses() {
965 return mCannedSmsResponses;
966 }
967
968 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800969 * @return True if the call is ringing, else logs the action name.
970 */
971 private boolean isRinging(String actionName) {
972 if (mState == CallState.RINGING) {
973 return true;
974 }
975
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800976 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800977 return false;
978 }
979
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800980 @SuppressWarnings("rawtypes")
981 private void decrementAssociatedCallCount(ServiceBinder binder) {
982 if (binder != null) {
983 binder.decrementAssociatedCallCount();
984 }
985 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700986
987 /**
988 * Looks up contact information based on the current handle.
989 */
990 private void startCallerInfoLookup() {
991 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
992
993 mQueryToken++; // Updated so that previous queries can no longer set the information.
994 mCallerInfo = null;
995 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700996 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700997 CallerInfoAsyncQuery.startQuery(
998 mQueryToken,
999 TelecommApp.getInstance(),
1000 number,
1001 sCallerInfoQueryListener,
1002 this);
1003 }
1004 }
1005
1006 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001007 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001008 * that was made.
1009 *
1010 * @param callerInfo The new caller information to set.
1011 * @param token The token used with this query.
1012 */
1013 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001014 Preconditions.checkNotNull(callerInfo);
1015
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001016 if (mQueryToken == token) {
1017 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -07001018 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001019
1020 if (mCallerInfo.person_id != 0) {
1021 Uri personUri =
1022 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
1023 Log.d(this, "Searching person uri %s for call %s", personUri, this);
1024 ContactsAsyncHelper.startObtainPhotoAsync(
1025 token,
1026 TelecommApp.getInstance(),
1027 personUri,
1028 sPhotoLoadListener,
1029 this);
Santos Cordon64c7e962014-07-02 15:15:27 -07001030 } else {
1031 for (Listener l : mListeners) {
1032 l.onCallerInfoChanged(this);
1033 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001034 }
Santos Cordon2174fb52014-05-29 08:22:56 -07001035
1036 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001037 }
1038 }
1039
1040 /**
1041 * Saves the specified photo information if the specified token matches that of the last query.
1042 *
1043 * @param photo The photo as a drawable.
1044 * @param photoIcon The photo as a small icon.
1045 * @param token The token used with this query.
1046 */
1047 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1048 if (mQueryToken == token) {
1049 mCallerInfo.cachedPhoto = photo;
1050 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -07001051
1052 for (Listener l : mListeners) {
1053 l.onCallerInfoChanged(this);
1054 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001055 }
1056 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001057
1058 private void maybeLoadCannedSmsResponses() {
1059 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1060 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1061 mCannedSmsResponsesLoadingStarted = true;
1062 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1063 new Response<Void, List<String>>() {
1064 @Override
1065 public void onResult(Void request, List<String>... result) {
1066 if (result.length > 0) {
1067 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1068 mCannedSmsResponses = result[0];
1069 for (Listener l : mListeners) {
1070 l.onCannedSmsResponsesLoaded(Call.this);
1071 }
1072 }
1073 }
1074
1075 @Override
1076 public void onError(Void request, int code, String msg) {
1077 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1078 msg);
1079 }
1080 }
1081 );
1082 } else {
1083 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1084 }
1085 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001086
1087 /**
1088 * Sets speakerphone option on when call begins.
1089 */
1090 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1091 mSpeakerphoneOn = startWithSpeakerphone;
1092 }
1093
1094 /**
1095 * Returns speakerphone option.
1096 *
1097 * @return Whether or not speakerphone should be set automatically when call begins.
1098 */
1099 public boolean getStartWithSpeakerphoneOn() {
1100 return mSpeakerphoneOn;
1101 }
Andrew Leee9a77652014-06-26 13:07:57 -07001102
1103 /**
1104 * Sets a call video provider for the call.
1105 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001106 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1107 mCallVideoProvider = callVideoProvider;
1108 for (Listener l : mListeners) {
1109 l.onCallVideoProviderChanged(Call.this);
1110 }
1111 }
1112
1113 /**
1114 * @return Return the call video Provider binder.
1115 */
1116 public ICallVideoProvider getCallVideoProvider() {
1117 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001118 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001119
1120 /**
1121 * Returns the features of this call.
1122 *
1123 * @return The features of this call.
1124 */
1125 public int getFeatures() {
1126 return mFeatures;
1127 }
1128
1129 /**
1130 * Set the features associated with the call and notify any listeners of the change.
1131 *
1132 * @param features The features.
1133 */
1134 public void setFeatures(int features) {
1135 Log.d(this, "setFeatures: %d", features);
1136 mFeatures = features;
1137 for (Listener l : mListeners) {
1138 l.onFeaturesChanged(Call.this);
1139 }
1140 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001141}