blob: 2810dc82966e224cffff30e24e5284a466fe4ab8 [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);
78 }
79
80 abstract static class ListenerBase implements Listener {
81 @Override
82 public void onSuccessfulOutgoingCall(Call call) {}
83 @Override
84 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
85 @Override
86 public void onCancelledOutgoingCall(Call call) {}
87 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -070088 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -070089 @Override
90 public void onFailedIncomingCall(Call call) {}
91 @Override
92 public void onRequestingRingback(Call call, boolean requestingRingback) {}
93 @Override
94 public void onPostDialWait(Call call, String remaining) {}
95 @Override
96 public void onIsConferenceCapableChanged(Call call, boolean isConferenceCapable) {}
97 @Override
98 public void onExpiredConferenceCall(Call call) {}
99 @Override
100 public void onConfirmedConferenceCall(Call call) {}
101 @Override
102 public void onParentChanged(Call call) {}
103 @Override
104 public void onChildrenChanged(Call call) {}
105 @Override
106 public void onCannedSmsResponsesLoaded(Call call) {}
107 @Override
108 public void onCallVideoProviderChanged(Call call) {}
109 @Override
110 public void onFeaturesChanged(Call call) {}
111 @Override
112 public void onCallerInfoChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700113 }
114
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700115 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700116 new OnQueryCompleteListener() {
117 /** ${inheritDoc} */
118 @Override
119 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
120 if (cookie != null) {
121 ((Call) cookie).setCallerInfo(callerInfo, token);
122 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700123 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700124 };
125
126 private static final OnImageLoadCompleteListener sPhotoLoadListener =
127 new OnImageLoadCompleteListener() {
128 /** ${inheritDoc} */
129 @Override
130 public void onImageLoadComplete(
131 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
132 if (cookie != null) {
133 ((Call) cookie).setPhoto(photo, photoIcon, token);
134 }
135 }
136 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800137
Sailesh Nepal810735e2014-03-18 18:15:46 -0700138 /** True if this is an incoming call. */
139 private final boolean mIsIncoming;
140
Ben Gilad0407fb22014-01-09 16:18:41 -0800141 /**
142 * The time this call was created, typically also the time this call was added to the set
143 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
144 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
145 * certain call attempts as failed attempts.
146 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700147 private final long mCreationTimeMillis = System.currentTimeMillis();
148
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700149 /** The gateway information associated with this call. This stores the original call handle
150 * that the user is attempting to connect to via the gateway, the actual handle to dial in
151 * order to connect the call via the gateway, as well as the package name of the gateway
152 * service. */
153 private final GatewayInfo mGatewayInfo;
154
Ihab Awad98a55602014-06-30 21:27:28 -0700155 private final PhoneAccount mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700156
Santos Cordon2174fb52014-05-29 08:22:56 -0700157 private final Handler mHandler = new Handler();
158
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700159 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800160
Santos Cordon61d0f702014-02-19 02:52:23 -0800161 /** The state of the call. */
162 private CallState mState;
163
164 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700165 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800166
Ben Gilad0407fb22014-01-09 16:18:41 -0800167 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700168 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800169 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700170 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700171
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700172 private boolean mIsEmergencyCall;
173
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700174 private boolean mSpeakerphoneOn;
175
Tyler Gunnc4abd912014-07-08 14:22:10 -0700176 private int mVideoState;
177
Ben Gilad61925612014-03-11 19:06:36 -0700178 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700179 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
180 * See {@link android.telephony.DisconnectCause}.
181 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700182 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700183
184 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700185 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700186 */
187 private String mDisconnectMessage;
188
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700189 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700190 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700191
Santos Cordon766d04f2014-05-06 10:28:25 -0700192 /** Set of listeners on this call. */
193 private Set<Listener> mListeners = Sets.newHashSet();
194
Santos Cordon682fe6b2014-05-20 08:56:39 -0700195 private OutgoingCallProcessor mOutgoingCallProcessor;
196
197 // TODO(santoscordon): The repositories should be changed into singleton types.
198 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700199
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700200 /** Caller information retrieved from the latest contact query. */
201 private CallerInfo mCallerInfo;
202
203 /** The latest token used with a contact info query. */
204 private int mQueryToken = 0;
205
Ihab Awadcb387ac2014-05-28 16:49:38 -0700206 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
207 private boolean mRequestingRingback = false;
208
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700209 /** Whether direct-to-voicemail query is pending. */
210 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700211
Santos Cordona1610702014-06-04 20:22:56 -0700212 private boolean mIsConferenceCapable = false;
213
214 private boolean mIsConference = false;
215
216 private Call mParentCall = null;
217
218 private List<Call> mChildCalls = new LinkedList<>();
219
Ihab Awadff7493a2014-06-10 13:47:44 -0700220 /** Set of text message responses allowed for this call, if applicable. */
221 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
222
223 /** Whether an attempt has been made to load the text message responses. */
224 private boolean mCannedSmsResponsesLoadingStarted = false;
225
Nancy Chena65d41f2014-06-24 12:06:03 -0700226 private ICallVideoProvider mCallVideoProvider;
227
Tyler Gunne19cc002014-07-01 11:32:53 -0700228 /** Features associated with the call which the InCall UI may wish to show icons for. */
229 private int mFeatures;
230
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700231 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700232 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700233 *
234 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800235 */
Santos Cordona1610702014-06-04 20:22:56 -0700236 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700237 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800238 }
239
240 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800241 * Persists the specified parameters and initializes the new instance.
242 *
243 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700244 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700245 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700246 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800247 */
Ihab Awad98a55602014-06-30 21:27:28 -0700248 Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700249 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700250 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700251 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700252 mGatewayInfo = gatewayInfo;
Ihab Awad98a55602014-06-30 21:27:28 -0700253 mAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700254 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700255 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700256 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800257 }
258
Santos Cordon766d04f2014-05-06 10:28:25 -0700259 void addListener(Listener listener) {
260 mListeners.add(listener);
261 }
262
263 void removeListener(Listener listener) {
264 mListeners.remove(listener);
265 }
266
Santos Cordon61d0f702014-02-19 02:52:23 -0800267 /** {@inheritDoc} */
268 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700269 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700270 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
271 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700272 }
273 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800274 }
275
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800276 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700277 if (mIsConference) {
278 if (!mChildCalls.isEmpty()) {
279 // If we have child calls, just return the child call.
280 return mChildCalls.get(0).getState();
281 }
282 return CallState.ACTIVE;
283 } else {
284 return mState;
285 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800286 }
287
288 /**
289 * Sets the call state. Although there exists the notion of appropriate state transitions
290 * (see {@link CallState}), in practice those expectations break down when cellular systems
291 * misbehave and they do this very often. The result is that we do not enforce state transitions
292 * and instead keep the code resilient to unexpected state changes.
293 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700294 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700295 Preconditions.checkState(newState != CallState.DISCONNECTED ||
296 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700297 if (mState != newState) {
298 Log.v(this, "setState %s -> %s", mState, newState);
299 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700300 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700301 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800302 }
303
Ihab Awadcb387ac2014-05-28 16:49:38 -0700304 void setRequestingRingback(boolean requestingRingback) {
305 mRequestingRingback = requestingRingback;
306 for (Listener l : mListeners) {
307 l.onRequestingRingback(this, mRequestingRingback);
308 }
309 }
310
311 boolean isRequestingRingback() {
312 return mRequestingRingback;
313 }
314
Sailesh Nepalce704b92014-03-17 18:31:43 -0700315 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800316 return mHandle;
317 }
318
Sailesh Nepalce704b92014-03-17 18:31:43 -0700319 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700320 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
321 mHandle = handle;
322 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700323 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700324 startCallerInfoLookup();
325 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700326 }
327
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700328 String getName() {
329 return mCallerInfo == null ? null : mCallerInfo.name;
330 }
331
332 Bitmap getPhotoIcon() {
333 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
334 }
335
336 Drawable getPhoto() {
337 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
338 }
339
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700340 /**
341 * @param disconnectCause The reason for the disconnection, any of
342 * {@link android.telephony.DisconnectCause}.
343 * @param disconnectMessage Optional call-service-provided message about the disconnect.
344 */
345 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
346 // TODO: Consider combining this method with a setDisconnected() method that is totally
347 // separate from setState.
348 mDisconnectCause = disconnectCause;
349 mDisconnectMessage = disconnectMessage;
350 }
351
352 int getDisconnectCause() {
353 return mDisconnectCause;
354 }
355
356 String getDisconnectMessage() {
357 return mDisconnectMessage;
358 }
359
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700360 boolean isEmergencyCall() {
361 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800362 }
363
Yorke Lee33501632014-03-17 19:24:12 -0700364 /**
365 * @return The original handle this call is associated with. In-call services should use this
366 * handle when indicating in their UI the handle that is being called.
367 */
368 public Uri getOriginalHandle() {
369 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
370 return mGatewayInfo.getOriginalHandle();
371 }
372 return getHandle();
373 }
374
375 GatewayInfo getGatewayInfo() {
376 return mGatewayInfo;
377 }
378
Ihab Awad98a55602014-06-30 21:27:28 -0700379 PhoneAccount getAccount() {
380 return mAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700381 }
382
Sailesh Nepal810735e2014-03-18 18:15:46 -0700383 boolean isIncoming() {
384 return mIsIncoming;
385 }
386
Ben Gilad0407fb22014-01-09 16:18:41 -0800387 /**
388 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700389 * period since this call was added to the set pending outgoing calls, see
390 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800391 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700392 long getAgeMillis() {
393 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800394 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800395
Yorke Leef98fb572014-03-05 10:56:55 -0800396 /**
397 * @return The time when this call object was created and added to the set of pending outgoing
398 * calls.
399 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700400 long getCreationTimeMillis() {
401 return mCreationTimeMillis;
402 }
403
404 long getConnectTimeMillis() {
405 return mConnectTimeMillis;
406 }
407
408 void setConnectTimeMillis(long connectTimeMillis) {
409 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800410 }
411
Santos Cordona1610702014-06-04 20:22:56 -0700412 boolean isConferenceCapable() {
413 return mIsConferenceCapable;
414 }
415
416 void setIsConferenceCapable(boolean isConferenceCapable) {
417 if (mIsConferenceCapable != isConferenceCapable) {
418 mIsConferenceCapable = isConferenceCapable;
419 for (Listener l : mListeners) {
420 l.onIsConferenceCapableChanged(this, mIsConferenceCapable);
421 }
422 }
423 }
424
425 Call getParentCall() {
426 return mParentCall;
427 }
428
429 List<Call> getChildCalls() {
430 return mChildCalls;
431 }
432
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700433 ConnectionServiceWrapper getConnectionService() {
434 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800435 }
436
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700437 void setConnectionService(ConnectionServiceWrapper service) {
438 Preconditions.checkNotNull(service);
439
440 clearConnectionService();
441
442 service.incrementAssociatedCallCount();
443 mConnectionService = service;
444 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700445 }
446
447 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700448 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700449 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700450 void clearConnectionService() {
451 if (mConnectionService != null) {
452 ConnectionServiceWrapper serviceTemp = mConnectionService;
453 mConnectionService = null;
454 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700455
456 // Decrementing the count can cause the service to unbind, which itself can trigger the
457 // service-death code. Since the service death code tries to clean up any associated
458 // calls, we need to make sure to remove that information (e.g., removeCall()) before
459 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700460 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
461 // to do.
462 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700463 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800464 }
465
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800466 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700467 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
468 * invoke handle[Un]SuccessfulIncomingCall.
469 *
470 * @param descriptor The relevant call-service descriptor.
471 * @param extras The optional extras passed via
472 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
473 */
474 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
475 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
476 }
477
Santos Cordon2174fb52014-05-29 08:22:56 -0700478 /**
479 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
480 * from the contacts provider. The call is not yet exposed to the user at this point and
481 * the result of the query will determine if the call is rejected or passed through to the
482 * in-call UI.
483 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700484 void handleVerifiedIncoming(ConnectionRequest request) {
485 // We do not handle incoming calls immediately when they are verified by the connection
486 // service. We allow the caller-info-query code to execute first so that we can read the
Santos Cordon2174fb52014-05-29 08:22:56 -0700487 // direct-to-voicemail property before deciding if we want to show the incoming call to the
488 // user or if we want to reject the call.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700489 mDirectToVoicemailQueryPending = true;
Santos Cordon2174fb52014-05-29 08:22:56 -0700490
491 // Setting the handle triggers the caller info lookup code.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700492 setHandle(request.getHandle());
Santos Cordon766d04f2014-05-06 10:28:25 -0700493
Santos Cordon2174fb52014-05-29 08:22:56 -0700494 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
495 // showing the user the incoming call screen.
496 mHandler.postDelayed(new Runnable() {
497 @Override
498 public void run() {
499 processDirectToVoicemail();
500 }
Santos Cordona1610702014-06-04 20:22:56 -0700501 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700502 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700503
Santos Cordon2174fb52014-05-29 08:22:56 -0700504 void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700505 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700506 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
507 Log.i(this, "Directing call to voicemail: %s.", this);
508 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
509 // will not need to set RINGING state prior to calling reject.
510 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700511 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700512 } else {
513 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
514 // the call state to RINGING.
515
516 // TODO(santoscordon): Replace this with state transition to RINGING.
517 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700518 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700519 }
520 }
521
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700522 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700523 }
524 }
525
526 void handleFailedIncoming() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700527 clearConnectionService();
Santos Cordon766d04f2014-05-06 10:28:25 -0700528
529 // TODO: Needs more specific disconnect error for this case.
530 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
531 setState(CallState.DISCONNECTED);
532
533 // TODO(santoscordon): Replace this with state transitions related to "connecting".
534 for (Listener l : mListeners) {
535 l.onFailedIncomingCall(this);
536 }
537 }
538
539 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700540 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700541 * through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700542 */
543 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700544 Preconditions.checkState(mOutgoingCallProcessor == null);
545
546 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700547 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700548 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700549 }
550
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700551 @Override
552 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700553 // TODO(santoscordon): Replace this with state transitions related to "connecting".
554 for (Listener l : mListeners) {
555 l.onSuccessfulOutgoingCall(this);
556 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700557 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700558 }
559
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700560 @Override
561 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700562 // TODO(santoscordon): Replace this with state transitions related to "connecting".
563 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700564 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700565 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700566
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700567 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700568 mOutgoingCallProcessor = null;
569 }
570
571 @Override
572 public void onOutgoingCallCancel() {
573 // TODO(santoscordon): Replace this with state transitions related to "connecting".
574 for (Listener l : mListeners) {
575 l.onCancelledOutgoingCall(this);
576 }
577
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700578 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700579 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700580 }
581
582 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700583 * Plays the specified DTMF tone.
584 */
585 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700586 if (mConnectionService == null) {
587 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700588 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700589 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
590 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700591 }
592 }
593
594 /**
595 * Stops playing any currently playing DTMF tone.
596 */
597 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700598 if (mConnectionService == null) {
599 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700600 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700601 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
602 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700603 }
604 }
605
606 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700607 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800608 */
609 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700610 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700611 Log.v(this, "Aborting call %s", this);
612 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700613 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700614 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700615
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700616 Log.i(this, "Send disconnect to connection service for call: %s", this);
617 // The call isn't officially disconnected until the connection service confirms that the
618 // call was actually disconnected. Only then is the association between call and
619 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
620 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800621 }
622 }
623
Santos Cordon682fe6b2014-05-20 08:56:39 -0700624 void abort() {
625 if (mOutgoingCallProcessor != null) {
626 mOutgoingCallProcessor.abort();
627 }
628 }
629
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800630 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800631 * Answers the call if it is ringing.
632 */
633 void answer() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700634 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800635
636 // Check to verify that the call is still in the ringing state. A call can change states
637 // between the time the user hits 'answer' and Telecomm receives the command.
638 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700639 // At this point, we are asking the connection service to answer but we don't assume
640 // that it will work. Instead, we wait until confirmation from the connectino service
641 // that the call is in a non-RINGING state before changing the UI. See
642 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
643 mConnectionService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800644 }
645 }
646
647 /**
648 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700649 *
650 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
651 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800652 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700653 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700654 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800655
656 // Check to verify that the call is still in the ringing state. A call can change states
657 // between the time the user hits 'reject' and Telecomm receives the command.
658 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700659 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800660 }
661 }
662
663 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700664 * Puts the call on hold if it is currently active.
665 */
666 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700667 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700668
669 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700670 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700671 }
672 }
673
674 /**
675 * Releases the call from hold if it is currently active.
676 */
677 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700678 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700679
680 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700681 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700682 }
683 }
684
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700685 /** Checks if this is a live call or not. */
686 boolean isAlive() {
687 switch (mState) {
688 case NEW:
689 case RINGING:
690 case DISCONNECTED:
691 case ABORTED:
692 return false;
693 default:
694 return true;
695 }
696 }
697
Santos Cordon40f78c22014-04-07 02:11:42 -0700698 boolean isActive() {
699 switch (mState) {
700 case ACTIVE:
701 case POST_DIAL:
702 case POST_DIAL_WAIT:
703 return true;
704 default:
705 return false;
706 }
707 }
708
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700709 Bundle getExtras() {
710 return mExtras;
711 }
712
713 void setExtras(Bundle extras) {
714 mExtras = extras;
715 }
716
Santos Cordon5ba7f272014-05-28 13:59:49 -0700717 Uri getRingtone() {
718 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
719 }
720
Evan Charlton352105c2014-06-03 14:10:54 -0700721 void onPostDialWait(String remaining) {
722 for (Listener l : mListeners) {
723 l.onPostDialWait(this, remaining);
724 }
725 }
726
727 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700728 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700729 }
730
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700731 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700732 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700733 }
734
Santos Cordona1610702014-06-04 20:22:56 -0700735 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700736 if (mConnectionService == null) {
737 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700738 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700739 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700740 }
741 }
742
743 void expireConference() {
744 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700745 // connection service...so start shutting down.
746 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700747 for (Listener l : mListeners) {
748 l.onExpiredConferenceCall(this);
749 }
750 }
751
752 void confirmConference() {
753 Log.v(this, "confirming Conf call %s", mListeners);
754 for (Listener l : mListeners) {
755 l.onConfirmedConferenceCall(this);
756 }
757 }
758
759 void splitFromConference() {
760 // TODO(santoscordon): todo
761 }
762
763 void setParentCall(Call parentCall) {
764 if (parentCall == this) {
765 Log.e(this, new Exception(), "setting the parent to self");
766 return;
767 }
768 Preconditions.checkState(parentCall == null || mParentCall == null);
769
770 Call oldParent = mParentCall;
771 if (mParentCall != null) {
772 mParentCall.removeChildCall(this);
773 }
774 mParentCall = parentCall;
775 if (mParentCall != null) {
776 mParentCall.addChildCall(this);
777 }
778
779 for (Listener l : mListeners) {
780 l.onParentChanged(this);
781 }
782 }
783
784 private void addChildCall(Call call) {
785 if (!mChildCalls.contains(call)) {
786 mChildCalls.add(call);
787
788 for (Listener l : mListeners) {
789 l.onChildrenChanged(this);
790 }
791 }
792 }
793
794 private void removeChildCall(Call call) {
795 if (mChildCalls.remove(call)) {
796 for (Listener l : mListeners) {
797 l.onChildrenChanged(this);
798 }
799 }
800 }
801
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800802 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700803 * Return whether the user can respond to this {@code Call} via an SMS message.
804 *
805 * @return true if the "Respond via SMS" feature should be enabled
806 * for this incoming call.
807 *
808 * The general rule is that we *do* allow "Respond via SMS" except for
809 * the few (relatively rare) cases where we know for sure it won't
810 * work, namely:
811 * - a bogus or blank incoming number
812 * - a call from a SIP address
813 * - a "call presentation" that doesn't allow the number to be revealed
814 *
815 * In all other cases, we allow the user to respond via SMS.
816 *
817 * Note that this behavior isn't perfect; for example we have no way
818 * to detect whether the incoming call is from a landline (with most
819 * networks at least), so we still enable this feature even though
820 * SMSes to that number will silently fail.
821 */
822 boolean isRespondViaSmsCapable() {
823 if (mState != CallState.RINGING) {
824 return false;
825 }
826
827 if (getHandle() == null) {
828 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
829 // other words, the user should not be able to see the incoming phone number.
830 return false;
831 }
832
833 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
834 // The incoming number is actually a URI (i.e. a SIP address),
835 // not a regular PSTN phone number, and we can't send SMSes to
836 // SIP addresses.
837 // (TODO: That might still be possible eventually, though. Is
838 // there some SIP-specific equivalent to sending a text message?)
839 return false;
840 }
841
842 // Is there a valid SMS application on the phone?
843 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
844 true /*updateIfNeeded*/) == null) {
845 return false;
846 }
847
848 // TODO: with some carriers (in certain countries) you *can* actually
849 // tell whether a given number is a mobile phone or not. So in that
850 // case we could potentially return false here if the incoming call is
851 // from a land line.
852
853 // If none of the above special cases apply, it's OK to enable the
854 // "Respond via SMS" feature.
855 return true;
856 }
857
858 List<String> getCannedSmsResponses() {
859 return mCannedSmsResponses;
860 }
861
862 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800863 * @return True if the call is ringing, else logs the action name.
864 */
865 private boolean isRinging(String actionName) {
866 if (mState == CallState.RINGING) {
867 return true;
868 }
869
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800870 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800871 return false;
872 }
873
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800874 @SuppressWarnings("rawtypes")
875 private void decrementAssociatedCallCount(ServiceBinder binder) {
876 if (binder != null) {
877 binder.decrementAssociatedCallCount();
878 }
879 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700880
881 /**
882 * Looks up contact information based on the current handle.
883 */
884 private void startCallerInfoLookup() {
885 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
886
887 mQueryToken++; // Updated so that previous queries can no longer set the information.
888 mCallerInfo = null;
889 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700890 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700891 CallerInfoAsyncQuery.startQuery(
892 mQueryToken,
893 TelecommApp.getInstance(),
894 number,
895 sCallerInfoQueryListener,
896 this);
897 }
898 }
899
900 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700901 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700902 * that was made.
903 *
904 * @param callerInfo The new caller information to set.
905 * @param token The token used with this query.
906 */
907 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700908 Preconditions.checkNotNull(callerInfo);
909
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700910 if (mQueryToken == token) {
911 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700912 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700913
914 if (mCallerInfo.person_id != 0) {
915 Uri personUri =
916 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
917 Log.d(this, "Searching person uri %s for call %s", personUri, this);
918 ContactsAsyncHelper.startObtainPhotoAsync(
919 token,
920 TelecommApp.getInstance(),
921 personUri,
922 sPhotoLoadListener,
923 this);
Santos Cordon64c7e962014-07-02 15:15:27 -0700924 } else {
925 for (Listener l : mListeners) {
926 l.onCallerInfoChanged(this);
927 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700928 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700929
930 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700931 }
932 }
933
934 /**
935 * Saves the specified photo information if the specified token matches that of the last query.
936 *
937 * @param photo The photo as a drawable.
938 * @param photoIcon The photo as a small icon.
939 * @param token The token used with this query.
940 */
941 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
942 if (mQueryToken == token) {
943 mCallerInfo.cachedPhoto = photo;
944 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -0700945
946 for (Listener l : mListeners) {
947 l.onCallerInfoChanged(this);
948 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700949 }
950 }
Ihab Awadff7493a2014-06-10 13:47:44 -0700951
952 private void maybeLoadCannedSmsResponses() {
953 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
954 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
955 mCannedSmsResponsesLoadingStarted = true;
956 RespondViaSmsManager.getInstance().loadCannedTextMessages(
957 new Response<Void, List<String>>() {
958 @Override
959 public void onResult(Void request, List<String>... result) {
960 if (result.length > 0) {
961 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
962 mCannedSmsResponses = result[0];
963 for (Listener l : mListeners) {
964 l.onCannedSmsResponsesLoaded(Call.this);
965 }
966 }
967 }
968
969 @Override
970 public void onError(Void request, int code, String msg) {
971 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
972 msg);
973 }
974 }
975 );
976 } else {
977 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
978 }
979 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700980
981 /**
982 * Sets speakerphone option on when call begins.
983 */
984 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
985 mSpeakerphoneOn = startWithSpeakerphone;
986 }
987
988 /**
989 * Returns speakerphone option.
990 *
991 * @return Whether or not speakerphone should be set automatically when call begins.
992 */
993 public boolean getStartWithSpeakerphoneOn() {
994 return mSpeakerphoneOn;
995 }
Andrew Leee9a77652014-06-26 13:07:57 -0700996
997 /**
998 * Sets a call video provider for the call.
999 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001000 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1001 mCallVideoProvider = callVideoProvider;
1002 for (Listener l : mListeners) {
1003 l.onCallVideoProviderChanged(Call.this);
1004 }
1005 }
1006
1007 /**
1008 * @return Return the call video Provider binder.
1009 */
1010 public ICallVideoProvider getCallVideoProvider() {
1011 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001012 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001013
1014 /**
1015 * Returns the features of this call.
1016 *
1017 * @return The features of this call.
1018 */
1019 public int getFeatures() {
1020 return mFeatures;
1021 }
1022
1023 /**
1024 * Set the features associated with the call and notify any listeners of the change.
1025 *
1026 * @param features The features.
1027 */
1028 public void setFeatures(int features) {
1029 Log.d(this, "setFeatures: %d", features);
1030 mFeatures = features;
1031 for (Listener l : mListeners) {
1032 l.onFeaturesChanged(Call.this);
1033 }
1034 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001035
1036 /**
1037 * The current video state for the call.
1038 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1039 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1040 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1041 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1042 *
1043 * @return True if video is enabled.
1044 */
1045 public int getVideoState() {
1046 return mVideoState;
1047 }
1048
1049 /**
1050 * At the start of the call, determines the desired video state for the call.
1051 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1052 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1053 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1054 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1055 *
1056 * @param videoState The desired video state for the call.
1057 */
1058 public void setVideoState(int videoState) {
1059 mVideoState = videoState;
1060 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001061}