blob: e6a3dbce57a73d876ef77e12e132e1a6670fd394 [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 Nepal5a73b032014-06-25 15:53:21 -070060final class Call implements OutgoingCallResponse {
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 Nepal810735e2014-03-18 18:15:46 -0700150 /** True if this is an incoming call. */
151 private final boolean mIsIncoming;
152
Ben Gilad0407fb22014-01-09 16:18:41 -0800153 /**
154 * The time this call was created, typically also the time this call was added to the set
155 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
156 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
157 * certain call attempts as failed attempts.
158 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700159 private final long mCreationTimeMillis = System.currentTimeMillis();
160
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700161 /** The gateway information associated with this call. This stores the original call handle
162 * that the user is attempting to connect to via the gateway, the actual handle to dial in
163 * order to connect the call via the gateway, as well as the package name of the gateway
164 * service. */
165 private final GatewayInfo mGatewayInfo;
166
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700167 private PhoneAccount mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700168
Santos Cordon2174fb52014-05-29 08:22:56 -0700169 private final Handler mHandler = new Handler();
170
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700171 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800172
Santos Cordon61d0f702014-02-19 02:52:23 -0800173 /** The state of the call. */
174 private CallState mState;
175
176 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700177 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800178
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700179 /** The {@link CallPropertyPresentation} that controls how the handle is shown. */
180 private int mHandlePresentation;
181
182 /** The caller display name (CNAP) set by the connection service. */
183 private String mCallerDisplayName;
184
185 /** The {@link CallPropertyPresentation} that controls how the caller display name is shown. */
186 private int mCallerDisplayNamePresentation;
187
Ben Gilad0407fb22014-01-09 16:18:41 -0800188 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700189 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800190 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700191 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700192
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700193 private boolean mIsEmergencyCall;
194
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700195 private boolean mSpeakerphoneOn;
196
Tyler Gunnc4abd912014-07-08 14:22:10 -0700197 private int mVideoState;
198
Ben Gilad61925612014-03-11 19:06:36 -0700199 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700200 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
201 * See {@link android.telephony.DisconnectCause}.
202 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700203 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700204
205 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700206 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700207 */
208 private String mDisconnectMessage;
209
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700210 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700211 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700212
Santos Cordon766d04f2014-05-06 10:28:25 -0700213 /** Set of listeners on this call. */
214 private Set<Listener> mListeners = Sets.newHashSet();
215
Santos Cordon682fe6b2014-05-20 08:56:39 -0700216 private OutgoingCallProcessor mOutgoingCallProcessor;
217
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700218 /** Caller information retrieved from the latest contact query. */
219 private CallerInfo mCallerInfo;
220
221 /** The latest token used with a contact info query. */
222 private int mQueryToken = 0;
223
Ihab Awadcb387ac2014-05-28 16:49:38 -0700224 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
225 private boolean mRequestingRingback = false;
226
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700227 /** Whether direct-to-voicemail query is pending. */
228 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700229
Sailesh Nepale20bc972014-07-09 21:22:36 -0700230 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700231
232 private boolean mIsConference = false;
233
234 private Call mParentCall = null;
235
236 private List<Call> mChildCalls = new LinkedList<>();
237
Ihab Awadff7493a2014-06-10 13:47:44 -0700238 /** Set of text message responses allowed for this call, if applicable. */
239 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
240
241 /** Whether an attempt has been made to load the text message responses. */
242 private boolean mCannedSmsResponsesLoadingStarted = false;
243
Nancy Chena65d41f2014-06-24 12:06:03 -0700244 private ICallVideoProvider mCallVideoProvider;
245
Sailesh Nepal7e669572014-07-08 21:29:12 -0700246 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700247 private StatusHints mStatusHints;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700248
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700249 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800250 * Persists the specified parameters and initializes the new instance.
251 *
252 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700253 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700254 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700255 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800256 */
Ihab Awad98a55602014-06-30 21:27:28 -0700257 Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700258 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700259 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700260 setHandle(handle, CallPropertyPresentation.ALLOWED);
Yorke Lee33501632014-03-17 19:24:12 -0700261 mGatewayInfo = gatewayInfo;
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700262 mPhoneAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700263 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700264 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700265 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800266 }
267
Santos Cordon766d04f2014-05-06 10:28:25 -0700268 void addListener(Listener listener) {
269 mListeners.add(listener);
270 }
271
272 void removeListener(Listener listener) {
273 mListeners.remove(listener);
274 }
275
Santos Cordon61d0f702014-02-19 02:52:23 -0800276 /** {@inheritDoc} */
277 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700278 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700279 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
280 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700281 }
282 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800283 }
284
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800285 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700286 if (mIsConference) {
287 if (!mChildCalls.isEmpty()) {
288 // If we have child calls, just return the child call.
289 return mChildCalls.get(0).getState();
290 }
291 return CallState.ACTIVE;
292 } else {
293 return mState;
294 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800295 }
296
297 /**
298 * Sets the call state. Although there exists the notion of appropriate state transitions
299 * (see {@link CallState}), in practice those expectations break down when cellular systems
300 * misbehave and they do this very often. The result is that we do not enforce state transitions
301 * and instead keep the code resilient to unexpected state changes.
302 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700303 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700304 Preconditions.checkState(newState != CallState.DISCONNECTED ||
305 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700306 if (mState != newState) {
307 Log.v(this, "setState %s -> %s", mState, newState);
308 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700309 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700310 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800311 }
312
Ihab Awadcb387ac2014-05-28 16:49:38 -0700313 void setRequestingRingback(boolean requestingRingback) {
314 mRequestingRingback = requestingRingback;
315 for (Listener l : mListeners) {
316 l.onRequestingRingback(this, mRequestingRingback);
317 }
318 }
319
320 boolean isRequestingRingback() {
321 return mRequestingRingback;
322 }
323
Sailesh Nepalce704b92014-03-17 18:31:43 -0700324 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800325 return mHandle;
326 }
327
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700328 int getHandlePresentation() {
329 return mHandlePresentation;
330 }
331
332 void setHandle(Uri handle, int presentation) {
333 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700334 mHandle = handle;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700335 mHandlePresentation = presentation;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700336 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700337 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700338 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700339 for (Listener l : mListeners) {
340 l.onHandleChanged(this);
341 }
342 }
343 }
344
345 String getCallerDisplayName() {
346 return mCallerDisplayName;
347 }
348
349 int getCallerDisplayNamePresentation() {
350 return mCallerDisplayNamePresentation;
351 }
352
353 void setCallerDisplayName(String callerDisplayName, int presentation) {
354 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
355 presentation != mCallerDisplayNamePresentation) {
356 mCallerDisplayName = callerDisplayName;
357 mCallerDisplayNamePresentation = presentation;
358 for (Listener l : mListeners) {
359 l.onCallerDisplayNameChanged(this);
360 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700361 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700362 }
363
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700364 String getName() {
365 return mCallerInfo == null ? null : mCallerInfo.name;
366 }
367
368 Bitmap getPhotoIcon() {
369 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
370 }
371
372 Drawable getPhoto() {
373 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
374 }
375
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700376 /**
377 * @param disconnectCause The reason for the disconnection, any of
378 * {@link android.telephony.DisconnectCause}.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700379 * @param disconnectMessage Optional message about the disconnect.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700380 */
381 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
382 // TODO: Consider combining this method with a setDisconnected() method that is totally
383 // separate from setState.
384 mDisconnectCause = disconnectCause;
385 mDisconnectMessage = disconnectMessage;
386 }
387
388 int getDisconnectCause() {
389 return mDisconnectCause;
390 }
391
392 String getDisconnectMessage() {
393 return mDisconnectMessage;
394 }
395
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700396 boolean isEmergencyCall() {
397 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800398 }
399
Yorke Lee33501632014-03-17 19:24:12 -0700400 /**
401 * @return The original handle this call is associated with. In-call services should use this
402 * handle when indicating in their UI the handle that is being called.
403 */
404 public Uri getOriginalHandle() {
405 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
406 return mGatewayInfo.getOriginalHandle();
407 }
408 return getHandle();
409 }
410
411 GatewayInfo getGatewayInfo() {
412 return mGatewayInfo;
413 }
414
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700415 PhoneAccount getPhoneAccount() {
416 return mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700417 }
418
Sailesh Nepal810735e2014-03-18 18:15:46 -0700419 boolean isIncoming() {
420 return mIsIncoming;
421 }
422
Ben Gilad0407fb22014-01-09 16:18:41 -0800423 /**
424 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700425 * period since this call was added to the set pending outgoing calls, see
426 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800427 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700428 long getAgeMillis() {
429 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800430 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800431
Yorke Leef98fb572014-03-05 10:56:55 -0800432 /**
433 * @return The time when this call object was created and added to the set of pending outgoing
434 * calls.
435 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700436 long getCreationTimeMillis() {
437 return mCreationTimeMillis;
438 }
439
440 long getConnectTimeMillis() {
441 return mConnectTimeMillis;
442 }
443
444 void setConnectTimeMillis(long connectTimeMillis) {
445 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800446 }
447
Sailesh Nepale20bc972014-07-09 21:22:36 -0700448 int getCallCapabilities() {
449 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700450 }
451
Sailesh Nepale20bc972014-07-09 21:22:36 -0700452 void setCallCapabilities(int callCapabilities) {
453 if (mCallCapabilities != callCapabilities) {
454 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700455 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700456 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700457 }
458 }
459 }
460
461 Call getParentCall() {
462 return mParentCall;
463 }
464
465 List<Call> getChildCalls() {
466 return mChildCalls;
467 }
468
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700469 ConnectionServiceWrapper getConnectionService() {
470 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800471 }
472
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700473 void setConnectionService(ConnectionServiceWrapper service) {
474 Preconditions.checkNotNull(service);
475
476 clearConnectionService();
477
478 service.incrementAssociatedCallCount();
479 mConnectionService = service;
480 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700481 }
482
483 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700484 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700485 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700486 void clearConnectionService() {
487 if (mConnectionService != null) {
488 ConnectionServiceWrapper serviceTemp = mConnectionService;
489 mConnectionService = null;
490 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700491
492 // Decrementing the count can cause the service to unbind, which itself can trigger the
493 // service-death code. Since the service death code tries to clean up any associated
494 // calls, we need to make sure to remove that information (e.g., removeCall()) before
495 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700496 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
497 // to do.
498 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700499 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800500 }
501
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800502 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700503 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
504 * invoke handle[Un]SuccessfulIncomingCall.
Santos Cordon766d04f2014-05-06 10:28:25 -0700505 */
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700506 void startIncoming() {
507 Switchboard.getInstance().retrieveIncomingCall(this);
Santos Cordon766d04f2014-05-06 10:28:25 -0700508 }
509
Santos Cordon2174fb52014-05-29 08:22:56 -0700510 /**
511 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
512 * from the contacts provider. The call is not yet exposed to the user at this point and
513 * the result of the query will determine if the call is rejected or passed through to the
514 * in-call UI.
515 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700516 void handleVerifiedIncoming(ConnectionRequest request) {
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700517 mPhoneAccount = request.getAccount();
518
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700519 // We do not handle incoming calls immediately when they are verified by the connection
520 // service. We allow the caller-info-query code to execute first so that we can read the
Santos Cordon2174fb52014-05-29 08:22:56 -0700521 // direct-to-voicemail property before deciding if we want to show the incoming call to the
522 // user or if we want to reject the call.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700523 mDirectToVoicemailQueryPending = true;
Santos Cordon2174fb52014-05-29 08:22:56 -0700524
525 // Setting the handle triggers the caller info lookup code.
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700526 setHandle(request.getHandle(), CallPropertyPresentation.ALLOWED);
Santos Cordon766d04f2014-05-06 10:28:25 -0700527
Santos Cordon2174fb52014-05-29 08:22:56 -0700528 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
529 // showing the user the incoming call screen.
530 mHandler.postDelayed(new Runnable() {
531 @Override
532 public void run() {
533 processDirectToVoicemail();
534 }
Santos Cordona1610702014-06-04 20:22:56 -0700535 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700536 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700537
Santos Cordon2174fb52014-05-29 08:22:56 -0700538 void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700539 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700540 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
541 Log.i(this, "Directing call to voicemail: %s.", this);
542 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
543 // will not need to set RINGING state prior to calling reject.
544 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700545 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700546 } else {
547 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
548 // the call state to RINGING.
549
550 // TODO(santoscordon): Replace this with state transition to RINGING.
551 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700552 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700553 }
554 }
555
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700556 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700557 }
558 }
559
560 void handleFailedIncoming() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700561 clearConnectionService();
Santos Cordon766d04f2014-05-06 10:28:25 -0700562
563 // TODO: Needs more specific disconnect error for this case.
564 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
565 setState(CallState.DISCONNECTED);
566
567 // TODO(santoscordon): Replace this with state transitions related to "connecting".
568 for (Listener l : mListeners) {
569 l.onFailedIncomingCall(this);
570 }
571 }
572
573 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700574 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700575 * through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700576 */
577 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700578 Preconditions.checkState(mOutgoingCallProcessor == null);
579
580 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700581 this, Switchboard.getInstance().getConnectionServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700582 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700583 }
584
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700585 @Override
586 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700587 // TODO(santoscordon): Replace this with state transitions related to "connecting".
588 for (Listener l : mListeners) {
589 l.onSuccessfulOutgoingCall(this);
590 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700591 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700592 }
593
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700594 @Override
595 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700596 // TODO(santoscordon): Replace this with state transitions related to "connecting".
597 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700598 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700599 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700600
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700601 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700602 mOutgoingCallProcessor = null;
603 }
604
605 @Override
606 public void onOutgoingCallCancel() {
607 // TODO(santoscordon): Replace this with state transitions related to "connecting".
608 for (Listener l : mListeners) {
609 l.onCancelledOutgoingCall(this);
610 }
611
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700612 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700613 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700614 }
615
616 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700617 * Plays the specified DTMF tone.
618 */
619 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700620 if (mConnectionService == null) {
621 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700622 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700623 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
624 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700625 }
626 }
627
628 /**
629 * Stops playing any currently playing DTMF tone.
630 */
631 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700632 if (mConnectionService == null) {
633 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700634 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700635 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
636 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700637 }
638 }
639
640 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700641 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800642 */
643 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700644 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700645 Log.v(this, "Aborting call %s", this);
646 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700647 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700648 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700649
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700650 Log.i(this, "Send disconnect to connection service for call: %s", this);
651 // The call isn't officially disconnected until the connection service confirms that the
652 // call was actually disconnected. Only then is the association between call and
653 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
654 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800655 }
656 }
657
Santos Cordon682fe6b2014-05-20 08:56:39 -0700658 void abort() {
659 if (mOutgoingCallProcessor != null) {
660 mOutgoingCallProcessor.abort();
661 }
662 }
663
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800664 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800665 * Answers the call if it is ringing.
666 */
667 void answer() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700668 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800669
670 // Check to verify that the call is still in the ringing state. A call can change states
671 // between the time the user hits 'answer' and Telecomm receives the command.
672 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700673 // At this point, we are asking the connection service to answer but we don't assume
674 // that it will work. Instead, we wait until confirmation from the connectino service
675 // that the call is in a non-RINGING state before changing the UI. See
676 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
677 mConnectionService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800678 }
679 }
680
681 /**
682 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700683 *
684 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
685 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800686 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700687 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700688 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800689
690 // Check to verify that the call is still in the ringing state. A call can change states
691 // between the time the user hits 'reject' and Telecomm receives the command.
692 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700693 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800694 }
695 }
696
697 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700698 * Puts the call on hold if it is currently active.
699 */
700 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700701 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700702
703 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700704 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700705 }
706 }
707
708 /**
709 * Releases the call from hold if it is currently active.
710 */
711 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700712 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700713
714 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700715 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700716 }
717 }
718
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700719 /** Checks if this is a live call or not. */
720 boolean isAlive() {
721 switch (mState) {
722 case NEW:
723 case RINGING:
724 case DISCONNECTED:
725 case ABORTED:
726 return false;
727 default:
728 return true;
729 }
730 }
731
Santos Cordon40f78c22014-04-07 02:11:42 -0700732 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -0700733 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -0700734 }
735
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700736 Bundle getExtras() {
737 return mExtras;
738 }
739
740 void setExtras(Bundle extras) {
741 mExtras = extras;
742 }
743
Santos Cordon5ba7f272014-05-28 13:59:49 -0700744 Uri getRingtone() {
745 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
746 }
747
Evan Charlton352105c2014-06-03 14:10:54 -0700748 void onPostDialWait(String remaining) {
749 for (Listener l : mListeners) {
750 l.onPostDialWait(this, remaining);
751 }
752 }
753
754 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700755 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700756 }
757
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700758 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700759 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700760 }
761
Santos Cordona1610702014-06-04 20:22:56 -0700762 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700763 if (mConnectionService == null) {
764 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700765 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700766 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700767 }
768 }
769
770 void expireConference() {
771 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700772 // connection service...so start shutting down.
773 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700774 for (Listener l : mListeners) {
775 l.onExpiredConferenceCall(this);
776 }
777 }
778
779 void confirmConference() {
780 Log.v(this, "confirming Conf call %s", mListeners);
781 for (Listener l : mListeners) {
782 l.onConfirmedConferenceCall(this);
783 }
784 }
785
786 void splitFromConference() {
787 // TODO(santoscordon): todo
788 }
789
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700790 void swapWithBackgroundCall() {
791 mConnectionService.swapWithBackgroundCall(this);
792 }
793
Santos Cordona1610702014-06-04 20:22:56 -0700794 void setParentCall(Call parentCall) {
795 if (parentCall == this) {
796 Log.e(this, new Exception(), "setting the parent to self");
797 return;
798 }
799 Preconditions.checkState(parentCall == null || mParentCall == null);
800
801 Call oldParent = mParentCall;
802 if (mParentCall != null) {
803 mParentCall.removeChildCall(this);
804 }
805 mParentCall = parentCall;
806 if (mParentCall != null) {
807 mParentCall.addChildCall(this);
808 }
809
810 for (Listener l : mListeners) {
811 l.onParentChanged(this);
812 }
813 }
814
815 private void addChildCall(Call call) {
816 if (!mChildCalls.contains(call)) {
817 mChildCalls.add(call);
818
819 for (Listener l : mListeners) {
820 l.onChildrenChanged(this);
821 }
822 }
823 }
824
825 private void removeChildCall(Call call) {
826 if (mChildCalls.remove(call)) {
827 for (Listener l : mListeners) {
828 l.onChildrenChanged(this);
829 }
830 }
831 }
832
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800833 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700834 * Return whether the user can respond to this {@code Call} via an SMS message.
835 *
836 * @return true if the "Respond via SMS" feature should be enabled
837 * for this incoming call.
838 *
839 * The general rule is that we *do* allow "Respond via SMS" except for
840 * the few (relatively rare) cases where we know for sure it won't
841 * work, namely:
842 * - a bogus or blank incoming number
843 * - a call from a SIP address
844 * - a "call presentation" that doesn't allow the number to be revealed
845 *
846 * In all other cases, we allow the user to respond via SMS.
847 *
848 * Note that this behavior isn't perfect; for example we have no way
849 * to detect whether the incoming call is from a landline (with most
850 * networks at least), so we still enable this feature even though
851 * SMSes to that number will silently fail.
852 */
853 boolean isRespondViaSmsCapable() {
854 if (mState != CallState.RINGING) {
855 return false;
856 }
857
858 if (getHandle() == null) {
859 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
860 // other words, the user should not be able to see the incoming phone number.
861 return false;
862 }
863
864 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
865 // The incoming number is actually a URI (i.e. a SIP address),
866 // not a regular PSTN phone number, and we can't send SMSes to
867 // SIP addresses.
868 // (TODO: That might still be possible eventually, though. Is
869 // there some SIP-specific equivalent to sending a text message?)
870 return false;
871 }
872
873 // Is there a valid SMS application on the phone?
874 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
875 true /*updateIfNeeded*/) == null) {
876 return false;
877 }
878
879 // TODO: with some carriers (in certain countries) you *can* actually
880 // tell whether a given number is a mobile phone or not. So in that
881 // case we could potentially return false here if the incoming call is
882 // from a land line.
883
884 // If none of the above special cases apply, it's OK to enable the
885 // "Respond via SMS" feature.
886 return true;
887 }
888
889 List<String> getCannedSmsResponses() {
890 return mCannedSmsResponses;
891 }
892
893 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800894 * @return True if the call is ringing, else logs the action name.
895 */
896 private boolean isRinging(String actionName) {
897 if (mState == CallState.RINGING) {
898 return true;
899 }
900
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800901 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800902 return false;
903 }
904
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800905 @SuppressWarnings("rawtypes")
906 private void decrementAssociatedCallCount(ServiceBinder binder) {
907 if (binder != null) {
908 binder.decrementAssociatedCallCount();
909 }
910 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700911
912 /**
913 * Looks up contact information based on the current handle.
914 */
915 private void startCallerInfoLookup() {
916 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
917
918 mQueryToken++; // Updated so that previous queries can no longer set the information.
919 mCallerInfo = null;
920 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700921 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700922 CallerInfoAsyncQuery.startQuery(
923 mQueryToken,
924 TelecommApp.getInstance(),
925 number,
926 sCallerInfoQueryListener,
927 this);
928 }
929 }
930
931 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700932 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700933 * that was made.
934 *
935 * @param callerInfo The new caller information to set.
936 * @param token The token used with this query.
937 */
938 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700939 Preconditions.checkNotNull(callerInfo);
940
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700941 if (mQueryToken == token) {
942 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700943 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700944
Makoto Onukia1662d02014-07-10 15:31:59 -0700945 if (mCallerInfo.contactDisplayPhotoUri != null) {
946 Log.d(this, "Searching person uri %s for call %s",
947 mCallerInfo.contactDisplayPhotoUri, this);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700948 ContactsAsyncHelper.startObtainPhotoAsync(
949 token,
950 TelecommApp.getInstance(),
Makoto Onukia1662d02014-07-10 15:31:59 -0700951 mCallerInfo.contactDisplayPhotoUri,
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700952 sPhotoLoadListener,
953 this);
Makoto Onukia1662d02014-07-10 15:31:59 -0700954 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
Santos Cordon64c7e962014-07-02 15:15:27 -0700955 } else {
956 for (Listener l : mListeners) {
957 l.onCallerInfoChanged(this);
958 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700959 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700960
961 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700962 }
963 }
964
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700965 CallerInfo getCallerInfo() {
966 return mCallerInfo;
967 }
968
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700969 /**
970 * Saves the specified photo information if the specified token matches that of the last query.
971 *
972 * @param photo The photo as a drawable.
973 * @param photoIcon The photo as a small icon.
974 * @param token The token used with this query.
975 */
976 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
977 if (mQueryToken == token) {
978 mCallerInfo.cachedPhoto = photo;
979 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -0700980
981 for (Listener l : mListeners) {
982 l.onCallerInfoChanged(this);
983 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700984 }
985 }
Ihab Awadff7493a2014-06-10 13:47:44 -0700986
987 private void maybeLoadCannedSmsResponses() {
988 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
989 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
990 mCannedSmsResponsesLoadingStarted = true;
991 RespondViaSmsManager.getInstance().loadCannedTextMessages(
992 new Response<Void, List<String>>() {
993 @Override
994 public void onResult(Void request, List<String>... result) {
995 if (result.length > 0) {
996 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
997 mCannedSmsResponses = result[0];
998 for (Listener l : mListeners) {
999 l.onCannedSmsResponsesLoaded(Call.this);
1000 }
1001 }
1002 }
1003
1004 @Override
1005 public void onError(Void request, int code, String msg) {
1006 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1007 msg);
1008 }
1009 }
1010 );
1011 } else {
1012 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1013 }
1014 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001015
1016 /**
1017 * Sets speakerphone option on when call begins.
1018 */
1019 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1020 mSpeakerphoneOn = startWithSpeakerphone;
1021 }
1022
1023 /**
1024 * Returns speakerphone option.
1025 *
1026 * @return Whether or not speakerphone should be set automatically when call begins.
1027 */
1028 public boolean getStartWithSpeakerphoneOn() {
1029 return mSpeakerphoneOn;
1030 }
Andrew Leee9a77652014-06-26 13:07:57 -07001031
1032 /**
1033 * Sets a call video provider for the call.
1034 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001035 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1036 mCallVideoProvider = callVideoProvider;
1037 for (Listener l : mListeners) {
1038 l.onCallVideoProviderChanged(Call.this);
1039 }
1040 }
1041
1042 /**
1043 * @return Return the call video Provider binder.
1044 */
1045 public ICallVideoProvider getCallVideoProvider() {
1046 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001047 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001048
1049 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07001050 * The current video state for the call.
1051 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1052 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1053 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1054 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1055 *
1056 * @return True if video is enabled.
1057 */
1058 public int getVideoState() {
1059 return mVideoState;
1060 }
1061
1062 /**
1063 * At the start of the call, determines the desired video state for the call.
1064 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1065 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1066 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1067 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1068 *
1069 * @param videoState The desired video state for the call.
1070 */
1071 public void setVideoState(int videoState) {
1072 mVideoState = videoState;
1073 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001074
1075 public boolean getAudioModeIsVoip() {
1076 return mAudioModeIsVoip;
1077 }
1078
1079 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1080 mAudioModeIsVoip = audioModeIsVoip;
1081 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001082 l.onAudioModeIsVoipChanged(this);
1083 }
1084 }
1085
1086 public StatusHints getStatusHints() {
1087 return mStatusHints;
1088 }
1089
1090 public void setStatusHints(StatusHints statusHints) {
1091 mStatusHints = statusHints;
1092 for (Listener l : mListeners) {
1093 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001094 }
1095 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001096}