blob: b06b0434152233d72c0cc191b14e2d5275d23934 [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;
46
Ihab Awadff7493a2014-06-10 13:47:44 -070047import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070048import java.util.LinkedList;
49import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080050import java.util.Locale;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070051import java.util.Objects;
Ben Gilad61925612014-03-11 19:06:36 -070052import java.util.Set;
Santos Cordondfc66012014-07-15 13:41:40 -070053import java.util.concurrent.CopyOnWriteArraySet;
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);
Andrew Lee4a796602014-07-11 17:23:03 -070084 void onVideoStateChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070085 }
86
87 abstract static class ListenerBase implements Listener {
88 @Override
89 public void onSuccessfulOutgoingCall(Call call) {}
90 @Override
91 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
92 @Override
93 public void onCancelledOutgoingCall(Call call) {}
94 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -070095 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -070096 @Override
97 public void onFailedIncomingCall(Call call) {}
98 @Override
99 public void onRequestingRingback(Call call, boolean requestingRingback) {}
100 @Override
101 public void onPostDialWait(Call call, String remaining) {}
102 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -0700103 public void onCallCapabilitiesChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700104 @Override
105 public void onExpiredConferenceCall(Call call) {}
106 @Override
107 public void onConfirmedConferenceCall(Call call) {}
108 @Override
109 public void onParentChanged(Call call) {}
110 @Override
111 public void onChildrenChanged(Call call) {}
112 @Override
113 public void onCannedSmsResponsesLoaded(Call call) {}
114 @Override
115 public void onCallVideoProviderChanged(Call call) {}
116 @Override
Santos Cordon64c7e962014-07-02 15:15:27 -0700117 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700118 @Override
119 public void onAudioModeIsVoipChanged(Call call) {}
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700120 @Override
121 public void onStatusHintsChanged(Call call) {}
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700122 @Override
123 public void onHandleChanged(Call call) {}
124 @Override
125 public void onCallerDisplayNameChanged(Call call) {}
Andrew Lee4a796602014-07-11 17:23:03 -0700126 @Override
127 public void onVideoStateChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700128 }
129
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700130 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700131 new OnQueryCompleteListener() {
132 /** ${inheritDoc} */
133 @Override
134 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
135 if (cookie != null) {
136 ((Call) cookie).setCallerInfo(callerInfo, token);
137 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700138 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700139 };
140
141 private static final OnImageLoadCompleteListener sPhotoLoadListener =
142 new OnImageLoadCompleteListener() {
143 /** ${inheritDoc} */
144 @Override
145 public void onImageLoadComplete(
146 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
147 if (cookie != null) {
148 ((Call) cookie).setPhoto(photo, photoIcon, token);
149 }
150 }
151 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800152
Sailesh Nepal664837f2014-07-14 16:31:51 -0700153 private final Runnable mDirectToVoicemailRunnable = new Runnable() {
154 @Override
155 public void run() {
156 processDirectToVoicemail();
157 }
158 };
159
Sailesh Nepal810735e2014-03-18 18:15:46 -0700160 /** True if this is an incoming call. */
161 private final boolean mIsIncoming;
162
Ben Gilad0407fb22014-01-09 16:18:41 -0800163 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700164 * The time this call was created. Beyond logging and such, may also be used for bookkeeping
165 * and specifically for marking certain call attempts as failed attempts.
Ben Gilad0407fb22014-01-09 16:18:41 -0800166 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700167 private final long mCreationTimeMillis = System.currentTimeMillis();
168
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700169 /** The gateway information associated with this call. This stores the original call handle
170 * that the user is attempting to connect to via the gateway, the actual handle to dial in
171 * order to connect the call via the gateway, as well as the package name of the gateway
172 * service. */
173 private final GatewayInfo mGatewayInfo;
174
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700175 private PhoneAccount mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700176
Santos Cordon2174fb52014-05-29 08:22:56 -0700177 private final Handler mHandler = new Handler();
178
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700179 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800180
Santos Cordon61d0f702014-02-19 02:52:23 -0800181 /** The state of the call. */
182 private CallState mState;
183
184 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700185 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800186
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700187 /** The {@link CallPropertyPresentation} that controls how the handle is shown. */
188 private int mHandlePresentation;
189
190 /** The caller display name (CNAP) set by the connection service. */
191 private String mCallerDisplayName;
192
193 /** The {@link CallPropertyPresentation} that controls how the caller display name is shown. */
194 private int mCallerDisplayNamePresentation;
195
Ben Gilad0407fb22014-01-09 16:18:41 -0800196 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700197 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800198 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700199 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700200
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700201 private boolean mIsEmergencyCall;
202
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700203 private boolean mSpeakerphoneOn;
204
Tyler Gunnc4abd912014-07-08 14:22:10 -0700205 private int mVideoState;
206
Ben Gilad61925612014-03-11 19:06:36 -0700207 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700208 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
209 * See {@link android.telephony.DisconnectCause}.
210 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700211 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700212
213 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700214 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700215 */
216 private String mDisconnectMessage;
217
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700218 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700219 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700220
Santos Cordon766d04f2014-05-06 10:28:25 -0700221 /** Set of listeners on this call. */
Santos Cordondfc66012014-07-15 13:41:40 -0700222 private Set<Listener> mListeners = new CopyOnWriteArraySet<>();
Santos Cordon766d04f2014-05-06 10:28:25 -0700223
Sailesh Nepal664837f2014-07-14 16:31:51 -0700224 private CreateConnectionProcessor mCreateConnectionProcessor;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700225
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700226 /** Caller information retrieved from the latest contact query. */
227 private CallerInfo mCallerInfo;
228
229 /** The latest token used with a contact info query. */
230 private int mQueryToken = 0;
231
Ihab Awadcb387ac2014-05-28 16:49:38 -0700232 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
233 private boolean mRequestingRingback = false;
234
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700235 /** Whether direct-to-voicemail query is pending. */
236 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700237
Sailesh Nepale20bc972014-07-09 21:22:36 -0700238 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700239
240 private boolean mIsConference = false;
241
242 private Call mParentCall = null;
243
244 private List<Call> mChildCalls = new LinkedList<>();
245
Ihab Awadff7493a2014-06-10 13:47:44 -0700246 /** Set of text message responses allowed for this call, if applicable. */
247 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
248
249 /** Whether an attempt has been made to load the text message responses. */
250 private boolean mCannedSmsResponsesLoadingStarted = false;
251
Nancy Chena65d41f2014-06-24 12:06:03 -0700252 private ICallVideoProvider mCallVideoProvider;
253
Sailesh Nepal7e669572014-07-08 21:29:12 -0700254 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700255 private StatusHints mStatusHints;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700256 private final ConnectionServiceRepository mRepository;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700257
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700258 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800259 * Persists the specified parameters and initializes the new instance.
260 *
261 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700262 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700263 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700264 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800265 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700266 Call(
267 ConnectionServiceRepository repository,
268 Uri handle,
269 GatewayInfo gatewayInfo,
270 PhoneAccount account,
271 boolean isIncoming,
272 boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700273 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700274 mRepository = repository;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700275 setHandle(handle, CallPropertyPresentation.ALLOWED);
Yorke Lee33501632014-03-17 19:24:12 -0700276 mGatewayInfo = gatewayInfo;
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700277 mPhoneAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700278 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700279 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700280 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800281 }
282
Santos Cordon766d04f2014-05-06 10:28:25 -0700283 void addListener(Listener listener) {
284 mListeners.add(listener);
285 }
286
287 void removeListener(Listener listener) {
288 mListeners.remove(listener);
289 }
290
Santos Cordon61d0f702014-02-19 02:52:23 -0800291 /** {@inheritDoc} */
292 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700293 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700294 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
295 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700296 }
297 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800298 }
299
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800300 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700301 if (mIsConference) {
302 if (!mChildCalls.isEmpty()) {
303 // If we have child calls, just return the child call.
304 return mChildCalls.get(0).getState();
305 }
306 return CallState.ACTIVE;
307 } else {
308 return mState;
309 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800310 }
311
312 /**
313 * Sets the call state. Although there exists the notion of appropriate state transitions
314 * (see {@link CallState}), in practice those expectations break down when cellular systems
315 * misbehave and they do this very often. The result is that we do not enforce state transitions
316 * and instead keep the code resilient to unexpected state changes.
317 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700318 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700319 Preconditions.checkState(newState != CallState.DISCONNECTED ||
320 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700321 if (mState != newState) {
322 Log.v(this, "setState %s -> %s", mState, newState);
323 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700324 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700325 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800326 }
327
Ihab Awadcb387ac2014-05-28 16:49:38 -0700328 void setRequestingRingback(boolean requestingRingback) {
329 mRequestingRingback = requestingRingback;
330 for (Listener l : mListeners) {
331 l.onRequestingRingback(this, mRequestingRingback);
332 }
333 }
334
335 boolean isRequestingRingback() {
336 return mRequestingRingback;
337 }
338
Sailesh Nepalce704b92014-03-17 18:31:43 -0700339 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800340 return mHandle;
341 }
342
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700343 int getHandlePresentation() {
344 return mHandlePresentation;
345 }
346
347 void setHandle(Uri handle, int presentation) {
348 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700349 mHandle = handle;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700350 mHandlePresentation = presentation;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700351 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700352 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700353 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700354 for (Listener l : mListeners) {
355 l.onHandleChanged(this);
356 }
357 }
358 }
359
360 String getCallerDisplayName() {
361 return mCallerDisplayName;
362 }
363
364 int getCallerDisplayNamePresentation() {
365 return mCallerDisplayNamePresentation;
366 }
367
368 void setCallerDisplayName(String callerDisplayName, int presentation) {
369 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
370 presentation != mCallerDisplayNamePresentation) {
371 mCallerDisplayName = callerDisplayName;
372 mCallerDisplayNamePresentation = presentation;
373 for (Listener l : mListeners) {
374 l.onCallerDisplayNameChanged(this);
375 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700376 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700377 }
378
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700379 String getName() {
380 return mCallerInfo == null ? null : mCallerInfo.name;
381 }
382
383 Bitmap getPhotoIcon() {
384 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
385 }
386
387 Drawable getPhoto() {
388 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
389 }
390
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700391 /**
392 * @param disconnectCause The reason for the disconnection, any of
393 * {@link android.telephony.DisconnectCause}.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700394 * @param disconnectMessage Optional message about the disconnect.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700395 */
396 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
397 // TODO: Consider combining this method with a setDisconnected() method that is totally
398 // separate from setState.
399 mDisconnectCause = disconnectCause;
400 mDisconnectMessage = disconnectMessage;
401 }
402
403 int getDisconnectCause() {
404 return mDisconnectCause;
405 }
406
407 String getDisconnectMessage() {
408 return mDisconnectMessage;
409 }
410
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700411 boolean isEmergencyCall() {
412 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800413 }
414
Yorke Lee33501632014-03-17 19:24:12 -0700415 /**
416 * @return The original handle this call is associated with. In-call services should use this
417 * handle when indicating in their UI the handle that is being called.
418 */
419 public Uri getOriginalHandle() {
420 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
421 return mGatewayInfo.getOriginalHandle();
422 }
423 return getHandle();
424 }
425
426 GatewayInfo getGatewayInfo() {
427 return mGatewayInfo;
428 }
429
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700430 PhoneAccount getPhoneAccount() {
431 return mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700432 }
433
Nancy Chen53ceedc2014-07-08 18:56:51 -0700434 void setPhoneAccount(PhoneAccount account) {
435 mPhoneAccount = account;
436 }
437
Sailesh Nepal810735e2014-03-18 18:15:46 -0700438 boolean isIncoming() {
439 return mIsIncoming;
440 }
441
Ben Gilad0407fb22014-01-09 16:18:41 -0800442 /**
443 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700444 * period since this call was added to the set pending outgoing calls, see
445 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800446 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700447 long getAgeMillis() {
448 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800449 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800450
Yorke Leef98fb572014-03-05 10:56:55 -0800451 /**
452 * @return The time when this call object was created and added to the set of pending outgoing
453 * calls.
454 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700455 long getCreationTimeMillis() {
456 return mCreationTimeMillis;
457 }
458
459 long getConnectTimeMillis() {
460 return mConnectTimeMillis;
461 }
462
463 void setConnectTimeMillis(long connectTimeMillis) {
464 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800465 }
466
Sailesh Nepale20bc972014-07-09 21:22:36 -0700467 int getCallCapabilities() {
468 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700469 }
470
Sailesh Nepale20bc972014-07-09 21:22:36 -0700471 void setCallCapabilities(int callCapabilities) {
472 if (mCallCapabilities != callCapabilities) {
473 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700474 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700475 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700476 }
477 }
478 }
479
480 Call getParentCall() {
481 return mParentCall;
482 }
483
484 List<Call> getChildCalls() {
485 return mChildCalls;
486 }
487
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700488 ConnectionServiceWrapper getConnectionService() {
489 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800490 }
491
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700492 void setConnectionService(ConnectionServiceWrapper service) {
493 Preconditions.checkNotNull(service);
494
495 clearConnectionService();
496
497 service.incrementAssociatedCallCount();
498 mConnectionService = service;
499 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700500 }
501
502 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700503 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700504 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700505 void clearConnectionService() {
506 if (mConnectionService != null) {
507 ConnectionServiceWrapper serviceTemp = mConnectionService;
508 mConnectionService = null;
509 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700510
511 // Decrementing the count can cause the service to unbind, which itself can trigger the
512 // service-death code. Since the service death code tries to clean up any associated
513 // calls, we need to make sure to remove that information (e.g., removeCall()) before
514 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700515 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
516 // to do.
517 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700518 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800519 }
520
Sailesh Nepal664837f2014-07-14 16:31:51 -0700521 private void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700522 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700523 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
524 Log.i(this, "Directing call to voicemail: %s.", this);
525 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
526 // will not need to set RINGING state prior to calling reject.
527 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700528 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700529 } else {
530 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
531 // the call state to RINGING.
532
533 // TODO(santoscordon): Replace this with state transition to RINGING.
534 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700535 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700536 }
537 }
538
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700539 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700540 }
541 }
542
Santos Cordon766d04f2014-05-06 10:28:25 -0700543 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700544 * Starts the create connection sequence. Upon completion, there should exist an active
545 * connection through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700546 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700547 void startCreateConnection() {
548 Preconditions.checkState(mCreateConnectionProcessor == null);
549 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
550 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700551 }
552
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700553 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700554 public void handleCreateConnectionSuccessful(ConnectionRequest request) {
555 mCreateConnectionProcessor = null;
556 mPhoneAccount = request.getAccount();
557
558 if (mIsIncoming) {
559 // We do not handle incoming calls immediately when they are verified by the connection
560 // service. We allow the caller-info-query code to execute first so that we can read the
561 // direct-to-voicemail property before deciding if we want to show the incoming call to
562 // the user or if we want to reject the call.
563 mDirectToVoicemailQueryPending = true;
564
565 // Setting the handle triggers the caller info lookup code.
566 setHandle(request.getHandle(), request.getHandlePresentation());
567
568 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
569 // showing the user the incoming call screen.
570 mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
571 } else {
572 for (Listener l : mListeners) {
573 l.onSuccessfulOutgoingCall(this);
574 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700575 }
576 }
577
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700578 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700579 public void handleCreateConnectionFailed(int code, String msg) {
580 mCreateConnectionProcessor = null;
581 if (mIsIncoming) {
582 clearConnectionService();
583 setDisconnectCause(code, null);
584 setState(CallState.DISCONNECTED);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700585
Sailesh Nepal664837f2014-07-14 16:31:51 -0700586 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
587 for (int i = 0; i < listeners.length; i++) {
588 listeners[i].onFailedIncomingCall(this);
589 }
590 } else {
591 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
592 for (int i = 0; i < listeners.length; i++) {
593 listeners[i].onFailedOutgoingCall(this, code, msg);
594 }
595 clearConnectionService();
596 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700597 }
598
599 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700600 public void handleCreateConnectionCancelled() {
601 mCreateConnectionProcessor = null;
602 if (mIsIncoming) {
603 clearConnectionService();
604 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
605 setState(CallState.DISCONNECTED);
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700606
Sailesh Nepal664837f2014-07-14 16:31:51 -0700607 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
608 for (int i = 0; i < listeners.length; i++) {
609 listeners[i].onFailedIncomingCall(this);
610 }
611 } else {
612 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
613 for (int i = 0; i < listeners.length; i++) {
614 listeners[i].onCancelledOutgoingCall(this);
615 }
616 clearConnectionService();
617 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700618 }
619
620 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700621 * Plays the specified DTMF tone.
622 */
623 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700624 if (mConnectionService == null) {
625 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700626 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700627 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
628 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700629 }
630 }
631
632 /**
633 * Stops playing any currently playing DTMF tone.
634 */
635 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700636 if (mConnectionService == null) {
637 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700638 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700639 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
640 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700641 }
642 }
643
644 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700645 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800646 */
647 void disconnect() {
Nancy Chen53ceedc2014-07-08 18:56:51 -0700648 if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700649 Log.v(this, "Aborting call %s", this);
650 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700651 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700652 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700653
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700654 Log.i(this, "Send disconnect to connection service for call: %s", this);
655 // The call isn't officially disconnected until the connection service confirms that the
656 // call was actually disconnected. Only then is the association between call and
657 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
658 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800659 }
660 }
661
Santos Cordon682fe6b2014-05-20 08:56:39 -0700662 void abort() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700663 if (mCreateConnectionProcessor != null) {
664 mCreateConnectionProcessor.abort();
Nancy Chen53ceedc2014-07-08 18:56:51 -0700665 } else if (mState == CallState.PRE_DIAL_WAIT) {
666 handleCreateConnectionFailed(DisconnectCause.LOCAL, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700667 }
668 }
669
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800670 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800671 * Answers the call if it is ringing.
Andrew Lee38931d02014-07-16 10:17:36 -0700672 *
673 * @param videoState The video state in which to answer the call.
Santos Cordon61d0f702014-02-19 02:52:23 -0800674 */
Andrew Lee38931d02014-07-16 10:17:36 -0700675 void answer(int videoState) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700676 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800677
678 // Check to verify that the call is still in the ringing state. A call can change states
679 // between the time the user hits 'answer' and Telecomm receives the command.
680 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700681 // At this point, we are asking the connection service to answer but we don't assume
682 // that it will work. Instead, we wait until confirmation from the connectino service
683 // that the call is in a non-RINGING state before changing the UI. See
684 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
Andrew Lee38931d02014-07-16 10:17:36 -0700685 mConnectionService.answer(this, videoState);
Santos Cordon61d0f702014-02-19 02:52:23 -0800686 }
687 }
688
689 /**
690 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700691 *
692 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
693 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800694 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700695 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700696 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800697
698 // Check to verify that the call is still in the ringing state. A call can change states
699 // between the time the user hits 'reject' and Telecomm receives the command.
700 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700701 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800702 }
703 }
704
705 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700706 * Puts the call on hold if it is currently active.
707 */
708 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700709 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700710
711 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700712 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700713 }
714 }
715
716 /**
717 * Releases the call from hold if it is currently active.
718 */
719 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700720 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700721
722 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700723 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700724 }
725 }
726
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700727 /** Checks if this is a live call or not. */
728 boolean isAlive() {
729 switch (mState) {
730 case NEW:
731 case RINGING:
732 case DISCONNECTED:
733 case ABORTED:
734 return false;
735 default:
736 return true;
737 }
738 }
739
Santos Cordon40f78c22014-04-07 02:11:42 -0700740 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -0700741 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -0700742 }
743
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700744 Bundle getExtras() {
745 return mExtras;
746 }
747
748 void setExtras(Bundle extras) {
749 mExtras = extras;
750 }
751
Santos Cordon5ba7f272014-05-28 13:59:49 -0700752 Uri getRingtone() {
753 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
754 }
755
Evan Charlton352105c2014-06-03 14:10:54 -0700756 void onPostDialWait(String remaining) {
757 for (Listener l : mListeners) {
758 l.onPostDialWait(this, remaining);
759 }
760 }
761
762 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700763 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700764 }
765
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700766 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700767 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700768 }
769
Santos Cordona1610702014-06-04 20:22:56 -0700770 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700771 if (mConnectionService == null) {
772 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700773 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700774 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700775 }
776 }
777
778 void expireConference() {
779 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700780 // connection service...so start shutting down.
781 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700782 for (Listener l : mListeners) {
783 l.onExpiredConferenceCall(this);
784 }
785 }
786
787 void confirmConference() {
788 Log.v(this, "confirming Conf call %s", mListeners);
789 for (Listener l : mListeners) {
790 l.onConfirmedConferenceCall(this);
791 }
792 }
793
794 void splitFromConference() {
795 // TODO(santoscordon): todo
796 }
797
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700798 void swapWithBackgroundCall() {
799 mConnectionService.swapWithBackgroundCall(this);
800 }
801
Santos Cordona1610702014-06-04 20:22:56 -0700802 void setParentCall(Call parentCall) {
803 if (parentCall == this) {
804 Log.e(this, new Exception(), "setting the parent to self");
805 return;
806 }
807 Preconditions.checkState(parentCall == null || mParentCall == null);
808
809 Call oldParent = mParentCall;
810 if (mParentCall != null) {
811 mParentCall.removeChildCall(this);
812 }
813 mParentCall = parentCall;
814 if (mParentCall != null) {
815 mParentCall.addChildCall(this);
816 }
817
818 for (Listener l : mListeners) {
819 l.onParentChanged(this);
820 }
821 }
822
823 private void addChildCall(Call call) {
824 if (!mChildCalls.contains(call)) {
825 mChildCalls.add(call);
826
827 for (Listener l : mListeners) {
828 l.onChildrenChanged(this);
829 }
830 }
831 }
832
833 private void removeChildCall(Call call) {
834 if (mChildCalls.remove(call)) {
835 for (Listener l : mListeners) {
836 l.onChildrenChanged(this);
837 }
838 }
839 }
840
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800841 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700842 * Return whether the user can respond to this {@code Call} via an SMS message.
843 *
844 * @return true if the "Respond via SMS" feature should be enabled
845 * for this incoming call.
846 *
847 * The general rule is that we *do* allow "Respond via SMS" except for
848 * the few (relatively rare) cases where we know for sure it won't
849 * work, namely:
850 * - a bogus or blank incoming number
851 * - a call from a SIP address
852 * - a "call presentation" that doesn't allow the number to be revealed
853 *
854 * In all other cases, we allow the user to respond via SMS.
855 *
856 * Note that this behavior isn't perfect; for example we have no way
857 * to detect whether the incoming call is from a landline (with most
858 * networks at least), so we still enable this feature even though
859 * SMSes to that number will silently fail.
860 */
861 boolean isRespondViaSmsCapable() {
862 if (mState != CallState.RINGING) {
863 return false;
864 }
865
866 if (getHandle() == null) {
867 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
868 // other words, the user should not be able to see the incoming phone number.
869 return false;
870 }
871
872 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
873 // The incoming number is actually a URI (i.e. a SIP address),
874 // not a regular PSTN phone number, and we can't send SMSes to
875 // SIP addresses.
876 // (TODO: That might still be possible eventually, though. Is
877 // there some SIP-specific equivalent to sending a text message?)
878 return false;
879 }
880
881 // Is there a valid SMS application on the phone?
882 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
883 true /*updateIfNeeded*/) == null) {
884 return false;
885 }
886
887 // TODO: with some carriers (in certain countries) you *can* actually
888 // tell whether a given number is a mobile phone or not. So in that
889 // case we could potentially return false here if the incoming call is
890 // from a land line.
891
892 // If none of the above special cases apply, it's OK to enable the
893 // "Respond via SMS" feature.
894 return true;
895 }
896
897 List<String> getCannedSmsResponses() {
898 return mCannedSmsResponses;
899 }
900
901 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800902 * @return True if the call is ringing, else logs the action name.
903 */
904 private boolean isRinging(String actionName) {
905 if (mState == CallState.RINGING) {
906 return true;
907 }
908
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800909 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800910 return false;
911 }
912
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800913 @SuppressWarnings("rawtypes")
914 private void decrementAssociatedCallCount(ServiceBinder binder) {
915 if (binder != null) {
916 binder.decrementAssociatedCallCount();
917 }
918 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700919
920 /**
921 * Looks up contact information based on the current handle.
922 */
923 private void startCallerInfoLookup() {
924 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
925
926 mQueryToken++; // Updated so that previous queries can no longer set the information.
927 mCallerInfo = null;
928 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700929 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700930 CallerInfoAsyncQuery.startQuery(
931 mQueryToken,
932 TelecommApp.getInstance(),
933 number,
934 sCallerInfoQueryListener,
935 this);
936 }
937 }
938
939 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700940 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700941 * that was made.
942 *
943 * @param callerInfo The new caller information to set.
944 * @param token The token used with this query.
945 */
946 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700947 Preconditions.checkNotNull(callerInfo);
948
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700949 if (mQueryToken == token) {
950 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700951 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700952
Makoto Onukia1662d02014-07-10 15:31:59 -0700953 if (mCallerInfo.contactDisplayPhotoUri != null) {
954 Log.d(this, "Searching person uri %s for call %s",
955 mCallerInfo.contactDisplayPhotoUri, this);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700956 ContactsAsyncHelper.startObtainPhotoAsync(
957 token,
958 TelecommApp.getInstance(),
Makoto Onukia1662d02014-07-10 15:31:59 -0700959 mCallerInfo.contactDisplayPhotoUri,
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700960 sPhotoLoadListener,
961 this);
Makoto Onukia1662d02014-07-10 15:31:59 -0700962 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
Santos Cordon64c7e962014-07-02 15:15:27 -0700963 } else {
964 for (Listener l : mListeners) {
965 l.onCallerInfoChanged(this);
966 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700967 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700968
969 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700970 }
971 }
972
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700973 CallerInfo getCallerInfo() {
974 return mCallerInfo;
975 }
976
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700977 /**
978 * Saves the specified photo information if the specified token matches that of the last query.
979 *
980 * @param photo The photo as a drawable.
981 * @param photoIcon The photo as a small icon.
982 * @param token The token used with this query.
983 */
984 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
985 if (mQueryToken == token) {
986 mCallerInfo.cachedPhoto = photo;
987 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -0700988
989 for (Listener l : mListeners) {
990 l.onCallerInfoChanged(this);
991 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700992 }
993 }
Ihab Awadff7493a2014-06-10 13:47:44 -0700994
995 private void maybeLoadCannedSmsResponses() {
996 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
997 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
998 mCannedSmsResponsesLoadingStarted = true;
999 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1000 new Response<Void, List<String>>() {
1001 @Override
1002 public void onResult(Void request, List<String>... result) {
1003 if (result.length > 0) {
1004 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1005 mCannedSmsResponses = result[0];
1006 for (Listener l : mListeners) {
1007 l.onCannedSmsResponsesLoaded(Call.this);
1008 }
1009 }
1010 }
1011
1012 @Override
1013 public void onError(Void request, int code, String msg) {
1014 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1015 msg);
1016 }
1017 }
1018 );
1019 } else {
1020 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1021 }
1022 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001023
1024 /**
1025 * Sets speakerphone option on when call begins.
1026 */
1027 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1028 mSpeakerphoneOn = startWithSpeakerphone;
1029 }
1030
1031 /**
1032 * Returns speakerphone option.
1033 *
1034 * @return Whether or not speakerphone should be set automatically when call begins.
1035 */
1036 public boolean getStartWithSpeakerphoneOn() {
1037 return mSpeakerphoneOn;
1038 }
Andrew Leee9a77652014-06-26 13:07:57 -07001039
1040 /**
1041 * Sets a call video provider for the call.
1042 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001043 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1044 mCallVideoProvider = callVideoProvider;
1045 for (Listener l : mListeners) {
1046 l.onCallVideoProviderChanged(Call.this);
1047 }
1048 }
1049
1050 /**
1051 * @return Return the call video Provider binder.
1052 */
1053 public ICallVideoProvider getCallVideoProvider() {
1054 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001055 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001056
1057 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07001058 * The current video state for the call.
1059 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1060 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1061 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1062 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1063 *
1064 * @return True if video is enabled.
1065 */
1066 public int getVideoState() {
1067 return mVideoState;
1068 }
1069
1070 /**
1071 * At the start of the call, determines the desired video state for the call.
1072 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1073 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1074 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1075 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1076 *
1077 * @param videoState The desired video state for the call.
1078 */
1079 public void setVideoState(int videoState) {
1080 mVideoState = videoState;
Andrew Lee4a796602014-07-11 17:23:03 -07001081 for (Listener l : mListeners) {
1082 l.onVideoStateChanged(this);
1083 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001084 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001085
1086 public boolean getAudioModeIsVoip() {
1087 return mAudioModeIsVoip;
1088 }
1089
1090 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1091 mAudioModeIsVoip = audioModeIsVoip;
1092 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001093 l.onAudioModeIsVoipChanged(this);
1094 }
1095 }
1096
1097 public StatusHints getStatusHints() {
1098 return mStatusHints;
1099 }
1100
1101 public void setStatusHints(StatusHints statusHints) {
1102 mStatusHints = statusHints;
1103 for (Listener l : mListeners) {
1104 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001105 }
1106 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001107}