blob: 5f28df84c2da6a563d63e48b549a59ef8728f271 [file] [log] [blame]
Ben Gilad0407fb22014-01-09 16:18:41 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Sailesh Nepal905dfba2014-07-14 08:20:41 -070019import android.content.ComponentName;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070020import android.content.ContentUris;
21import android.graphics.Bitmap;
22import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070023import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070024import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070025import android.os.Handler;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070026import android.provider.ContactsContract.Contacts;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070027import android.telecomm.CallPropertyPresentation;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080028import android.telecomm.CallState;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070029import android.telecomm.ConnectionRequest;
Yorke Lee33501632014-03-17 19:24:12 -070030import android.telecomm.GatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -070031import android.telecomm.PhoneAccount;
Ihab Awadff7493a2014-06-10 13:47:44 -070032import android.telecomm.Response;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070033import android.telecomm.StatusHints;
Santos Cordon766d04f2014-05-06 10:28:25 -070034import android.telecomm.TelecommConstants;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070035import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070036import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070037import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080038
Nancy Chena65d41f2014-06-24 12:06:03 -070039import com.android.internal.telecomm.ICallVideoProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070040import com.android.internal.telephony.CallerInfo;
41import com.android.internal.telephony.CallerInfoAsyncQuery;
42import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070043import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070044import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080045import com.google.common.base.Preconditions;
Ihab Awadff7493a2014-06-10 13:47:44 -070046import com.google.common.collect.Sets;
Santos Cordon61d0f702014-02-19 02:52:23 -080047
Ihab Awadff7493a2014-06-10 13:47:44 -070048import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070049import java.util.LinkedList;
50import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080051import java.util.Locale;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070052import java.util.Objects;
Ben Gilad61925612014-03-11 19:06:36 -070053import java.util.Set;
Ben Gilad0407fb22014-01-09 16:18:41 -080054
Ben Gilad2495d572014-01-09 17:26:19 -080055/**
56 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
57 * from the time the call intent was received by Telecomm (vs. the time the call was
58 * connected etc).
59 */
Sailesh Nepal664837f2014-07-14 16:31:51 -070060final class Call implements CreateConnectionResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070061 /**
62 * Listener for events on the call.
63 */
64 interface Listener {
65 void onSuccessfulOutgoingCall(Call call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -070066 void onFailedOutgoingCall(Call call, int errorCode, String errorMsg);
67 void onCancelledOutgoingCall(Call call);
Sailesh Nepalc92c4362014-07-04 18:33:21 -070068 void onSuccessfulIncomingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070069 void onFailedIncomingCall(Call call);
Ihab Awadcb387ac2014-05-28 16:49:38 -070070 void onRequestingRingback(Call call, boolean requestingRingback);
Evan Charlton352105c2014-06-03 14:10:54 -070071 void onPostDialWait(Call call, String remaining);
Sailesh Nepale20bc972014-07-09 21:22:36 -070072 void onCallCapabilitiesChanged(Call call);
Santos Cordona1610702014-06-04 20:22:56 -070073 void onExpiredConferenceCall(Call call);
74 void onConfirmedConferenceCall(Call call);
75 void onParentChanged(Call call);
76 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -070077 void onCannedSmsResponsesLoaded(Call call);
Nancy Chena65d41f2014-06-24 12:06:03 -070078 void onCallVideoProviderChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070079 void onCallerInfoChanged(Call call);
Sailesh Nepal7e669572014-07-08 21:29:12 -070080 void onAudioModeIsVoipChanged(Call call);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070081 void onStatusHintsChanged(Call call);
Sailesh Nepale8ecb982014-07-11 17:19:42 -070082 void onHandleChanged(Call call);
83 void onCallerDisplayNameChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070084 }
85
86 abstract static class ListenerBase implements Listener {
87 @Override
88 public void onSuccessfulOutgoingCall(Call call) {}
89 @Override
90 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
91 @Override
92 public void onCancelledOutgoingCall(Call call) {}
93 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -070094 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -070095 @Override
96 public void onFailedIncomingCall(Call call) {}
97 @Override
98 public void onRequestingRingback(Call call, boolean requestingRingback) {}
99 @Override
100 public void onPostDialWait(Call call, String remaining) {}
101 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -0700102 public void onCallCapabilitiesChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700103 @Override
104 public void onExpiredConferenceCall(Call call) {}
105 @Override
106 public void onConfirmedConferenceCall(Call call) {}
107 @Override
108 public void onParentChanged(Call call) {}
109 @Override
110 public void onChildrenChanged(Call call) {}
111 @Override
112 public void onCannedSmsResponsesLoaded(Call call) {}
113 @Override
114 public void onCallVideoProviderChanged(Call call) {}
115 @Override
Santos Cordon64c7e962014-07-02 15:15:27 -0700116 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700117 @Override
118 public void onAudioModeIsVoipChanged(Call call) {}
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700119 @Override
120 public void onStatusHintsChanged(Call call) {}
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700121 @Override
122 public void onHandleChanged(Call call) {}
123 @Override
124 public void onCallerDisplayNameChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700125 }
126
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700127 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700128 new OnQueryCompleteListener() {
129 /** ${inheritDoc} */
130 @Override
131 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
132 if (cookie != null) {
133 ((Call) cookie).setCallerInfo(callerInfo, token);
134 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700135 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700136 };
137
138 private static final OnImageLoadCompleteListener sPhotoLoadListener =
139 new OnImageLoadCompleteListener() {
140 /** ${inheritDoc} */
141 @Override
142 public void onImageLoadComplete(
143 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
144 if (cookie != null) {
145 ((Call) cookie).setPhoto(photo, photoIcon, token);
146 }
147 }
148 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800149
Sailesh Nepal664837f2014-07-14 16:31:51 -0700150 private final Runnable mDirectToVoicemailRunnable = new Runnable() {
151 @Override
152 public void run() {
153 processDirectToVoicemail();
154 }
155 };
156
Sailesh Nepal810735e2014-03-18 18:15:46 -0700157 /** True if this is an incoming call. */
158 private final boolean mIsIncoming;
159
Ben Gilad0407fb22014-01-09 16:18:41 -0800160 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700161 * The time this call was created. Beyond logging and such, may also be used for bookkeeping
162 * and specifically for marking certain call attempts as failed attempts.
Ben Gilad0407fb22014-01-09 16:18:41 -0800163 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700164 private final long mCreationTimeMillis = System.currentTimeMillis();
165
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700166 /** The gateway information associated with this call. This stores the original call handle
167 * that the user is attempting to connect to via the gateway, the actual handle to dial in
168 * order to connect the call via the gateway, as well as the package name of the gateway
169 * service. */
170 private final GatewayInfo mGatewayInfo;
171
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700172 private PhoneAccount mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700173
Santos Cordon2174fb52014-05-29 08:22:56 -0700174 private final Handler mHandler = new Handler();
175
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700176 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800177
Santos Cordon61d0f702014-02-19 02:52:23 -0800178 /** The state of the call. */
179 private CallState mState;
180
181 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700182 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800183
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700184 /** The {@link CallPropertyPresentation} that controls how the handle is shown. */
185 private int mHandlePresentation;
186
187 /** The caller display name (CNAP) set by the connection service. */
188 private String mCallerDisplayName;
189
190 /** The {@link CallPropertyPresentation} that controls how the caller display name is shown. */
191 private int mCallerDisplayNamePresentation;
192
Ben Gilad0407fb22014-01-09 16:18:41 -0800193 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700194 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800195 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700196 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700197
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700198 private boolean mIsEmergencyCall;
199
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700200 private boolean mSpeakerphoneOn;
201
Tyler Gunnc4abd912014-07-08 14:22:10 -0700202 private int mVideoState;
203
Ben Gilad61925612014-03-11 19:06:36 -0700204 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700205 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
206 * See {@link android.telephony.DisconnectCause}.
207 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700208 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700209
210 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700211 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700212 */
213 private String mDisconnectMessage;
214
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700215 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700216 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700217
Santos Cordon766d04f2014-05-06 10:28:25 -0700218 /** Set of listeners on this call. */
219 private Set<Listener> mListeners = Sets.newHashSet();
220
Sailesh Nepal664837f2014-07-14 16:31:51 -0700221 private CreateConnectionProcessor mCreateConnectionProcessor;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700222
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700223 /** Caller information retrieved from the latest contact query. */
224 private CallerInfo mCallerInfo;
225
226 /** The latest token used with a contact info query. */
227 private int mQueryToken = 0;
228
Ihab Awadcb387ac2014-05-28 16:49:38 -0700229 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
230 private boolean mRequestingRingback = false;
231
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700232 /** Whether direct-to-voicemail query is pending. */
233 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700234
Sailesh Nepale20bc972014-07-09 21:22:36 -0700235 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700236
237 private boolean mIsConference = false;
238
239 private Call mParentCall = null;
240
241 private List<Call> mChildCalls = new LinkedList<>();
242
Ihab Awadff7493a2014-06-10 13:47:44 -0700243 /** Set of text message responses allowed for this call, if applicable. */
244 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
245
246 /** Whether an attempt has been made to load the text message responses. */
247 private boolean mCannedSmsResponsesLoadingStarted = false;
248
Nancy Chena65d41f2014-06-24 12:06:03 -0700249 private ICallVideoProvider mCallVideoProvider;
250
Sailesh Nepal7e669572014-07-08 21:29:12 -0700251 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700252 private StatusHints mStatusHints;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700253 private final ConnectionServiceRepository mRepository;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700254
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700255 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800256 * Persists the specified parameters and initializes the new instance.
257 *
258 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700259 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700260 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700261 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800262 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700263 Call(
264 ConnectionServiceRepository repository,
265 Uri handle,
266 GatewayInfo gatewayInfo,
267 PhoneAccount account,
268 boolean isIncoming,
269 boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700270 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700271 mRepository = repository;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700272 setHandle(handle, CallPropertyPresentation.ALLOWED);
Yorke Lee33501632014-03-17 19:24:12 -0700273 mGatewayInfo = gatewayInfo;
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700274 mPhoneAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700275 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700276 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700277 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800278 }
279
Santos Cordon766d04f2014-05-06 10:28:25 -0700280 void addListener(Listener listener) {
281 mListeners.add(listener);
282 }
283
284 void removeListener(Listener listener) {
285 mListeners.remove(listener);
286 }
287
Santos Cordon61d0f702014-02-19 02:52:23 -0800288 /** {@inheritDoc} */
289 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700290 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700291 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
292 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700293 }
294 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800295 }
296
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800297 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700298 if (mIsConference) {
299 if (!mChildCalls.isEmpty()) {
300 // If we have child calls, just return the child call.
301 return mChildCalls.get(0).getState();
302 }
303 return CallState.ACTIVE;
304 } else {
305 return mState;
306 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800307 }
308
309 /**
310 * Sets the call state. Although there exists the notion of appropriate state transitions
311 * (see {@link CallState}), in practice those expectations break down when cellular systems
312 * misbehave and they do this very often. The result is that we do not enforce state transitions
313 * and instead keep the code resilient to unexpected state changes.
314 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700315 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700316 Preconditions.checkState(newState != CallState.DISCONNECTED ||
317 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700318 if (mState != newState) {
319 Log.v(this, "setState %s -> %s", mState, newState);
320 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700321 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700322 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800323 }
324
Ihab Awadcb387ac2014-05-28 16:49:38 -0700325 void setRequestingRingback(boolean requestingRingback) {
326 mRequestingRingback = requestingRingback;
327 for (Listener l : mListeners) {
328 l.onRequestingRingback(this, mRequestingRingback);
329 }
330 }
331
332 boolean isRequestingRingback() {
333 return mRequestingRingback;
334 }
335
Sailesh Nepalce704b92014-03-17 18:31:43 -0700336 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800337 return mHandle;
338 }
339
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700340 int getHandlePresentation() {
341 return mHandlePresentation;
342 }
343
344 void setHandle(Uri handle, int presentation) {
345 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700346 mHandle = handle;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700347 mHandlePresentation = presentation;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700348 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700349 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700350 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700351 for (Listener l : mListeners) {
352 l.onHandleChanged(this);
353 }
354 }
355 }
356
357 String getCallerDisplayName() {
358 return mCallerDisplayName;
359 }
360
361 int getCallerDisplayNamePresentation() {
362 return mCallerDisplayNamePresentation;
363 }
364
365 void setCallerDisplayName(String callerDisplayName, int presentation) {
366 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
367 presentation != mCallerDisplayNamePresentation) {
368 mCallerDisplayName = callerDisplayName;
369 mCallerDisplayNamePresentation = presentation;
370 for (Listener l : mListeners) {
371 l.onCallerDisplayNameChanged(this);
372 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700373 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700374 }
375
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700376 String getName() {
377 return mCallerInfo == null ? null : mCallerInfo.name;
378 }
379
380 Bitmap getPhotoIcon() {
381 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
382 }
383
384 Drawable getPhoto() {
385 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
386 }
387
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700388 /**
389 * @param disconnectCause The reason for the disconnection, any of
390 * {@link android.telephony.DisconnectCause}.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700391 * @param disconnectMessage Optional message about the disconnect.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700392 */
393 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
394 // TODO: Consider combining this method with a setDisconnected() method that is totally
395 // separate from setState.
396 mDisconnectCause = disconnectCause;
397 mDisconnectMessage = disconnectMessage;
398 }
399
400 int getDisconnectCause() {
401 return mDisconnectCause;
402 }
403
404 String getDisconnectMessage() {
405 return mDisconnectMessage;
406 }
407
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700408 boolean isEmergencyCall() {
409 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800410 }
411
Yorke Lee33501632014-03-17 19:24:12 -0700412 /**
413 * @return The original handle this call is associated with. In-call services should use this
414 * handle when indicating in their UI the handle that is being called.
415 */
416 public Uri getOriginalHandle() {
417 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
418 return mGatewayInfo.getOriginalHandle();
419 }
420 return getHandle();
421 }
422
423 GatewayInfo getGatewayInfo() {
424 return mGatewayInfo;
425 }
426
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700427 PhoneAccount getPhoneAccount() {
428 return mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700429 }
430
Sailesh Nepal810735e2014-03-18 18:15:46 -0700431 boolean isIncoming() {
432 return mIsIncoming;
433 }
434
Ben Gilad0407fb22014-01-09 16:18:41 -0800435 /**
436 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700437 * period since this call was added to the set pending outgoing calls, see
438 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800439 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700440 long getAgeMillis() {
441 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800442 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800443
Yorke Leef98fb572014-03-05 10:56:55 -0800444 /**
445 * @return The time when this call object was created and added to the set of pending outgoing
446 * calls.
447 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700448 long getCreationTimeMillis() {
449 return mCreationTimeMillis;
450 }
451
452 long getConnectTimeMillis() {
453 return mConnectTimeMillis;
454 }
455
456 void setConnectTimeMillis(long connectTimeMillis) {
457 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800458 }
459
Sailesh Nepale20bc972014-07-09 21:22:36 -0700460 int getCallCapabilities() {
461 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700462 }
463
Sailesh Nepale20bc972014-07-09 21:22:36 -0700464 void setCallCapabilities(int callCapabilities) {
465 if (mCallCapabilities != callCapabilities) {
466 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700467 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700468 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700469 }
470 }
471 }
472
473 Call getParentCall() {
474 return mParentCall;
475 }
476
477 List<Call> getChildCalls() {
478 return mChildCalls;
479 }
480
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700481 ConnectionServiceWrapper getConnectionService() {
482 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800483 }
484
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700485 void setConnectionService(ConnectionServiceWrapper service) {
486 Preconditions.checkNotNull(service);
487
488 clearConnectionService();
489
490 service.incrementAssociatedCallCount();
491 mConnectionService = service;
492 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700493 }
494
495 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700496 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700497 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700498 void clearConnectionService() {
499 if (mConnectionService != null) {
500 ConnectionServiceWrapper serviceTemp = mConnectionService;
501 mConnectionService = null;
502 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700503
504 // Decrementing the count can cause the service to unbind, which itself can trigger the
505 // service-death code. Since the service death code tries to clean up any associated
506 // calls, we need to make sure to remove that information (e.g., removeCall()) before
507 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700508 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
509 // to do.
510 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700511 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800512 }
513
Sailesh Nepal664837f2014-07-14 16:31:51 -0700514 private void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700515 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700516 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
517 Log.i(this, "Directing call to voicemail: %s.", this);
518 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
519 // will not need to set RINGING state prior to calling reject.
520 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700521 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700522 } else {
523 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
524 // the call state to RINGING.
525
526 // TODO(santoscordon): Replace this with state transition to RINGING.
527 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700528 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700529 }
530 }
531
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700532 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700533 }
534 }
535
Santos Cordon766d04f2014-05-06 10:28:25 -0700536 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700537 * Starts the create connection sequence. Upon completion, there should exist an active
538 * connection through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700539 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700540 void startCreateConnection() {
541 Preconditions.checkState(mCreateConnectionProcessor == null);
542 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
543 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700544 }
545
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700546 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700547 public void handleCreateConnectionSuccessful(ConnectionRequest request) {
548 mCreateConnectionProcessor = null;
549 mPhoneAccount = request.getAccount();
550
551 if (mIsIncoming) {
552 // We do not handle incoming calls immediately when they are verified by the connection
553 // service. We allow the caller-info-query code to execute first so that we can read the
554 // direct-to-voicemail property before deciding if we want to show the incoming call to
555 // the user or if we want to reject the call.
556 mDirectToVoicemailQueryPending = true;
557
558 // Setting the handle triggers the caller info lookup code.
559 setHandle(request.getHandle(), request.getHandlePresentation());
560
561 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
562 // showing the user the incoming call screen.
563 mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
564 } else {
565 for (Listener l : mListeners) {
566 l.onSuccessfulOutgoingCall(this);
567 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700568 }
569 }
570
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700571 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700572 public void handleCreateConnectionFailed(int code, String msg) {
573 mCreateConnectionProcessor = null;
574 if (mIsIncoming) {
575 clearConnectionService();
576 setDisconnectCause(code, null);
577 setState(CallState.DISCONNECTED);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700578
Sailesh Nepal664837f2014-07-14 16:31:51 -0700579 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
580 for (int i = 0; i < listeners.length; i++) {
581 listeners[i].onFailedIncomingCall(this);
582 }
583 } else {
584 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
585 for (int i = 0; i < listeners.length; i++) {
586 listeners[i].onFailedOutgoingCall(this, code, msg);
587 }
588 clearConnectionService();
589 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700590 }
591
592 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700593 public void handleCreateConnectionCancelled() {
594 mCreateConnectionProcessor = null;
595 if (mIsIncoming) {
596 clearConnectionService();
597 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
598 setState(CallState.DISCONNECTED);
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700599
Sailesh Nepal664837f2014-07-14 16:31:51 -0700600 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
601 for (int i = 0; i < listeners.length; i++) {
602 listeners[i].onFailedIncomingCall(this);
603 }
604 } else {
605 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
606 for (int i = 0; i < listeners.length; i++) {
607 listeners[i].onCancelledOutgoingCall(this);
608 }
609 clearConnectionService();
610 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700611 }
612
613 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700614 * Plays the specified DTMF tone.
615 */
616 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700617 if (mConnectionService == null) {
618 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700619 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700620 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
621 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700622 }
623 }
624
625 /**
626 * Stops playing any currently playing DTMF tone.
627 */
628 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700629 if (mConnectionService == null) {
630 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700631 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700632 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
633 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700634 }
635 }
636
637 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700638 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800639 */
640 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700641 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700642 Log.v(this, "Aborting call %s", this);
643 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700644 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700645 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700646
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700647 Log.i(this, "Send disconnect to connection service for call: %s", this);
648 // The call isn't officially disconnected until the connection service confirms that the
649 // call was actually disconnected. Only then is the association between call and
650 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
651 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800652 }
653 }
654
Santos Cordon682fe6b2014-05-20 08:56:39 -0700655 void abort() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700656 if (mCreateConnectionProcessor != null) {
657 mCreateConnectionProcessor.abort();
Santos Cordon682fe6b2014-05-20 08:56:39 -0700658 }
659 }
660
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800661 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800662 * Answers the call if it is ringing.
663 */
664 void answer() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700665 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800666
667 // Check to verify that the call is still in the ringing state. A call can change states
668 // between the time the user hits 'answer' and Telecomm receives the command.
669 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700670 // At this point, we are asking the connection service to answer but we don't assume
671 // that it will work. Instead, we wait until confirmation from the connectino service
672 // that the call is in a non-RINGING state before changing the UI. See
673 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
674 mConnectionService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800675 }
676 }
677
678 /**
679 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700680 *
681 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
682 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800683 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700684 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700685 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800686
687 // Check to verify that the call is still in the ringing state. A call can change states
688 // between the time the user hits 'reject' and Telecomm receives the command.
689 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700690 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800691 }
692 }
693
694 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700695 * Puts the call on hold if it is currently active.
696 */
697 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700698 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700699
700 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700701 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700702 }
703 }
704
705 /**
706 * Releases the call from hold if it is currently active.
707 */
708 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700709 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700710
711 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700712 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700713 }
714 }
715
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700716 /** Checks if this is a live call or not. */
717 boolean isAlive() {
718 switch (mState) {
719 case NEW:
720 case RINGING:
721 case DISCONNECTED:
722 case ABORTED:
723 return false;
724 default:
725 return true;
726 }
727 }
728
Santos Cordon40f78c22014-04-07 02:11:42 -0700729 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -0700730 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -0700731 }
732
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700733 Bundle getExtras() {
734 return mExtras;
735 }
736
737 void setExtras(Bundle extras) {
738 mExtras = extras;
739 }
740
Santos Cordon5ba7f272014-05-28 13:59:49 -0700741 Uri getRingtone() {
742 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
743 }
744
Evan Charlton352105c2014-06-03 14:10:54 -0700745 void onPostDialWait(String remaining) {
746 for (Listener l : mListeners) {
747 l.onPostDialWait(this, remaining);
748 }
749 }
750
751 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700752 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700753 }
754
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700755 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700756 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700757 }
758
Santos Cordona1610702014-06-04 20:22:56 -0700759 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700760 if (mConnectionService == null) {
761 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700762 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700763 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700764 }
765 }
766
767 void expireConference() {
768 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700769 // connection service...so start shutting down.
770 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700771 for (Listener l : mListeners) {
772 l.onExpiredConferenceCall(this);
773 }
774 }
775
776 void confirmConference() {
777 Log.v(this, "confirming Conf call %s", mListeners);
778 for (Listener l : mListeners) {
779 l.onConfirmedConferenceCall(this);
780 }
781 }
782
783 void splitFromConference() {
784 // TODO(santoscordon): todo
785 }
786
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700787 void swapWithBackgroundCall() {
788 mConnectionService.swapWithBackgroundCall(this);
789 }
790
Santos Cordona1610702014-06-04 20:22:56 -0700791 void setParentCall(Call parentCall) {
792 if (parentCall == this) {
793 Log.e(this, new Exception(), "setting the parent to self");
794 return;
795 }
796 Preconditions.checkState(parentCall == null || mParentCall == null);
797
798 Call oldParent = mParentCall;
799 if (mParentCall != null) {
800 mParentCall.removeChildCall(this);
801 }
802 mParentCall = parentCall;
803 if (mParentCall != null) {
804 mParentCall.addChildCall(this);
805 }
806
807 for (Listener l : mListeners) {
808 l.onParentChanged(this);
809 }
810 }
811
812 private void addChildCall(Call call) {
813 if (!mChildCalls.contains(call)) {
814 mChildCalls.add(call);
815
816 for (Listener l : mListeners) {
817 l.onChildrenChanged(this);
818 }
819 }
820 }
821
822 private void removeChildCall(Call call) {
823 if (mChildCalls.remove(call)) {
824 for (Listener l : mListeners) {
825 l.onChildrenChanged(this);
826 }
827 }
828 }
829
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800830 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700831 * Return whether the user can respond to this {@code Call} via an SMS message.
832 *
833 * @return true if the "Respond via SMS" feature should be enabled
834 * for this incoming call.
835 *
836 * The general rule is that we *do* allow "Respond via SMS" except for
837 * the few (relatively rare) cases where we know for sure it won't
838 * work, namely:
839 * - a bogus or blank incoming number
840 * - a call from a SIP address
841 * - a "call presentation" that doesn't allow the number to be revealed
842 *
843 * In all other cases, we allow the user to respond via SMS.
844 *
845 * Note that this behavior isn't perfect; for example we have no way
846 * to detect whether the incoming call is from a landline (with most
847 * networks at least), so we still enable this feature even though
848 * SMSes to that number will silently fail.
849 */
850 boolean isRespondViaSmsCapable() {
851 if (mState != CallState.RINGING) {
852 return false;
853 }
854
855 if (getHandle() == null) {
856 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
857 // other words, the user should not be able to see the incoming phone number.
858 return false;
859 }
860
861 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
862 // The incoming number is actually a URI (i.e. a SIP address),
863 // not a regular PSTN phone number, and we can't send SMSes to
864 // SIP addresses.
865 // (TODO: That might still be possible eventually, though. Is
866 // there some SIP-specific equivalent to sending a text message?)
867 return false;
868 }
869
870 // Is there a valid SMS application on the phone?
871 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
872 true /*updateIfNeeded*/) == null) {
873 return false;
874 }
875
876 // TODO: with some carriers (in certain countries) you *can* actually
877 // tell whether a given number is a mobile phone or not. So in that
878 // case we could potentially return false here if the incoming call is
879 // from a land line.
880
881 // If none of the above special cases apply, it's OK to enable the
882 // "Respond via SMS" feature.
883 return true;
884 }
885
886 List<String> getCannedSmsResponses() {
887 return mCannedSmsResponses;
888 }
889
890 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800891 * @return True if the call is ringing, else logs the action name.
892 */
893 private boolean isRinging(String actionName) {
894 if (mState == CallState.RINGING) {
895 return true;
896 }
897
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800898 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800899 return false;
900 }
901
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800902 @SuppressWarnings("rawtypes")
903 private void decrementAssociatedCallCount(ServiceBinder binder) {
904 if (binder != null) {
905 binder.decrementAssociatedCallCount();
906 }
907 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700908
909 /**
910 * Looks up contact information based on the current handle.
911 */
912 private void startCallerInfoLookup() {
913 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
914
915 mQueryToken++; // Updated so that previous queries can no longer set the information.
916 mCallerInfo = null;
917 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700918 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700919 CallerInfoAsyncQuery.startQuery(
920 mQueryToken,
921 TelecommApp.getInstance(),
922 number,
923 sCallerInfoQueryListener,
924 this);
925 }
926 }
927
928 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700929 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700930 * that was made.
931 *
932 * @param callerInfo The new caller information to set.
933 * @param token The token used with this query.
934 */
935 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700936 Preconditions.checkNotNull(callerInfo);
937
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700938 if (mQueryToken == token) {
939 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700940 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700941
Makoto Onukia1662d02014-07-10 15:31:59 -0700942 if (mCallerInfo.contactDisplayPhotoUri != null) {
943 Log.d(this, "Searching person uri %s for call %s",
944 mCallerInfo.contactDisplayPhotoUri, this);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700945 ContactsAsyncHelper.startObtainPhotoAsync(
946 token,
947 TelecommApp.getInstance(),
Makoto Onukia1662d02014-07-10 15:31:59 -0700948 mCallerInfo.contactDisplayPhotoUri,
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700949 sPhotoLoadListener,
950 this);
Makoto Onukia1662d02014-07-10 15:31:59 -0700951 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
Santos Cordon64c7e962014-07-02 15:15:27 -0700952 } else {
953 for (Listener l : mListeners) {
954 l.onCallerInfoChanged(this);
955 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700956 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700957
958 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700959 }
960 }
961
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700962 CallerInfo getCallerInfo() {
963 return mCallerInfo;
964 }
965
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700966 /**
967 * Saves the specified photo information if the specified token matches that of the last query.
968 *
969 * @param photo The photo as a drawable.
970 * @param photoIcon The photo as a small icon.
971 * @param token The token used with this query.
972 */
973 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
974 if (mQueryToken == token) {
975 mCallerInfo.cachedPhoto = photo;
976 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -0700977
978 for (Listener l : mListeners) {
979 l.onCallerInfoChanged(this);
980 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700981 }
982 }
Ihab Awadff7493a2014-06-10 13:47:44 -0700983
984 private void maybeLoadCannedSmsResponses() {
985 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
986 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
987 mCannedSmsResponsesLoadingStarted = true;
988 RespondViaSmsManager.getInstance().loadCannedTextMessages(
989 new Response<Void, List<String>>() {
990 @Override
991 public void onResult(Void request, List<String>... result) {
992 if (result.length > 0) {
993 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
994 mCannedSmsResponses = result[0];
995 for (Listener l : mListeners) {
996 l.onCannedSmsResponsesLoaded(Call.this);
997 }
998 }
999 }
1000
1001 @Override
1002 public void onError(Void request, int code, String msg) {
1003 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1004 msg);
1005 }
1006 }
1007 );
1008 } else {
1009 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1010 }
1011 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001012
1013 /**
1014 * Sets speakerphone option on when call begins.
1015 */
1016 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1017 mSpeakerphoneOn = startWithSpeakerphone;
1018 }
1019
1020 /**
1021 * Returns speakerphone option.
1022 *
1023 * @return Whether or not speakerphone should be set automatically when call begins.
1024 */
1025 public boolean getStartWithSpeakerphoneOn() {
1026 return mSpeakerphoneOn;
1027 }
Andrew Leee9a77652014-06-26 13:07:57 -07001028
1029 /**
1030 * Sets a call video provider for the call.
1031 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001032 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1033 mCallVideoProvider = callVideoProvider;
1034 for (Listener l : mListeners) {
1035 l.onCallVideoProviderChanged(Call.this);
1036 }
1037 }
1038
1039 /**
1040 * @return Return the call video Provider binder.
1041 */
1042 public ICallVideoProvider getCallVideoProvider() {
1043 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001044 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001045
1046 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07001047 * The current video state for the call.
1048 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1049 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1050 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1051 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1052 *
1053 * @return True if video is enabled.
1054 */
1055 public int getVideoState() {
1056 return mVideoState;
1057 }
1058
1059 /**
1060 * At the start of the call, determines the desired video state for the call.
1061 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1062 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1063 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1064 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1065 *
1066 * @param videoState The desired video state for the call.
1067 */
1068 public void setVideoState(int videoState) {
1069 mVideoState = videoState;
1070 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001071
1072 public boolean getAudioModeIsVoip() {
1073 return mAudioModeIsVoip;
1074 }
1075
1076 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1077 mAudioModeIsVoip = audioModeIsVoip;
1078 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001079 l.onAudioModeIsVoipChanged(this);
1080 }
1081 }
1082
1083 public StatusHints getStatusHints() {
1084 return mStatusHints;
1085 }
1086
1087 public void setStatusHints(StatusHints statusHints) {
1088 mStatusHints = statusHints;
1089 for (Listener l : mListeners) {
1090 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001091 }
1092 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001093}