blob: 0eeb21e1b40233cd5de061b6b4b80c8e3524d6d9 [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 Nepal9d58de52014-07-18 14:53:19 -070019import android.app.PendingIntent;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070020import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070022import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070023import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070024import android.os.Handler;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070025import android.telecomm.CallPropertyPresentation;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080026import android.telecomm.CallState;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070027import android.telecomm.ConnectionRequest;
Yorke Lee33501632014-03-17 19:24:12 -070028import android.telecomm.GatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -070029import android.telecomm.PhoneAccount;
Ihab Awadff7493a2014-06-10 13:47:44 -070030import android.telecomm.Response;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070031import android.telecomm.StatusHints;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070032import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070033import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070034import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080035
Nancy Chena65d41f2014-06-24 12:06:03 -070036import com.android.internal.telecomm.ICallVideoProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070037import com.android.internal.telephony.CallerInfo;
38import com.android.internal.telephony.CallerInfoAsyncQuery;
39import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070040import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070041import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080042import com.google.common.base.Preconditions;
43
Ihab Awadff7493a2014-06-10 13:47:44 -070044import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070045import java.util.LinkedList;
46import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080047import java.util.Locale;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070048import java.util.Objects;
Ben Gilad61925612014-03-11 19:06:36 -070049import java.util.Set;
Santos Cordondfc66012014-07-15 13:41:40 -070050import java.util.concurrent.CopyOnWriteArraySet;
Ben Gilad0407fb22014-01-09 16:18:41 -080051
Ben Gilad2495d572014-01-09 17:26:19 -080052/**
53 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
54 * from the time the call intent was received by Telecomm (vs. the time the call was
55 * connected etc).
56 */
Sailesh Nepal664837f2014-07-14 16:31:51 -070057final class Call implements CreateConnectionResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070058 /**
59 * Listener for events on the call.
60 */
61 interface Listener {
62 void onSuccessfulOutgoingCall(Call call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -070063 void onFailedOutgoingCall(Call call, int errorCode, String errorMsg);
64 void onCancelledOutgoingCall(Call call);
Sailesh Nepalc92c4362014-07-04 18:33:21 -070065 void onSuccessfulIncomingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070066 void onFailedIncomingCall(Call call);
Ihab Awadcb387ac2014-05-28 16:49:38 -070067 void onRequestingRingback(Call call, boolean requestingRingback);
Evan Charlton352105c2014-06-03 14:10:54 -070068 void onPostDialWait(Call call, String remaining);
Sailesh Nepale20bc972014-07-09 21:22:36 -070069 void onCallCapabilitiesChanged(Call call);
Santos Cordona1610702014-06-04 20:22:56 -070070 void onExpiredConferenceCall(Call call);
71 void onConfirmedConferenceCall(Call call);
72 void onParentChanged(Call call);
73 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -070074 void onCannedSmsResponsesLoaded(Call call);
Nancy Chena65d41f2014-06-24 12:06:03 -070075 void onCallVideoProviderChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070076 void onCallerInfoChanged(Call call);
Sailesh Nepal7e669572014-07-08 21:29:12 -070077 void onAudioModeIsVoipChanged(Call call);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070078 void onStatusHintsChanged(Call call);
Sailesh Nepale8ecb982014-07-11 17:19:42 -070079 void onHandleChanged(Call call);
80 void onCallerDisplayNameChanged(Call call);
Andrew Lee4a796602014-07-11 17:23:03 -070081 void onVideoStateChanged(Call call);
Sailesh Nepal9d58de52014-07-18 14:53:19 -070082 void onStartActivityFromInCall(Call call, PendingIntent intent);
Ihab Awad69eb0f52014-07-18 11:20:37 -070083 void onPhoneAccountChanged(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) {}
Andrew Lee4a796602014-07-11 17:23:03 -0700125 @Override
126 public void onVideoStateChanged(Call call) {}
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700127 @Override
128 public void onStartActivityFromInCall(Call call, PendingIntent intent) {}
Ihab Awad69eb0f52014-07-18 11:20:37 -0700129 @Override
130 public void onPhoneAccountChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700131 }
132
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700133 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700134 new OnQueryCompleteListener() {
135 /** ${inheritDoc} */
136 @Override
137 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
138 if (cookie != null) {
139 ((Call) cookie).setCallerInfo(callerInfo, token);
140 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700141 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700142 };
143
144 private static final OnImageLoadCompleteListener sPhotoLoadListener =
145 new OnImageLoadCompleteListener() {
146 /** ${inheritDoc} */
147 @Override
148 public void onImageLoadComplete(
149 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
150 if (cookie != null) {
151 ((Call) cookie).setPhoto(photo, photoIcon, token);
152 }
153 }
154 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800155
Sailesh Nepal664837f2014-07-14 16:31:51 -0700156 private final Runnable mDirectToVoicemailRunnable = new Runnable() {
157 @Override
158 public void run() {
159 processDirectToVoicemail();
160 }
161 };
162
Sailesh Nepal810735e2014-03-18 18:15:46 -0700163 /** True if this is an incoming call. */
164 private final boolean mIsIncoming;
165
Ben Gilad0407fb22014-01-09 16:18:41 -0800166 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700167 * The time this call was created. Beyond logging and such, may also be used for bookkeeping
168 * and specifically for marking certain call attempts as failed attempts.
Ben Gilad0407fb22014-01-09 16:18:41 -0800169 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700170 private final long mCreationTimeMillis = System.currentTimeMillis();
171
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700172 /** The gateway information associated with this call. This stores the original call handle
173 * that the user is attempting to connect to via the gateway, the actual handle to dial in
174 * order to connect the call via the gateway, as well as the package name of the gateway
175 * service. */
176 private final GatewayInfo mGatewayInfo;
177
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700178 private PhoneAccount mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700179
Santos Cordon2174fb52014-05-29 08:22:56 -0700180 private final Handler mHandler = new Handler();
181
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700182 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800183
Santos Cordon61d0f702014-02-19 02:52:23 -0800184 /** The state of the call. */
185 private CallState mState;
186
187 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700188 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800189
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700190 /** The {@link CallPropertyPresentation} that controls how the handle is shown. */
191 private int mHandlePresentation;
192
193 /** The caller display name (CNAP) set by the connection service. */
194 private String mCallerDisplayName;
195
196 /** The {@link CallPropertyPresentation} that controls how the caller display name is shown. */
197 private int mCallerDisplayNamePresentation;
198
Ben Gilad0407fb22014-01-09 16:18:41 -0800199 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700200 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800201 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700202 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700203
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700204 private boolean mIsEmergencyCall;
205
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700206 private boolean mSpeakerphoneOn;
207
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700208 /**
209 * Tracks the video states which were applicable over the duration of a call.
210 * See {@link android.telecomm.VideoCallProfile} for a list of valid video states.
211 */
212 private int mVideoStateHistory;
213
Tyler Gunnc4abd912014-07-08 14:22:10 -0700214 private int mVideoState;
215
Ben Gilad61925612014-03-11 19:06:36 -0700216 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700217 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
218 * See {@link android.telephony.DisconnectCause}.
219 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700220 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700221
222 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700223 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700224 */
225 private String mDisconnectMessage;
226
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700227 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700228 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700229
Santos Cordon766d04f2014-05-06 10:28:25 -0700230 /** Set of listeners on this call. */
Santos Cordondfc66012014-07-15 13:41:40 -0700231 private Set<Listener> mListeners = new CopyOnWriteArraySet<>();
Santos Cordon766d04f2014-05-06 10:28:25 -0700232
Sailesh Nepal664837f2014-07-14 16:31:51 -0700233 private CreateConnectionProcessor mCreateConnectionProcessor;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700234
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700235 /** Caller information retrieved from the latest contact query. */
236 private CallerInfo mCallerInfo;
237
238 /** The latest token used with a contact info query. */
239 private int mQueryToken = 0;
240
Ihab Awadcb387ac2014-05-28 16:49:38 -0700241 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
242 private boolean mRequestingRingback = false;
243
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700244 /** Whether direct-to-voicemail query is pending. */
245 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700246
Sailesh Nepale20bc972014-07-09 21:22:36 -0700247 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700248
249 private boolean mIsConference = false;
250
251 private Call mParentCall = null;
252
253 private List<Call> mChildCalls = new LinkedList<>();
254
Ihab Awadff7493a2014-06-10 13:47:44 -0700255 /** Set of text message responses allowed for this call, if applicable. */
256 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
257
258 /** Whether an attempt has been made to load the text message responses. */
259 private boolean mCannedSmsResponsesLoadingStarted = false;
260
Nancy Chena65d41f2014-06-24 12:06:03 -0700261 private ICallVideoProvider mCallVideoProvider;
262
Sailesh Nepal7e669572014-07-08 21:29:12 -0700263 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700264 private StatusHints mStatusHints;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700265 private final ConnectionServiceRepository mRepository;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700266
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700267 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800268 * Persists the specified parameters and initializes the new instance.
269 *
270 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700271 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700272 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700273 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800274 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700275 Call(
276 ConnectionServiceRepository repository,
277 Uri handle,
278 GatewayInfo gatewayInfo,
279 PhoneAccount account,
280 boolean isIncoming,
281 boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700282 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700283 mRepository = repository;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700284 setHandle(handle, CallPropertyPresentation.ALLOWED);
Yorke Lee33501632014-03-17 19:24:12 -0700285 mGatewayInfo = gatewayInfo;
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700286 mPhoneAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700287 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700288 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700289 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800290 }
291
Santos Cordon766d04f2014-05-06 10:28:25 -0700292 void addListener(Listener listener) {
293 mListeners.add(listener);
294 }
295
296 void removeListener(Listener listener) {
297 mListeners.remove(listener);
298 }
299
Santos Cordon61d0f702014-02-19 02:52:23 -0800300 /** {@inheritDoc} */
301 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700302 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700303 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
304 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700305 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700306
307 return String.format(Locale.US, "[%s, %s, %s, %d]", mState, component,
308 Log.piiHandle(mHandle), getVideoState());
Santos Cordon61d0f702014-02-19 02:52:23 -0800309 }
310
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800311 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700312 if (mIsConference) {
313 if (!mChildCalls.isEmpty()) {
314 // If we have child calls, just return the child call.
315 return mChildCalls.get(0).getState();
316 }
317 return CallState.ACTIVE;
318 } else {
319 return mState;
320 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800321 }
322
323 /**
324 * Sets the call state. Although there exists the notion of appropriate state transitions
325 * (see {@link CallState}), in practice those expectations break down when cellular systems
326 * misbehave and they do this very often. The result is that we do not enforce state transitions
327 * and instead keep the code resilient to unexpected state changes.
328 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700329 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700330 Preconditions.checkState(newState != CallState.DISCONNECTED ||
331 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700332 if (mState != newState) {
333 Log.v(this, "setState %s -> %s", mState, newState);
334 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700335 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700336 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800337 }
338
Ihab Awadcb387ac2014-05-28 16:49:38 -0700339 void setRequestingRingback(boolean requestingRingback) {
340 mRequestingRingback = requestingRingback;
341 for (Listener l : mListeners) {
342 l.onRequestingRingback(this, mRequestingRingback);
343 }
344 }
345
346 boolean isRequestingRingback() {
347 return mRequestingRingback;
348 }
349
Sailesh Nepalce704b92014-03-17 18:31:43 -0700350 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800351 return mHandle;
352 }
353
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700354 int getHandlePresentation() {
355 return mHandlePresentation;
356 }
357
358 void setHandle(Uri handle, int presentation) {
359 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700360 mHandle = handle;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700361 mHandlePresentation = presentation;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700362 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700363 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700364 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700365 for (Listener l : mListeners) {
366 l.onHandleChanged(this);
367 }
368 }
369 }
370
371 String getCallerDisplayName() {
372 return mCallerDisplayName;
373 }
374
375 int getCallerDisplayNamePresentation() {
376 return mCallerDisplayNamePresentation;
377 }
378
379 void setCallerDisplayName(String callerDisplayName, int presentation) {
380 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
381 presentation != mCallerDisplayNamePresentation) {
382 mCallerDisplayName = callerDisplayName;
383 mCallerDisplayNamePresentation = presentation;
384 for (Listener l : mListeners) {
385 l.onCallerDisplayNameChanged(this);
386 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700387 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700388 }
389
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700390 String getName() {
391 return mCallerInfo == null ? null : mCallerInfo.name;
392 }
393
394 Bitmap getPhotoIcon() {
395 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
396 }
397
398 Drawable getPhoto() {
399 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
400 }
401
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700402 /**
403 * @param disconnectCause The reason for the disconnection, any of
404 * {@link android.telephony.DisconnectCause}.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700405 * @param disconnectMessage Optional message about the disconnect.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700406 */
407 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
408 // TODO: Consider combining this method with a setDisconnected() method that is totally
409 // separate from setState.
410 mDisconnectCause = disconnectCause;
411 mDisconnectMessage = disconnectMessage;
412 }
413
414 int getDisconnectCause() {
415 return mDisconnectCause;
416 }
417
418 String getDisconnectMessage() {
419 return mDisconnectMessage;
420 }
421
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700422 boolean isEmergencyCall() {
423 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800424 }
425
Yorke Lee33501632014-03-17 19:24:12 -0700426 /**
427 * @return The original handle this call is associated with. In-call services should use this
428 * handle when indicating in their UI the handle that is being called.
429 */
430 public Uri getOriginalHandle() {
431 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
432 return mGatewayInfo.getOriginalHandle();
433 }
434 return getHandle();
435 }
436
437 GatewayInfo getGatewayInfo() {
438 return mGatewayInfo;
439 }
440
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700441 PhoneAccount getPhoneAccount() {
442 return mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700443 }
444
Nancy Chen53ceedc2014-07-08 18:56:51 -0700445 void setPhoneAccount(PhoneAccount account) {
Ihab Awad69eb0f52014-07-18 11:20:37 -0700446 if (!Objects.equals(mPhoneAccount, account)) {
447 mPhoneAccount = account;
448 for (Listener l : mListeners) {
449 l.onPhoneAccountChanged(this);
450 }
451 }
Nancy Chen53ceedc2014-07-08 18:56:51 -0700452 }
453
Sailesh Nepal810735e2014-03-18 18:15:46 -0700454 boolean isIncoming() {
455 return mIsIncoming;
456 }
457
Ben Gilad0407fb22014-01-09 16:18:41 -0800458 /**
459 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700460 * period since this call was added to the set pending outgoing calls, see
461 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800462 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700463 long getAgeMillis() {
464 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800465 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800466
Yorke Leef98fb572014-03-05 10:56:55 -0800467 /**
468 * @return The time when this call object was created and added to the set of pending outgoing
469 * calls.
470 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700471 long getCreationTimeMillis() {
472 return mCreationTimeMillis;
473 }
474
475 long getConnectTimeMillis() {
476 return mConnectTimeMillis;
477 }
478
479 void setConnectTimeMillis(long connectTimeMillis) {
480 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800481 }
482
Sailesh Nepale20bc972014-07-09 21:22:36 -0700483 int getCallCapabilities() {
484 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700485 }
486
Sailesh Nepale20bc972014-07-09 21:22:36 -0700487 void setCallCapabilities(int callCapabilities) {
488 if (mCallCapabilities != callCapabilities) {
489 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700490 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700491 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700492 }
493 }
494 }
495
496 Call getParentCall() {
497 return mParentCall;
498 }
499
500 List<Call> getChildCalls() {
501 return mChildCalls;
502 }
503
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700504 ConnectionServiceWrapper getConnectionService() {
505 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800506 }
507
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700508 void setConnectionService(ConnectionServiceWrapper service) {
509 Preconditions.checkNotNull(service);
510
511 clearConnectionService();
512
513 service.incrementAssociatedCallCount();
514 mConnectionService = service;
515 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700516 }
517
518 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700519 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700520 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700521 void clearConnectionService() {
522 if (mConnectionService != null) {
523 ConnectionServiceWrapper serviceTemp = mConnectionService;
524 mConnectionService = null;
525 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700526
527 // Decrementing the count can cause the service to unbind, which itself can trigger the
528 // service-death code. Since the service death code tries to clean up any associated
529 // calls, we need to make sure to remove that information (e.g., removeCall()) before
530 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700531 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
532 // to do.
533 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700534 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800535 }
536
Sailesh Nepal664837f2014-07-14 16:31:51 -0700537 private void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700538 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700539 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
540 Log.i(this, "Directing call to voicemail: %s.", this);
541 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
542 // will not need to set RINGING state prior to calling reject.
543 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700544 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700545 } else {
546 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
547 // the call state to RINGING.
548
549 // TODO(santoscordon): Replace this with state transition to RINGING.
550 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700551 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700552 }
553 }
554
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700555 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700556 }
557 }
558
Santos Cordon766d04f2014-05-06 10:28:25 -0700559 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700560 * Starts the create connection sequence. Upon completion, there should exist an active
561 * connection through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700562 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700563 void startCreateConnection() {
564 Preconditions.checkState(mCreateConnectionProcessor == null);
565 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
566 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700567 }
568
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700569 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700570 public void handleCreateConnectionSuccessful(ConnectionRequest request) {
571 mCreateConnectionProcessor = null;
572 mPhoneAccount = request.getAccount();
573
574 if (mIsIncoming) {
575 // We do not handle incoming calls immediately when they are verified by the connection
576 // service. We allow the caller-info-query code to execute first so that we can read the
577 // direct-to-voicemail property before deciding if we want to show the incoming call to
578 // the user or if we want to reject the call.
579 mDirectToVoicemailQueryPending = true;
580
581 // Setting the handle triggers the caller info lookup code.
582 setHandle(request.getHandle(), request.getHandlePresentation());
583
584 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
585 // showing the user the incoming call screen.
586 mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
587 } else {
588 for (Listener l : mListeners) {
589 l.onSuccessfulOutgoingCall(this);
590 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700591 }
592 }
593
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700594 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700595 public void handleCreateConnectionFailed(int code, String msg) {
596 mCreateConnectionProcessor = null;
597 if (mIsIncoming) {
598 clearConnectionService();
599 setDisconnectCause(code, null);
600 setState(CallState.DISCONNECTED);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700601
Sailesh Nepal664837f2014-07-14 16:31:51 -0700602 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
603 for (int i = 0; i < listeners.length; i++) {
604 listeners[i].onFailedIncomingCall(this);
605 }
606 } else {
607 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
608 for (int i = 0; i < listeners.length; i++) {
609 listeners[i].onFailedOutgoingCall(this, code, msg);
610 }
611 clearConnectionService();
612 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700613 }
614
615 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700616 public void handleCreateConnectionCancelled() {
617 mCreateConnectionProcessor = null;
618 if (mIsIncoming) {
619 clearConnectionService();
620 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
621 setState(CallState.DISCONNECTED);
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700622
Sailesh Nepal664837f2014-07-14 16:31:51 -0700623 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
624 for (int i = 0; i < listeners.length; i++) {
625 listeners[i].onFailedIncomingCall(this);
626 }
627 } else {
628 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
629 for (int i = 0; i < listeners.length; i++) {
630 listeners[i].onCancelledOutgoingCall(this);
631 }
632 clearConnectionService();
633 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700634 }
635
636 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700637 * Plays the specified DTMF tone.
638 */
639 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700640 if (mConnectionService == null) {
641 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700642 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700643 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
644 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700645 }
646 }
647
648 /**
649 * Stops playing any currently playing DTMF tone.
650 */
651 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700652 if (mConnectionService == null) {
653 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700654 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700655 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
656 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700657 }
658 }
659
660 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700661 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800662 */
663 void disconnect() {
Nancy Chen53ceedc2014-07-08 18:56:51 -0700664 if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700665 Log.v(this, "Aborting call %s", this);
666 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700667 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700668 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700669
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700670 Log.i(this, "Send disconnect to connection service for call: %s", this);
671 // The call isn't officially disconnected until the connection service confirms that the
672 // call was actually disconnected. Only then is the association between call and
673 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
674 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800675 }
676 }
677
Santos Cordon682fe6b2014-05-20 08:56:39 -0700678 void abort() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700679 if (mCreateConnectionProcessor != null) {
680 mCreateConnectionProcessor.abort();
Nancy Chen53ceedc2014-07-08 18:56:51 -0700681 } else if (mState == CallState.PRE_DIAL_WAIT) {
682 handleCreateConnectionFailed(DisconnectCause.LOCAL, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700683 }
684 }
685
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800686 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800687 * Answers the call if it is ringing.
Andrew Lee38931d02014-07-16 10:17:36 -0700688 *
689 * @param videoState The video state in which to answer the call.
Santos Cordon61d0f702014-02-19 02:52:23 -0800690 */
Andrew Lee38931d02014-07-16 10:17:36 -0700691 void answer(int videoState) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700692 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800693
694 // Check to verify that the call is still in the ringing state. A call can change states
695 // between the time the user hits 'answer' and Telecomm receives the command.
696 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700697 // At this point, we are asking the connection service to answer but we don't assume
698 // that it will work. Instead, we wait until confirmation from the connectino service
699 // that the call is in a non-RINGING state before changing the UI. See
700 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
Andrew Lee38931d02014-07-16 10:17:36 -0700701 mConnectionService.answer(this, videoState);
Santos Cordon61d0f702014-02-19 02:52:23 -0800702 }
703 }
704
705 /**
706 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700707 *
708 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
709 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800710 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700711 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700712 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800713
714 // Check to verify that the call is still in the ringing state. A call can change states
715 // between the time the user hits 'reject' and Telecomm receives the command.
716 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700717 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800718 }
719 }
720
721 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700722 * Puts the call on hold if it is currently active.
723 */
724 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700725 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700726
727 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700728 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700729 }
730 }
731
732 /**
733 * Releases the call from hold if it is currently active.
734 */
735 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700736 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700737
738 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700739 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700740 }
741 }
742
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700743 /** Checks if this is a live call or not. */
744 boolean isAlive() {
745 switch (mState) {
746 case NEW:
747 case RINGING:
748 case DISCONNECTED:
749 case ABORTED:
750 return false;
751 default:
752 return true;
753 }
754 }
755
Santos Cordon40f78c22014-04-07 02:11:42 -0700756 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -0700757 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -0700758 }
759
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700760 Bundle getExtras() {
761 return mExtras;
762 }
763
764 void setExtras(Bundle extras) {
765 mExtras = extras;
766 }
767
Santos Cordon5ba7f272014-05-28 13:59:49 -0700768 Uri getRingtone() {
769 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
770 }
771
Evan Charlton352105c2014-06-03 14:10:54 -0700772 void onPostDialWait(String remaining) {
773 for (Listener l : mListeners) {
774 l.onPostDialWait(this, remaining);
775 }
776 }
777
778 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700779 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700780 }
781
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700782 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700783 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700784 }
785
Santos Cordona1610702014-06-04 20:22:56 -0700786 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700787 if (mConnectionService == null) {
788 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700789 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700790 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700791 }
792 }
793
794 void expireConference() {
795 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700796 // connection service...so start shutting down.
797 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700798 for (Listener l : mListeners) {
799 l.onExpiredConferenceCall(this);
800 }
801 }
802
803 void confirmConference() {
804 Log.v(this, "confirming Conf call %s", mListeners);
805 for (Listener l : mListeners) {
806 l.onConfirmedConferenceCall(this);
807 }
808 }
809
810 void splitFromConference() {
811 // TODO(santoscordon): todo
812 }
813
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700814 void swapWithBackgroundCall() {
815 mConnectionService.swapWithBackgroundCall(this);
816 }
817
Santos Cordona1610702014-06-04 20:22:56 -0700818 void setParentCall(Call parentCall) {
819 if (parentCall == this) {
820 Log.e(this, new Exception(), "setting the parent to self");
821 return;
822 }
823 Preconditions.checkState(parentCall == null || mParentCall == null);
824
825 Call oldParent = mParentCall;
826 if (mParentCall != null) {
827 mParentCall.removeChildCall(this);
828 }
829 mParentCall = parentCall;
830 if (mParentCall != null) {
831 mParentCall.addChildCall(this);
832 }
833
834 for (Listener l : mListeners) {
835 l.onParentChanged(this);
836 }
837 }
838
839 private void addChildCall(Call call) {
840 if (!mChildCalls.contains(call)) {
841 mChildCalls.add(call);
842
843 for (Listener l : mListeners) {
844 l.onChildrenChanged(this);
845 }
846 }
847 }
848
849 private void removeChildCall(Call call) {
850 if (mChildCalls.remove(call)) {
851 for (Listener l : mListeners) {
852 l.onChildrenChanged(this);
853 }
854 }
855 }
856
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800857 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700858 * Return whether the user can respond to this {@code Call} via an SMS message.
859 *
860 * @return true if the "Respond via SMS" feature should be enabled
861 * for this incoming call.
862 *
863 * The general rule is that we *do* allow "Respond via SMS" except for
864 * the few (relatively rare) cases where we know for sure it won't
865 * work, namely:
866 * - a bogus or blank incoming number
867 * - a call from a SIP address
868 * - a "call presentation" that doesn't allow the number to be revealed
869 *
870 * In all other cases, we allow the user to respond via SMS.
871 *
872 * Note that this behavior isn't perfect; for example we have no way
873 * to detect whether the incoming call is from a landline (with most
874 * networks at least), so we still enable this feature even though
875 * SMSes to that number will silently fail.
876 */
877 boolean isRespondViaSmsCapable() {
878 if (mState != CallState.RINGING) {
879 return false;
880 }
881
882 if (getHandle() == null) {
883 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
884 // other words, the user should not be able to see the incoming phone number.
885 return false;
886 }
887
888 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
889 // The incoming number is actually a URI (i.e. a SIP address),
890 // not a regular PSTN phone number, and we can't send SMSes to
891 // SIP addresses.
892 // (TODO: That might still be possible eventually, though. Is
893 // there some SIP-specific equivalent to sending a text message?)
894 return false;
895 }
896
897 // Is there a valid SMS application on the phone?
898 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
899 true /*updateIfNeeded*/) == null) {
900 return false;
901 }
902
903 // TODO: with some carriers (in certain countries) you *can* actually
904 // tell whether a given number is a mobile phone or not. So in that
905 // case we could potentially return false here if the incoming call is
906 // from a land line.
907
908 // If none of the above special cases apply, it's OK to enable the
909 // "Respond via SMS" feature.
910 return true;
911 }
912
913 List<String> getCannedSmsResponses() {
914 return mCannedSmsResponses;
915 }
916
917 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800918 * @return True if the call is ringing, else logs the action name.
919 */
920 private boolean isRinging(String actionName) {
921 if (mState == CallState.RINGING) {
922 return true;
923 }
924
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800925 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800926 return false;
927 }
928
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800929 @SuppressWarnings("rawtypes")
930 private void decrementAssociatedCallCount(ServiceBinder binder) {
931 if (binder != null) {
932 binder.decrementAssociatedCallCount();
933 }
934 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700935
936 /**
937 * Looks up contact information based on the current handle.
938 */
939 private void startCallerInfoLookup() {
940 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
941
942 mQueryToken++; // Updated so that previous queries can no longer set the information.
943 mCallerInfo = null;
944 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700945 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700946 CallerInfoAsyncQuery.startQuery(
947 mQueryToken,
948 TelecommApp.getInstance(),
949 number,
950 sCallerInfoQueryListener,
951 this);
952 }
953 }
954
955 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700956 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700957 * that was made.
958 *
959 * @param callerInfo The new caller information to set.
960 * @param token The token used with this query.
961 */
962 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700963 Preconditions.checkNotNull(callerInfo);
964
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700965 if (mQueryToken == token) {
966 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700967 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700968
Makoto Onukia1662d02014-07-10 15:31:59 -0700969 if (mCallerInfo.contactDisplayPhotoUri != null) {
970 Log.d(this, "Searching person uri %s for call %s",
971 mCallerInfo.contactDisplayPhotoUri, this);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700972 ContactsAsyncHelper.startObtainPhotoAsync(
973 token,
974 TelecommApp.getInstance(),
Makoto Onukia1662d02014-07-10 15:31:59 -0700975 mCallerInfo.contactDisplayPhotoUri,
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700976 sPhotoLoadListener,
977 this);
Makoto Onukia1662d02014-07-10 15:31:59 -0700978 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
Santos Cordon64c7e962014-07-02 15:15:27 -0700979 } else {
980 for (Listener l : mListeners) {
981 l.onCallerInfoChanged(this);
982 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700983 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700984
985 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700986 }
987 }
988
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700989 CallerInfo getCallerInfo() {
990 return mCallerInfo;
991 }
992
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700993 /**
994 * Saves the specified photo information if the specified token matches that of the last query.
995 *
996 * @param photo The photo as a drawable.
997 * @param photoIcon The photo as a small icon.
998 * @param token The token used with this query.
999 */
1000 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1001 if (mQueryToken == token) {
1002 mCallerInfo.cachedPhoto = photo;
1003 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -07001004
1005 for (Listener l : mListeners) {
1006 l.onCallerInfoChanged(this);
1007 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001008 }
1009 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001010
1011 private void maybeLoadCannedSmsResponses() {
1012 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1013 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1014 mCannedSmsResponsesLoadingStarted = true;
1015 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1016 new Response<Void, List<String>>() {
1017 @Override
1018 public void onResult(Void request, List<String>... result) {
1019 if (result.length > 0) {
1020 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1021 mCannedSmsResponses = result[0];
1022 for (Listener l : mListeners) {
1023 l.onCannedSmsResponsesLoaded(Call.this);
1024 }
1025 }
1026 }
1027
1028 @Override
1029 public void onError(Void request, int code, String msg) {
1030 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1031 msg);
1032 }
1033 }
1034 );
1035 } else {
1036 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1037 }
1038 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001039
1040 /**
1041 * Sets speakerphone option on when call begins.
1042 */
1043 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1044 mSpeakerphoneOn = startWithSpeakerphone;
1045 }
1046
1047 /**
1048 * Returns speakerphone option.
1049 *
1050 * @return Whether or not speakerphone should be set automatically when call begins.
1051 */
1052 public boolean getStartWithSpeakerphoneOn() {
1053 return mSpeakerphoneOn;
1054 }
Andrew Leee9a77652014-06-26 13:07:57 -07001055
1056 /**
1057 * Sets a call video provider for the call.
1058 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001059 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1060 mCallVideoProvider = callVideoProvider;
1061 for (Listener l : mListeners) {
1062 l.onCallVideoProviderChanged(Call.this);
1063 }
1064 }
1065
1066 /**
1067 * @return Return the call video Provider binder.
1068 */
1069 public ICallVideoProvider getCallVideoProvider() {
1070 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001071 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001072
1073 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07001074 * The current video state for the call.
1075 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1076 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1077 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1078 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1079 *
1080 * @return True if video is enabled.
1081 */
1082 public int getVideoState() {
1083 return mVideoState;
1084 }
1085
1086 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001087 * Returns the video states which were applicable over the duration of a call.
1088 * See {@link android.telecomm.VideoCallProfile} for a list of valid video states.
1089 *
1090 * @return The video states applicable over the duration of the call.
1091 */
1092 public int getVideoStateHistory() {
1093 return mVideoStateHistory;
1094 }
1095
1096 /**
1097 * Determines the current video state for the call.
1098 * For an outgoing call determines the desired video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001099 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1100 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1101 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1102 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1103 *
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001104 * @param videoState The video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001105 */
1106 public void setVideoState(int videoState) {
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001107 // Track which video states were applicable over the duration of the call.
1108 mVideoStateHistory = mVideoStateHistory | videoState;
1109
Tyler Gunnc4abd912014-07-08 14:22:10 -07001110 mVideoState = videoState;
Andrew Lee4a796602014-07-11 17:23:03 -07001111 for (Listener l : mListeners) {
1112 l.onVideoStateChanged(this);
1113 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001114 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001115
1116 public boolean getAudioModeIsVoip() {
1117 return mAudioModeIsVoip;
1118 }
1119
1120 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1121 mAudioModeIsVoip = audioModeIsVoip;
1122 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001123 l.onAudioModeIsVoipChanged(this);
1124 }
1125 }
1126
1127 public StatusHints getStatusHints() {
1128 return mStatusHints;
1129 }
1130
1131 public void setStatusHints(StatusHints statusHints) {
1132 mStatusHints = statusHints;
1133 for (Listener l : mListeners) {
1134 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001135 }
1136 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -07001137
1138 public void startActivityFromInCall(PendingIntent intent) {
1139 if (intent.isActivity()) {
1140 for (Listener l : mListeners) {
1141 l.onStartActivityFromInCall(this, intent);
1142 }
1143 } else {
1144 Log.w(this, "startActivityFromInCall, activity intent required");
1145 }
1146 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001147}