blob: 99e4b07274424cbbd19d9ad3945e09f8452ce1c7 [file] [log] [blame]
Ben Gilad0407fb22014-01-09 16:18:41 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Santos Cordon99c8a6f2014-05-28 18:28:47 -070019import android.content.ContentUris;
20import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070022import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070023import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070024import android.os.Handler;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070025import android.provider.ContactsContract.Contacts;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070026import android.telecomm.CallServiceDescriptor;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080027import android.telecomm.CallState;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070028import android.telecomm.ConnectionRequest;
Yorke Lee33501632014-03-17 19:24:12 -070029import android.telecomm.GatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -070030import android.telecomm.PhoneAccount;
Ihab Awadff7493a2014-06-10 13:47:44 -070031import android.telecomm.Response;
Santos Cordon766d04f2014-05-06 10:28:25 -070032import android.telecomm.TelecommConstants;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070033import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070034import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070035import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080036
Nancy Chena65d41f2014-06-24 12:06:03 -070037import com.android.internal.telecomm.ICallVideoProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070038import com.android.internal.telephony.CallerInfo;
39import com.android.internal.telephony.CallerInfoAsyncQuery;
40import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070041import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070042import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080043import com.google.common.base.Preconditions;
Ihab Awadff7493a2014-06-10 13:47:44 -070044import com.google.common.collect.Sets;
Santos Cordon61d0f702014-02-19 02:52:23 -080045
Ihab Awadff7493a2014-06-10 13:47:44 -070046import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070047import java.util.LinkedList;
48import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080049import java.util.Locale;
Ben Gilad61925612014-03-11 19:06:36 -070050import java.util.Set;
Ben Gilad0407fb22014-01-09 16:18:41 -080051
Ben Gilad2495d572014-01-09 17:26:19 -080052/**
53 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
54 * from the time the call intent was received by Telecomm (vs. the time the call was
55 * connected etc).
56 */
Sailesh Nepal5a73b032014-06-25 15:53:21 -070057final class Call implements OutgoingCallResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070058 /**
59 * 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);
Santos Cordona1610702014-06-04 20:22:56 -070069 void onIsConferenceCapableChanged(Call call, boolean isConferenceCapable);
70 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);
Tyler Gunne19cc002014-07-01 11:32:53 -070076 void onFeaturesChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070077 void onCallerInfoChanged(Call call);
Sailesh Nepal7e669572014-07-08 21:29:12 -070078 void onAudioModeIsVoipChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070079 }
80
81 abstract static class ListenerBase implements Listener {
82 @Override
83 public void onSuccessfulOutgoingCall(Call call) {}
84 @Override
85 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
86 @Override
87 public void onCancelledOutgoingCall(Call call) {}
88 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -070089 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -070090 @Override
91 public void onFailedIncomingCall(Call call) {}
92 @Override
93 public void onRequestingRingback(Call call, boolean requestingRingback) {}
94 @Override
95 public void onPostDialWait(Call call, String remaining) {}
96 @Override
97 public void onIsConferenceCapableChanged(Call call, boolean isConferenceCapable) {}
98 @Override
99 public void onExpiredConferenceCall(Call call) {}
100 @Override
101 public void onConfirmedConferenceCall(Call call) {}
102 @Override
103 public void onParentChanged(Call call) {}
104 @Override
105 public void onChildrenChanged(Call call) {}
106 @Override
107 public void onCannedSmsResponsesLoaded(Call call) {}
108 @Override
109 public void onCallVideoProviderChanged(Call call) {}
110 @Override
111 public void onFeaturesChanged(Call call) {}
112 @Override
113 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700114 @Override
115 public void onAudioModeIsVoipChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700116 }
117
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700118 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700119 new OnQueryCompleteListener() {
120 /** ${inheritDoc} */
121 @Override
122 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
123 if (cookie != null) {
124 ((Call) cookie).setCallerInfo(callerInfo, token);
125 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700126 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700127 };
128
129 private static final OnImageLoadCompleteListener sPhotoLoadListener =
130 new OnImageLoadCompleteListener() {
131 /** ${inheritDoc} */
132 @Override
133 public void onImageLoadComplete(
134 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
135 if (cookie != null) {
136 ((Call) cookie).setPhoto(photo, photoIcon, token);
137 }
138 }
139 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800140
Sailesh Nepal810735e2014-03-18 18:15:46 -0700141 /** True if this is an incoming call. */
142 private final boolean mIsIncoming;
143
Ben Gilad0407fb22014-01-09 16:18:41 -0800144 /**
145 * The time this call was created, typically also the time this call was added to the set
146 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
147 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
148 * certain call attempts as failed attempts.
149 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700150 private final long mCreationTimeMillis = System.currentTimeMillis();
151
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700152 /** The gateway information associated with this call. This stores the original call handle
153 * that the user is attempting to connect to via the gateway, the actual handle to dial in
154 * order to connect the call via the gateway, as well as the package name of the gateway
155 * service. */
156 private final GatewayInfo mGatewayInfo;
157
Ihab Awad98a55602014-06-30 21:27:28 -0700158 private final PhoneAccount mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700159
Santos Cordon2174fb52014-05-29 08:22:56 -0700160 private final Handler mHandler = new Handler();
161
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700162 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800163
Santos Cordon61d0f702014-02-19 02:52:23 -0800164 /** The state of the call. */
165 private CallState mState;
166
167 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700168 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800169
Ben Gilad0407fb22014-01-09 16:18:41 -0800170 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700171 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800172 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700173 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700174
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700175 private boolean mIsEmergencyCall;
176
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700177 private boolean mSpeakerphoneOn;
178
Tyler Gunnc4abd912014-07-08 14:22:10 -0700179 private int mVideoState;
180
Ben Gilad61925612014-03-11 19:06:36 -0700181 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700182 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
183 * See {@link android.telephony.DisconnectCause}.
184 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700185 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700186
187 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700188 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700189 */
190 private String mDisconnectMessage;
191
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700192 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700193 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700194
Santos Cordon766d04f2014-05-06 10:28:25 -0700195 /** Set of listeners on this call. */
196 private Set<Listener> mListeners = Sets.newHashSet();
197
Santos Cordon682fe6b2014-05-20 08:56:39 -0700198 private OutgoingCallProcessor mOutgoingCallProcessor;
199
200 // TODO(santoscordon): The repositories should be changed into singleton types.
201 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700202
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700203 /** Caller information retrieved from the latest contact query. */
204 private CallerInfo mCallerInfo;
205
206 /** The latest token used with a contact info query. */
207 private int mQueryToken = 0;
208
Ihab Awadcb387ac2014-05-28 16:49:38 -0700209 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
210 private boolean mRequestingRingback = false;
211
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700212 /** Whether direct-to-voicemail query is pending. */
213 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700214
Santos Cordona1610702014-06-04 20:22:56 -0700215 private boolean mIsConferenceCapable = false;
216
217 private boolean mIsConference = false;
218
219 private Call mParentCall = null;
220
221 private List<Call> mChildCalls = new LinkedList<>();
222
Ihab Awadff7493a2014-06-10 13:47:44 -0700223 /** Set of text message responses allowed for this call, if applicable. */
224 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
225
226 /** Whether an attempt has been made to load the text message responses. */
227 private boolean mCannedSmsResponsesLoadingStarted = false;
228
Nancy Chena65d41f2014-06-24 12:06:03 -0700229 private ICallVideoProvider mCallVideoProvider;
230
Tyler Gunne19cc002014-07-01 11:32:53 -0700231 /** Features associated with the call which the InCall UI may wish to show icons for. */
232 private int mFeatures;
233
Sailesh Nepal7e669572014-07-08 21:29:12 -0700234 private boolean mAudioModeIsVoip;
235
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700236 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700237 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700238 *
239 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800240 */
Santos Cordona1610702014-06-04 20:22:56 -0700241 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700242 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800243 }
244
245 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800246 * Persists the specified parameters and initializes the new instance.
247 *
248 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700249 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700250 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700251 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800252 */
Ihab Awad98a55602014-06-30 21:27:28 -0700253 Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700254 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700255 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700256 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700257 mGatewayInfo = gatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -0700258 mAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700259 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700260 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700261 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800262 }
263
Santos Cordon766d04f2014-05-06 10:28:25 -0700264 void addListener(Listener listener) {
265 mListeners.add(listener);
266 }
267
268 void removeListener(Listener listener) {
269 mListeners.remove(listener);
270 }
271
Santos Cordon61d0f702014-02-19 02:52:23 -0800272 /** {@inheritDoc} */
273 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700274 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700275 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
276 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700277 }
278 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800279 }
280
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800281 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700282 if (mIsConference) {
283 if (!mChildCalls.isEmpty()) {
284 // If we have child calls, just return the child call.
285 return mChildCalls.get(0).getState();
286 }
287 return CallState.ACTIVE;
288 } else {
289 return mState;
290 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800291 }
292
293 /**
294 * Sets the call state. Although there exists the notion of appropriate state transitions
295 * (see {@link CallState}), in practice those expectations break down when cellular systems
296 * misbehave and they do this very often. The result is that we do not enforce state transitions
297 * and instead keep the code resilient to unexpected state changes.
298 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700299 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700300 Preconditions.checkState(newState != CallState.DISCONNECTED ||
301 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700302 if (mState != newState) {
303 Log.v(this, "setState %s -> %s", mState, newState);
304 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700305 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700306 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800307 }
308
Ihab Awadcb387ac2014-05-28 16:49:38 -0700309 void setRequestingRingback(boolean requestingRingback) {
310 mRequestingRingback = requestingRingback;
311 for (Listener l : mListeners) {
312 l.onRequestingRingback(this, mRequestingRingback);
313 }
314 }
315
316 boolean isRequestingRingback() {
317 return mRequestingRingback;
318 }
319
Sailesh Nepalce704b92014-03-17 18:31:43 -0700320 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800321 return mHandle;
322 }
323
Sailesh Nepalce704b92014-03-17 18:31:43 -0700324 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700325 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
326 mHandle = handle;
327 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700328 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700329 startCallerInfoLookup();
330 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700331 }
332
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700333 String getName() {
334 return mCallerInfo == null ? null : mCallerInfo.name;
335 }
336
337 Bitmap getPhotoIcon() {
338 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
339 }
340
341 Drawable getPhoto() {
342 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
343 }
344
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700345 /**
346 * @param disconnectCause The reason for the disconnection, any of
347 * {@link android.telephony.DisconnectCause}.
348 * @param disconnectMessage Optional call-service-provided message about the disconnect.
349 */
350 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
351 // TODO: Consider combining this method with a setDisconnected() method that is totally
352 // separate from setState.
353 mDisconnectCause = disconnectCause;
354 mDisconnectMessage = disconnectMessage;
355 }
356
357 int getDisconnectCause() {
358 return mDisconnectCause;
359 }
360
361 String getDisconnectMessage() {
362 return mDisconnectMessage;
363 }
364
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700365 boolean isEmergencyCall() {
366 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800367 }
368
Yorke Lee33501632014-03-17 19:24:12 -0700369 /**
370 * @return The original handle this call is associated with. In-call services should use this
371 * handle when indicating in their UI the handle that is being called.
372 */
373 public Uri getOriginalHandle() {
374 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
375 return mGatewayInfo.getOriginalHandle();
376 }
377 return getHandle();
378 }
379
380 GatewayInfo getGatewayInfo() {
381 return mGatewayInfo;
382 }
383
Ihab Awad98a55602014-06-30 21:27:28 -0700384 PhoneAccount getAccount() {
385 return mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700386 }
387
Sailesh Nepal810735e2014-03-18 18:15:46 -0700388 boolean isIncoming() {
389 return mIsIncoming;
390 }
391
Ben Gilad0407fb22014-01-09 16:18:41 -0800392 /**
393 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700394 * period since this call was added to the set pending outgoing calls, see
395 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800396 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700397 long getAgeMillis() {
398 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800399 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800400
Yorke Leef98fb572014-03-05 10:56:55 -0800401 /**
402 * @return The time when this call object was created and added to the set of pending outgoing
403 * calls.
404 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700405 long getCreationTimeMillis() {
406 return mCreationTimeMillis;
407 }
408
409 long getConnectTimeMillis() {
410 return mConnectTimeMillis;
411 }
412
413 void setConnectTimeMillis(long connectTimeMillis) {
414 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800415 }
416
Santos Cordona1610702014-06-04 20:22:56 -0700417 boolean isConferenceCapable() {
418 return mIsConferenceCapable;
419 }
420
421 void setIsConferenceCapable(boolean isConferenceCapable) {
422 if (mIsConferenceCapable != isConferenceCapable) {
423 mIsConferenceCapable = isConferenceCapable;
424 for (Listener l : mListeners) {
425 l.onIsConferenceCapableChanged(this, mIsConferenceCapable);
426 }
427 }
428 }
429
430 Call getParentCall() {
431 return mParentCall;
432 }
433
434 List<Call> getChildCalls() {
435 return mChildCalls;
436 }
437
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700438 ConnectionServiceWrapper getConnectionService() {
439 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800440 }
441
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700442 void setConnectionService(ConnectionServiceWrapper service) {
443 Preconditions.checkNotNull(service);
444
445 clearConnectionService();
446
447 service.incrementAssociatedCallCount();
448 mConnectionService = service;
449 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700450 }
451
452 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700453 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700454 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700455 void clearConnectionService() {
456 if (mConnectionService != null) {
457 ConnectionServiceWrapper serviceTemp = mConnectionService;
458 mConnectionService = null;
459 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700460
461 // Decrementing the count can cause the service to unbind, which itself can trigger the
462 // service-death code. Since the service death code tries to clean up any associated
463 // calls, we need to make sure to remove that information (e.g., removeCall()) before
464 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700465 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
466 // to do.
467 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700468 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800469 }
470
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800471 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700472 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
473 * invoke handle[Un]SuccessfulIncomingCall.
474 *
475 * @param descriptor The relevant call-service descriptor.
476 * @param extras The optional extras passed via
477 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
478 */
479 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
480 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
481 }
482
Santos Cordon2174fb52014-05-29 08:22:56 -0700483 /**
484 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
485 * from the contacts provider. The call is not yet exposed to the user at this point and
486 * the result of the query will determine if the call is rejected or passed through to the
487 * in-call UI.
488 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700489 void handleVerifiedIncoming(ConnectionRequest request) {
490 // We do not handle incoming calls immediately when they are verified by the connection
491 // service. We allow the caller-info-query code to execute first so that we can read the
Santos Cordon2174fb52014-05-29 08:22:56 -0700492 // direct-to-voicemail property before deciding if we want to show the incoming call to the
493 // user or if we want to reject the call.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700494 mDirectToVoicemailQueryPending = true;
Santos Cordon2174fb52014-05-29 08:22:56 -0700495
496 // Setting the handle triggers the caller info lookup code.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700497 setHandle(request.getHandle());
Santos Cordon766d04f2014-05-06 10:28:25 -0700498
Santos Cordon2174fb52014-05-29 08:22:56 -0700499 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
500 // showing the user the incoming call screen.
501 mHandler.postDelayed(new Runnable() {
502 @Override
503 public void run() {
504 processDirectToVoicemail();
505 }
Santos Cordona1610702014-06-04 20:22:56 -0700506 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700507 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700508
Santos Cordon2174fb52014-05-29 08:22:56 -0700509 void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700510 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700511 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
512 Log.i(this, "Directing call to voicemail: %s.", this);
513 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
514 // will not need to set RINGING state prior to calling reject.
515 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700516 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700517 } else {
518 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
519 // the call state to RINGING.
520
521 // TODO(santoscordon): Replace this with state transition to RINGING.
522 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700523 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700524 }
525 }
526
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700527 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700528 }
529 }
530
531 void handleFailedIncoming() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700532 clearConnectionService();
Santos Cordon766d04f2014-05-06 10:28:25 -0700533
534 // TODO: Needs more specific disconnect error for this case.
535 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
536 setState(CallState.DISCONNECTED);
537
538 // TODO(santoscordon): Replace this with state transitions related to "connecting".
539 for (Listener l : mListeners) {
540 l.onFailedIncomingCall(this);
541 }
542 }
543
544 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700545 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700546 * through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700547 */
548 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700549 Preconditions.checkState(mOutgoingCallProcessor == null);
550
551 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700552 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700553 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700554 }
555
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700556 @Override
557 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700558 // TODO(santoscordon): Replace this with state transitions related to "connecting".
559 for (Listener l : mListeners) {
560 l.onSuccessfulOutgoingCall(this);
561 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700562 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700563 }
564
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700565 @Override
566 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700567 // TODO(santoscordon): Replace this with state transitions related to "connecting".
568 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700569 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700570 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700571
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700572 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700573 mOutgoingCallProcessor = null;
574 }
575
576 @Override
577 public void onOutgoingCallCancel() {
578 // TODO(santoscordon): Replace this with state transitions related to "connecting".
579 for (Listener l : mListeners) {
580 l.onCancelledOutgoingCall(this);
581 }
582
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700583 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700584 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700585 }
586
587 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700588 * Plays the specified DTMF tone.
589 */
590 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700591 if (mConnectionService == null) {
592 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700593 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700594 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
595 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700596 }
597 }
598
599 /**
600 * Stops playing any currently playing DTMF tone.
601 */
602 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700603 if (mConnectionService == null) {
604 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700605 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700606 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
607 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700608 }
609 }
610
611 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700612 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800613 */
614 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700615 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700616 Log.v(this, "Aborting call %s", this);
617 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700618 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700619 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700620
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700621 Log.i(this, "Send disconnect to connection service for call: %s", this);
622 // The call isn't officially disconnected until the connection service confirms that the
623 // call was actually disconnected. Only then is the association between call and
624 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
625 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800626 }
627 }
628
Santos Cordon682fe6b2014-05-20 08:56:39 -0700629 void abort() {
630 if (mOutgoingCallProcessor != null) {
631 mOutgoingCallProcessor.abort();
632 }
633 }
634
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800635 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800636 * Answers the call if it is ringing.
637 */
638 void answer() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700639 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800640
641 // Check to verify that the call is still in the ringing state. A call can change states
642 // between the time the user hits 'answer' and Telecomm receives the command.
643 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700644 // At this point, we are asking the connection service to answer but we don't assume
645 // that it will work. Instead, we wait until confirmation from the connectino service
646 // that the call is in a non-RINGING state before changing the UI. See
647 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
648 mConnectionService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800649 }
650 }
651
652 /**
653 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700654 *
655 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
656 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800657 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700658 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700659 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800660
661 // Check to verify that the call is still in the ringing state. A call can change states
662 // between the time the user hits 'reject' and Telecomm receives the command.
663 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700664 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800665 }
666 }
667
668 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700669 * Puts the call on hold if it is currently active.
670 */
671 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700672 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700673
674 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700675 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700676 }
677 }
678
679 /**
680 * Releases the call from hold if it is currently active.
681 */
682 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700683 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700684
685 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700686 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700687 }
688 }
689
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700690 /** Checks if this is a live call or not. */
691 boolean isAlive() {
692 switch (mState) {
693 case NEW:
694 case RINGING:
695 case DISCONNECTED:
696 case ABORTED:
697 return false;
698 default:
699 return true;
700 }
701 }
702
Santos Cordon40f78c22014-04-07 02:11:42 -0700703 boolean isActive() {
704 switch (mState) {
705 case ACTIVE:
706 case POST_DIAL:
707 case POST_DIAL_WAIT:
708 return true;
709 default:
710 return false;
711 }
712 }
713
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700714 Bundle getExtras() {
715 return mExtras;
716 }
717
718 void setExtras(Bundle extras) {
719 mExtras = extras;
720 }
721
Santos Cordon5ba7f272014-05-28 13:59:49 -0700722 Uri getRingtone() {
723 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
724 }
725
Evan Charlton352105c2014-06-03 14:10:54 -0700726 void onPostDialWait(String remaining) {
727 for (Listener l : mListeners) {
728 l.onPostDialWait(this, remaining);
729 }
730 }
731
732 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700733 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700734 }
735
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700736 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700737 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700738 }
739
Santos Cordona1610702014-06-04 20:22:56 -0700740 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700741 if (mConnectionService == null) {
742 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700743 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700744 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700745 }
746 }
747
748 void expireConference() {
749 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700750 // connection service...so start shutting down.
751 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700752 for (Listener l : mListeners) {
753 l.onExpiredConferenceCall(this);
754 }
755 }
756
757 void confirmConference() {
758 Log.v(this, "confirming Conf call %s", mListeners);
759 for (Listener l : mListeners) {
760 l.onConfirmedConferenceCall(this);
761 }
762 }
763
764 void splitFromConference() {
765 // TODO(santoscordon): todo
766 }
767
768 void setParentCall(Call parentCall) {
769 if (parentCall == this) {
770 Log.e(this, new Exception(), "setting the parent to self");
771 return;
772 }
773 Preconditions.checkState(parentCall == null || mParentCall == null);
774
775 Call oldParent = mParentCall;
776 if (mParentCall != null) {
777 mParentCall.removeChildCall(this);
778 }
779 mParentCall = parentCall;
780 if (mParentCall != null) {
781 mParentCall.addChildCall(this);
782 }
783
784 for (Listener l : mListeners) {
785 l.onParentChanged(this);
786 }
787 }
788
789 private void addChildCall(Call call) {
790 if (!mChildCalls.contains(call)) {
791 mChildCalls.add(call);
792
793 for (Listener l : mListeners) {
794 l.onChildrenChanged(this);
795 }
796 }
797 }
798
799 private void removeChildCall(Call call) {
800 if (mChildCalls.remove(call)) {
801 for (Listener l : mListeners) {
802 l.onChildrenChanged(this);
803 }
804 }
805 }
806
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800807 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700808 * Return whether the user can respond to this {@code Call} via an SMS message.
809 *
810 * @return true if the "Respond via SMS" feature should be enabled
811 * for this incoming call.
812 *
813 * The general rule is that we *do* allow "Respond via SMS" except for
814 * the few (relatively rare) cases where we know for sure it won't
815 * work, namely:
816 * - a bogus or blank incoming number
817 * - a call from a SIP address
818 * - a "call presentation" that doesn't allow the number to be revealed
819 *
820 * In all other cases, we allow the user to respond via SMS.
821 *
822 * Note that this behavior isn't perfect; for example we have no way
823 * to detect whether the incoming call is from a landline (with most
824 * networks at least), so we still enable this feature even though
825 * SMSes to that number will silently fail.
826 */
827 boolean isRespondViaSmsCapable() {
828 if (mState != CallState.RINGING) {
829 return false;
830 }
831
832 if (getHandle() == null) {
833 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
834 // other words, the user should not be able to see the incoming phone number.
835 return false;
836 }
837
838 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
839 // The incoming number is actually a URI (i.e. a SIP address),
840 // not a regular PSTN phone number, and we can't send SMSes to
841 // SIP addresses.
842 // (TODO: That might still be possible eventually, though. Is
843 // there some SIP-specific equivalent to sending a text message?)
844 return false;
845 }
846
847 // Is there a valid SMS application on the phone?
848 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
849 true /*updateIfNeeded*/) == null) {
850 return false;
851 }
852
853 // TODO: with some carriers (in certain countries) you *can* actually
854 // tell whether a given number is a mobile phone or not. So in that
855 // case we could potentially return false here if the incoming call is
856 // from a land line.
857
858 // If none of the above special cases apply, it's OK to enable the
859 // "Respond via SMS" feature.
860 return true;
861 }
862
863 List<String> getCannedSmsResponses() {
864 return mCannedSmsResponses;
865 }
866
867 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800868 * @return True if the call is ringing, else logs the action name.
869 */
870 private boolean isRinging(String actionName) {
871 if (mState == CallState.RINGING) {
872 return true;
873 }
874
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800875 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800876 return false;
877 }
878
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800879 @SuppressWarnings("rawtypes")
880 private void decrementAssociatedCallCount(ServiceBinder binder) {
881 if (binder != null) {
882 binder.decrementAssociatedCallCount();
883 }
884 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700885
886 /**
887 * Looks up contact information based on the current handle.
888 */
889 private void startCallerInfoLookup() {
890 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
891
892 mQueryToken++; // Updated so that previous queries can no longer set the information.
893 mCallerInfo = null;
894 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700895 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700896 CallerInfoAsyncQuery.startQuery(
897 mQueryToken,
898 TelecommApp.getInstance(),
899 number,
900 sCallerInfoQueryListener,
901 this);
902 }
903 }
904
905 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700906 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700907 * that was made.
908 *
909 * @param callerInfo The new caller information to set.
910 * @param token The token used with this query.
911 */
912 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700913 Preconditions.checkNotNull(callerInfo);
914
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700915 if (mQueryToken == token) {
916 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700917 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700918
919 if (mCallerInfo.person_id != 0) {
920 Uri personUri =
921 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
922 Log.d(this, "Searching person uri %s for call %s", personUri, this);
923 ContactsAsyncHelper.startObtainPhotoAsync(
924 token,
925 TelecommApp.getInstance(),
926 personUri,
927 sPhotoLoadListener,
928 this);
Santos Cordon64c7e962014-07-02 15:15:27 -0700929 } else {
930 for (Listener l : mListeners) {
931 l.onCallerInfoChanged(this);
932 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700933 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700934
935 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700936 }
937 }
938
939 /**
940 * Saves the specified photo information if the specified token matches that of the last query.
941 *
942 * @param photo The photo as a drawable.
943 * @param photoIcon The photo as a small icon.
944 * @param token The token used with this query.
945 */
946 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
947 if (mQueryToken == token) {
948 mCallerInfo.cachedPhoto = photo;
949 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -0700950
951 for (Listener l : mListeners) {
952 l.onCallerInfoChanged(this);
953 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700954 }
955 }
Ihab Awadff7493a2014-06-10 13:47:44 -0700956
957 private void maybeLoadCannedSmsResponses() {
958 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
959 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
960 mCannedSmsResponsesLoadingStarted = true;
961 RespondViaSmsManager.getInstance().loadCannedTextMessages(
962 new Response<Void, List<String>>() {
963 @Override
964 public void onResult(Void request, List<String>... result) {
965 if (result.length > 0) {
966 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
967 mCannedSmsResponses = result[0];
968 for (Listener l : mListeners) {
969 l.onCannedSmsResponsesLoaded(Call.this);
970 }
971 }
972 }
973
974 @Override
975 public void onError(Void request, int code, String msg) {
976 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
977 msg);
978 }
979 }
980 );
981 } else {
982 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
983 }
984 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700985
986 /**
987 * Sets speakerphone option on when call begins.
988 */
989 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
990 mSpeakerphoneOn = startWithSpeakerphone;
991 }
992
993 /**
994 * Returns speakerphone option.
995 *
996 * @return Whether or not speakerphone should be set automatically when call begins.
997 */
998 public boolean getStartWithSpeakerphoneOn() {
999 return mSpeakerphoneOn;
1000 }
Andrew Leee9a77652014-06-26 13:07:57 -07001001
1002 /**
1003 * Sets a call video provider for the call.
1004 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001005 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1006 mCallVideoProvider = callVideoProvider;
1007 for (Listener l : mListeners) {
1008 l.onCallVideoProviderChanged(Call.this);
1009 }
1010 }
1011
1012 /**
1013 * @return Return the call video Provider binder.
1014 */
1015 public ICallVideoProvider getCallVideoProvider() {
1016 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001017 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001018
1019 /**
1020 * Returns the features of this call.
1021 *
1022 * @return The features of this call.
1023 */
1024 public int getFeatures() {
1025 return mFeatures;
1026 }
1027
1028 /**
1029 * Set the features associated with the call and notify any listeners of the change.
1030 *
1031 * @param features The features.
1032 */
1033 public void setFeatures(int features) {
1034 Log.d(this, "setFeatures: %d", features);
1035 mFeatures = features;
1036 for (Listener l : mListeners) {
1037 l.onFeaturesChanged(Call.this);
1038 }
1039 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001040
1041 /**
1042 * The current video state for the call.
1043 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1044 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1045 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1046 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1047 *
1048 * @return True if video is enabled.
1049 */
1050 public int getVideoState() {
1051 return mVideoState;
1052 }
1053
1054 /**
1055 * At the start of the call, determines the desired video state for the call.
1056 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1057 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1058 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1059 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1060 *
1061 * @param videoState The desired video state for the call.
1062 */
1063 public void setVideoState(int videoState) {
1064 mVideoState = videoState;
1065 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001066
1067 public boolean getAudioModeIsVoip() {
1068 return mAudioModeIsVoip;
1069 }
1070
1071 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1072 mAudioModeIsVoip = audioModeIsVoip;
1073 for (Listener l : mListeners) {
1074 l.onAudioModeIsVoipChanged(Call.this);
1075 }
1076 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001077}