blob: f5229ccc2746ad7ba7c8456396885e1615fc682b [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;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070032import android.telecomm.StatusHints;
Santos Cordon766d04f2014-05-06 10:28:25 -070033import android.telecomm.TelecommConstants;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070034import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070035import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070036import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080037
Nancy Chena65d41f2014-06-24 12:06:03 -070038import com.android.internal.telecomm.ICallVideoProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070039import com.android.internal.telephony.CallerInfo;
40import com.android.internal.telephony.CallerInfoAsyncQuery;
41import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070042import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070043import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080044import com.google.common.base.Preconditions;
Ihab Awadff7493a2014-06-10 13:47:44 -070045import com.google.common.collect.Sets;
Santos Cordon61d0f702014-02-19 02:52:23 -080046
Ihab Awadff7493a2014-06-10 13:47:44 -070047import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070048import java.util.LinkedList;
49import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080050import java.util.Locale;
Ben Gilad61925612014-03-11 19:06:36 -070051import java.util.Set;
Ben Gilad0407fb22014-01-09 16:18:41 -080052
Ben Gilad2495d572014-01-09 17:26:19 -080053/**
54 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
55 * from the time the call intent was received by Telecomm (vs. the time the call was
56 * connected etc).
57 */
Sailesh Nepal5a73b032014-06-25 15:53:21 -070058final class Call implements OutgoingCallResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070059 /**
60 * Listener for events on the call.
61 */
62 interface Listener {
63 void onSuccessfulOutgoingCall(Call call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -070064 void onFailedOutgoingCall(Call call, int errorCode, String errorMsg);
65 void onCancelledOutgoingCall(Call call);
Sailesh Nepalc92c4362014-07-04 18:33:21 -070066 void onSuccessfulIncomingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070067 void onFailedIncomingCall(Call call);
Ihab Awadcb387ac2014-05-28 16:49:38 -070068 void onRequestingRingback(Call call, boolean requestingRingback);
Evan Charlton352105c2014-06-03 14:10:54 -070069 void onPostDialWait(Call call, String remaining);
Sailesh Nepale20bc972014-07-09 21:22:36 -070070 void onCallCapabilitiesChanged(Call call);
Santos Cordona1610702014-06-04 20:22:56 -070071 void onExpiredConferenceCall(Call call);
72 void onConfirmedConferenceCall(Call call);
73 void onParentChanged(Call call);
74 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -070075 void onCannedSmsResponsesLoaded(Call call);
Nancy Chena65d41f2014-06-24 12:06:03 -070076 void onCallVideoProviderChanged(Call call);
Tyler Gunne19cc002014-07-01 11:32:53 -070077 void onFeaturesChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070078 void onCallerInfoChanged(Call call);
Sailesh Nepal7e669572014-07-08 21:29:12 -070079 void onAudioModeIsVoipChanged(Call call);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070080 void onStatusHintsChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070081 }
82
83 abstract static class ListenerBase implements Listener {
84 @Override
85 public void onSuccessfulOutgoingCall(Call call) {}
86 @Override
87 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
88 @Override
89 public void onCancelledOutgoingCall(Call call) {}
90 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -070091 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -070092 @Override
93 public void onFailedIncomingCall(Call call) {}
94 @Override
95 public void onRequestingRingback(Call call, boolean requestingRingback) {}
96 @Override
97 public void onPostDialWait(Call call, String remaining) {}
98 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -070099 public void onCallCapabilitiesChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700100 @Override
101 public void onExpiredConferenceCall(Call call) {}
102 @Override
103 public void onConfirmedConferenceCall(Call call) {}
104 @Override
105 public void onParentChanged(Call call) {}
106 @Override
107 public void onChildrenChanged(Call call) {}
108 @Override
109 public void onCannedSmsResponsesLoaded(Call call) {}
110 @Override
111 public void onCallVideoProviderChanged(Call call) {}
112 @Override
113 public void onFeaturesChanged(Call call) {}
114 @Override
115 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700116 @Override
117 public void onAudioModeIsVoipChanged(Call call) {}
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700118 @Override
119 public void onStatusHintsChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700120 }
121
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700122 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700123 new OnQueryCompleteListener() {
124 /** ${inheritDoc} */
125 @Override
126 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
127 if (cookie != null) {
128 ((Call) cookie).setCallerInfo(callerInfo, token);
129 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700130 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700131 };
132
133 private static final OnImageLoadCompleteListener sPhotoLoadListener =
134 new OnImageLoadCompleteListener() {
135 /** ${inheritDoc} */
136 @Override
137 public void onImageLoadComplete(
138 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
139 if (cookie != null) {
140 ((Call) cookie).setPhoto(photo, photoIcon, token);
141 }
142 }
143 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800144
Sailesh Nepal810735e2014-03-18 18:15:46 -0700145 /** True if this is an incoming call. */
146 private final boolean mIsIncoming;
147
Ben Gilad0407fb22014-01-09 16:18:41 -0800148 /**
149 * The time this call was created, typically also the time this call was added to the set
150 * of pending outgoing calls (mPendingOutgoingCalls) that's maintained by the switchboard.
151 * Beyond logging and such, may also be used for bookkeeping and specifically for marking
152 * certain call attempts as failed attempts.
153 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700154 private final long mCreationTimeMillis = System.currentTimeMillis();
155
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700156 /** The gateway information associated with this call. This stores the original call handle
157 * that the user is attempting to connect to via the gateway, the actual handle to dial in
158 * order to connect the call via the gateway, as well as the package name of the gateway
159 * service. */
160 private final GatewayInfo mGatewayInfo;
161
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700162 private PhoneAccount mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700163
Santos Cordon2174fb52014-05-29 08:22:56 -0700164 private final Handler mHandler = new Handler();
165
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700166 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800167
Santos Cordon61d0f702014-02-19 02:52:23 -0800168 /** The state of the call. */
169 private CallState mState;
170
171 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700172 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800173
Ben Gilad0407fb22014-01-09 16:18:41 -0800174 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700175 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800176 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700177 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700178
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700179 private boolean mIsEmergencyCall;
180
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700181 private boolean mSpeakerphoneOn;
182
Tyler Gunnc4abd912014-07-08 14:22:10 -0700183 private int mVideoState;
184
Ben Gilad61925612014-03-11 19:06:36 -0700185 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700186 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
187 * See {@link android.telephony.DisconnectCause}.
188 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700189 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700190
191 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700192 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700193 */
194 private String mDisconnectMessage;
195
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700196 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700197 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700198
Santos Cordon766d04f2014-05-06 10:28:25 -0700199 /** Set of listeners on this call. */
200 private Set<Listener> mListeners = Sets.newHashSet();
201
Santos Cordon682fe6b2014-05-20 08:56:39 -0700202 private OutgoingCallProcessor mOutgoingCallProcessor;
203
204 // TODO(santoscordon): The repositories should be changed into singleton types.
205 private CallServiceRepository mCallServiceRepository;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700206
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700207 /** Caller information retrieved from the latest contact query. */
208 private CallerInfo mCallerInfo;
209
210 /** The latest token used with a contact info query. */
211 private int mQueryToken = 0;
212
Ihab Awadcb387ac2014-05-28 16:49:38 -0700213 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
214 private boolean mRequestingRingback = false;
215
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700216 /** Whether direct-to-voicemail query is pending. */
217 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700218
Sailesh Nepale20bc972014-07-09 21:22:36 -0700219 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700220
221 private boolean mIsConference = false;
222
223 private Call mParentCall = null;
224
225 private List<Call> mChildCalls = new LinkedList<>();
226
Ihab Awadff7493a2014-06-10 13:47:44 -0700227 /** Set of text message responses allowed for this call, if applicable. */
228 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
229
230 /** Whether an attempt has been made to load the text message responses. */
231 private boolean mCannedSmsResponsesLoadingStarted = false;
232
Nancy Chena65d41f2014-06-24 12:06:03 -0700233 private ICallVideoProvider mCallVideoProvider;
234
Tyler Gunne19cc002014-07-01 11:32:53 -0700235 /** Features associated with the call which the InCall UI may wish to show icons for. */
236 private int mFeatures;
237
Sailesh Nepal7e669572014-07-08 21:29:12 -0700238 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700239 private StatusHints mStatusHints;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700240
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700241 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700242 * Creates an empty call object.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700243 *
244 * @param isIncoming True if this is an incoming call.
Santos Cordon493e8f22014-02-19 03:15:12 -0800245 */
Santos Cordona1610702014-06-04 20:22:56 -0700246 Call(boolean isIncoming, boolean isConference) {
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700247 this(null, null, null, isIncoming, isConference);
Santos Cordon493e8f22014-02-19 03:15:12 -0800248 }
249
250 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800251 * Persists the specified parameters and initializes the new instance.
252 *
253 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700254 * @param gatewayInfo Gateway information to use for the call.
Ihab Awad98a55602014-06-30 21:27:28 -0700255 * @param account Account information to use for the call.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700256 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800257 */
Ihab Awad98a55602014-06-30 21:27:28 -0700258 Call(Uri handle, GatewayInfo gatewayInfo, PhoneAccount account,
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700259 boolean isIncoming, boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700260 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700261 setHandle(handle);
Yorke Lee33501632014-03-17 19:24:12 -0700262 mGatewayInfo = gatewayInfo;
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700263 mPhoneAccount = account;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700264 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700265 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700266 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800267 }
268
Santos Cordon766d04f2014-05-06 10:28:25 -0700269 void addListener(Listener listener) {
270 mListeners.add(listener);
271 }
272
273 void removeListener(Listener listener) {
274 mListeners.remove(listener);
275 }
276
Santos Cordon61d0f702014-02-19 02:52:23 -0800277 /** {@inheritDoc} */
278 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700279 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700280 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
281 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700282 }
283 return String.format(Locale.US, "[%s, %s, %s]", mState, component, Log.piiHandle(mHandle));
Santos Cordon61d0f702014-02-19 02:52:23 -0800284 }
285
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800286 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700287 if (mIsConference) {
288 if (!mChildCalls.isEmpty()) {
289 // If we have child calls, just return the child call.
290 return mChildCalls.get(0).getState();
291 }
292 return CallState.ACTIVE;
293 } else {
294 return mState;
295 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800296 }
297
298 /**
299 * Sets the call state. Although there exists the notion of appropriate state transitions
300 * (see {@link CallState}), in practice those expectations break down when cellular systems
301 * misbehave and they do this very often. The result is that we do not enforce state transitions
302 * and instead keep the code resilient to unexpected state changes.
303 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700304 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700305 Preconditions.checkState(newState != CallState.DISCONNECTED ||
306 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700307 if (mState != newState) {
308 Log.v(this, "setState %s -> %s", mState, newState);
309 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700310 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700311 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800312 }
313
Ihab Awadcb387ac2014-05-28 16:49:38 -0700314 void setRequestingRingback(boolean requestingRingback) {
315 mRequestingRingback = requestingRingback;
316 for (Listener l : mListeners) {
317 l.onRequestingRingback(this, mRequestingRingback);
318 }
319 }
320
321 boolean isRequestingRingback() {
322 return mRequestingRingback;
323 }
324
Sailesh Nepalce704b92014-03-17 18:31:43 -0700325 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800326 return mHandle;
327 }
328
Sailesh Nepalce704b92014-03-17 18:31:43 -0700329 void setHandle(Uri handle) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700330 if ((mHandle == null && handle != null) || (mHandle != null && !mHandle.equals(handle))) {
331 mHandle = handle;
332 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700333 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700334 startCallerInfoLookup();
335 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700336 }
337
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700338 String getName() {
339 return mCallerInfo == null ? null : mCallerInfo.name;
340 }
341
342 Bitmap getPhotoIcon() {
343 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
344 }
345
346 Drawable getPhoto() {
347 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
348 }
349
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700350 /**
351 * @param disconnectCause The reason for the disconnection, any of
352 * {@link android.telephony.DisconnectCause}.
353 * @param disconnectMessage Optional call-service-provided message about the disconnect.
354 */
355 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
356 // TODO: Consider combining this method with a setDisconnected() method that is totally
357 // separate from setState.
358 mDisconnectCause = disconnectCause;
359 mDisconnectMessage = disconnectMessage;
360 }
361
362 int getDisconnectCause() {
363 return mDisconnectCause;
364 }
365
366 String getDisconnectMessage() {
367 return mDisconnectMessage;
368 }
369
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700370 boolean isEmergencyCall() {
371 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800372 }
373
Yorke Lee33501632014-03-17 19:24:12 -0700374 /**
375 * @return The original handle this call is associated with. In-call services should use this
376 * handle when indicating in their UI the handle that is being called.
377 */
378 public Uri getOriginalHandle() {
379 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
380 return mGatewayInfo.getOriginalHandle();
381 }
382 return getHandle();
383 }
384
385 GatewayInfo getGatewayInfo() {
386 return mGatewayInfo;
387 }
388
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700389 PhoneAccount getPhoneAccount() {
390 return mPhoneAccount;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700391 }
392
Sailesh Nepal810735e2014-03-18 18:15:46 -0700393 boolean isIncoming() {
394 return mIsIncoming;
395 }
396
Ben Gilad0407fb22014-01-09 16:18:41 -0800397 /**
398 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700399 * period since this call was added to the set pending outgoing calls, see
400 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800401 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700402 long getAgeMillis() {
403 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800404 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800405
Yorke Leef98fb572014-03-05 10:56:55 -0800406 /**
407 * @return The time when this call object was created and added to the set of pending outgoing
408 * calls.
409 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700410 long getCreationTimeMillis() {
411 return mCreationTimeMillis;
412 }
413
414 long getConnectTimeMillis() {
415 return mConnectTimeMillis;
416 }
417
418 void setConnectTimeMillis(long connectTimeMillis) {
419 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800420 }
421
Sailesh Nepale20bc972014-07-09 21:22:36 -0700422 int getCallCapabilities() {
423 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700424 }
425
Sailesh Nepale20bc972014-07-09 21:22:36 -0700426 void setCallCapabilities(int callCapabilities) {
427 if (mCallCapabilities != callCapabilities) {
428 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700429 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700430 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700431 }
432 }
433 }
434
435 Call getParentCall() {
436 return mParentCall;
437 }
438
439 List<Call> getChildCalls() {
440 return mChildCalls;
441 }
442
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700443 ConnectionServiceWrapper getConnectionService() {
444 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800445 }
446
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700447 void setConnectionService(ConnectionServiceWrapper service) {
448 Preconditions.checkNotNull(service);
449
450 clearConnectionService();
451
452 service.incrementAssociatedCallCount();
453 mConnectionService = service;
454 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700455 }
456
457 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700458 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700459 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700460 void clearConnectionService() {
461 if (mConnectionService != null) {
462 ConnectionServiceWrapper serviceTemp = mConnectionService;
463 mConnectionService = null;
464 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700465
466 // Decrementing the count can cause the service to unbind, which itself can trigger the
467 // service-death code. Since the service death code tries to clean up any associated
468 // calls, we need to make sure to remove that information (e.g., removeCall()) before
469 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700470 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
471 // to do.
472 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700473 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800474 }
475
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800476 /**
Santos Cordon766d04f2014-05-06 10:28:25 -0700477 * Starts the incoming call flow through the switchboard. When switchboard completes, it will
478 * invoke handle[Un]SuccessfulIncomingCall.
479 *
480 * @param descriptor The relevant call-service descriptor.
481 * @param extras The optional extras passed via
482 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}.
483 */
484 void startIncoming(CallServiceDescriptor descriptor, Bundle extras) {
485 Switchboard.getInstance().retrieveIncomingCall(this, descriptor, extras);
486 }
487
Santos Cordon2174fb52014-05-29 08:22:56 -0700488 /**
489 * Takes a verified incoming call and uses the handle to lookup direct-to-voicemail property
490 * from the contacts provider. The call is not yet exposed to the user at this point and
491 * the result of the query will determine if the call is rejected or passed through to the
492 * in-call UI.
493 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700494 void handleVerifiedIncoming(ConnectionRequest request) {
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700495 mPhoneAccount = request.getAccount();
496
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700497 // We do not handle incoming calls immediately when they are verified by the connection
498 // service. We allow the caller-info-query code to execute first so that we can read the
Santos Cordon2174fb52014-05-29 08:22:56 -0700499 // direct-to-voicemail property before deciding if we want to show the incoming call to the
500 // user or if we want to reject the call.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700501 mDirectToVoicemailQueryPending = true;
Santos Cordon2174fb52014-05-29 08:22:56 -0700502
503 // Setting the handle triggers the caller info lookup code.
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700504 setHandle(request.getHandle());
Santos Cordon766d04f2014-05-06 10:28:25 -0700505
Santos Cordon2174fb52014-05-29 08:22:56 -0700506 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
507 // showing the user the incoming call screen.
508 mHandler.postDelayed(new Runnable() {
509 @Override
510 public void run() {
511 processDirectToVoicemail();
512 }
Santos Cordona1610702014-06-04 20:22:56 -0700513 }, Timeouts.getDirectToVoicemailMillis());
Santos Cordon2174fb52014-05-29 08:22:56 -0700514 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700515
Santos Cordon2174fb52014-05-29 08:22:56 -0700516 void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700517 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700518 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
519 Log.i(this, "Directing call to voicemail: %s.", this);
520 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
521 // will not need to set RINGING state prior to calling reject.
522 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700523 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700524 } else {
525 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
526 // the call state to RINGING.
527
528 // TODO(santoscordon): Replace this with state transition to RINGING.
529 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700530 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700531 }
532 }
533
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700534 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700535 }
536 }
537
538 void handleFailedIncoming() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700539 clearConnectionService();
Santos Cordon766d04f2014-05-06 10:28:25 -0700540
541 // TODO: Needs more specific disconnect error for this case.
542 setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED, null);
543 setState(CallState.DISCONNECTED);
544
545 // TODO(santoscordon): Replace this with state transitions related to "connecting".
546 for (Listener l : mListeners) {
547 l.onFailedIncomingCall(this);
548 }
549 }
550
551 /**
Santos Cordon682fe6b2014-05-20 08:56:39 -0700552 * Starts the outgoing call sequence. Upon completion, there should exist an active connection
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700553 * through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700554 */
555 void startOutgoing() {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700556 Preconditions.checkState(mOutgoingCallProcessor == null);
557
558 mOutgoingCallProcessor = new OutgoingCallProcessor(
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700559 this, Switchboard.getInstance().getCallServiceRepository(), this);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700560 mOutgoingCallProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700561 }
562
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700563 @Override
564 public void onOutgoingCallSuccess() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700565 // TODO(santoscordon): Replace this with state transitions related to "connecting".
566 for (Listener l : mListeners) {
567 l.onSuccessfulOutgoingCall(this);
568 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700569 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700570 }
571
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700572 @Override
573 public void onOutgoingCallFailure(int code, String msg) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700574 // TODO(santoscordon): Replace this with state transitions related to "connecting".
575 for (Listener l : mListeners) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700576 l.onFailedOutgoingCall(this, code, msg);
Santos Cordon766d04f2014-05-06 10:28:25 -0700577 }
Santos Cordon682fe6b2014-05-20 08:56:39 -0700578
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700579 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700580 mOutgoingCallProcessor = null;
581 }
582
583 @Override
584 public void onOutgoingCallCancel() {
585 // TODO(santoscordon): Replace this with state transitions related to "connecting".
586 for (Listener l : mListeners) {
587 l.onCancelledOutgoingCall(this);
588 }
589
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700590 clearConnectionService();
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700591 mOutgoingCallProcessor = null;
Santos Cordon766d04f2014-05-06 10:28:25 -0700592 }
593
594 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700595 * Plays the specified DTMF tone.
596 */
597 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700598 if (mConnectionService == null) {
599 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700600 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700601 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
602 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700603 }
604 }
605
606 /**
607 * Stops playing any currently playing DTMF tone.
608 */
609 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700610 if (mConnectionService == null) {
611 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700612 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700613 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
614 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700615 }
616 }
617
618 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700619 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800620 */
621 void disconnect() {
Santos Cordon766d04f2014-05-06 10:28:25 -0700622 if (mState == CallState.NEW) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700623 Log.v(this, "Aborting call %s", this);
624 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700625 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700626 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700627
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700628 Log.i(this, "Send disconnect to connection service for call: %s", this);
629 // The call isn't officially disconnected until the connection service confirms that the
630 // call was actually disconnected. Only then is the association between call and
631 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
632 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800633 }
634 }
635
Santos Cordon682fe6b2014-05-20 08:56:39 -0700636 void abort() {
637 if (mOutgoingCallProcessor != null) {
638 mOutgoingCallProcessor.abort();
639 }
640 }
641
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800642 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800643 * Answers the call if it is ringing.
644 */
645 void answer() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700646 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800647
648 // Check to verify that the call is still in the ringing state. A call can change states
649 // between the time the user hits 'answer' and Telecomm receives the command.
650 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700651 // At this point, we are asking the connection service to answer but we don't assume
652 // that it will work. Instead, we wait until confirmation from the connectino service
653 // that the call is in a non-RINGING state before changing the UI. See
654 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
655 mConnectionService.answer(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800656 }
657 }
658
659 /**
660 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700661 *
662 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
663 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800664 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700665 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700666 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800667
668 // Check to verify that the call is still in the ringing state. A call can change states
669 // between the time the user hits 'reject' and Telecomm receives the command.
670 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700671 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800672 }
673 }
674
675 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700676 * Puts the call on hold if it is currently active.
677 */
678 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700679 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700680
681 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700682 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700683 }
684 }
685
686 /**
687 * Releases the call from hold if it is currently active.
688 */
689 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700690 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700691
692 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700693 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700694 }
695 }
696
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700697 /** Checks if this is a live call or not. */
698 boolean isAlive() {
699 switch (mState) {
700 case NEW:
701 case RINGING:
702 case DISCONNECTED:
703 case ABORTED:
704 return false;
705 default:
706 return true;
707 }
708 }
709
Santos Cordon40f78c22014-04-07 02:11:42 -0700710 boolean isActive() {
711 switch (mState) {
712 case ACTIVE:
713 case POST_DIAL:
714 case POST_DIAL_WAIT:
715 return true;
716 default:
717 return false;
718 }
719 }
720
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700721 Bundle getExtras() {
722 return mExtras;
723 }
724
725 void setExtras(Bundle extras) {
726 mExtras = extras;
727 }
728
Santos Cordon5ba7f272014-05-28 13:59:49 -0700729 Uri getRingtone() {
730 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
731 }
732
Evan Charlton352105c2014-06-03 14:10:54 -0700733 void onPostDialWait(String remaining) {
734 for (Listener l : mListeners) {
735 l.onPostDialWait(this, remaining);
736 }
737 }
738
739 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700740 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700741 }
742
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700743 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700744 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700745 }
746
Santos Cordona1610702014-06-04 20:22:56 -0700747 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700748 if (mConnectionService == null) {
749 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700750 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700751 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700752 }
753 }
754
755 void expireConference() {
756 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700757 // connection service...so start shutting down.
758 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700759 for (Listener l : mListeners) {
760 l.onExpiredConferenceCall(this);
761 }
762 }
763
764 void confirmConference() {
765 Log.v(this, "confirming Conf call %s", mListeners);
766 for (Listener l : mListeners) {
767 l.onConfirmedConferenceCall(this);
768 }
769 }
770
771 void splitFromConference() {
772 // TODO(santoscordon): todo
773 }
774
775 void setParentCall(Call parentCall) {
776 if (parentCall == this) {
777 Log.e(this, new Exception(), "setting the parent to self");
778 return;
779 }
780 Preconditions.checkState(parentCall == null || mParentCall == null);
781
782 Call oldParent = mParentCall;
783 if (mParentCall != null) {
784 mParentCall.removeChildCall(this);
785 }
786 mParentCall = parentCall;
787 if (mParentCall != null) {
788 mParentCall.addChildCall(this);
789 }
790
791 for (Listener l : mListeners) {
792 l.onParentChanged(this);
793 }
794 }
795
796 private void addChildCall(Call call) {
797 if (!mChildCalls.contains(call)) {
798 mChildCalls.add(call);
799
800 for (Listener l : mListeners) {
801 l.onChildrenChanged(this);
802 }
803 }
804 }
805
806 private void removeChildCall(Call call) {
807 if (mChildCalls.remove(call)) {
808 for (Listener l : mListeners) {
809 l.onChildrenChanged(this);
810 }
811 }
812 }
813
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800814 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700815 * Return whether the user can respond to this {@code Call} via an SMS message.
816 *
817 * @return true if the "Respond via SMS" feature should be enabled
818 * for this incoming call.
819 *
820 * The general rule is that we *do* allow "Respond via SMS" except for
821 * the few (relatively rare) cases where we know for sure it won't
822 * work, namely:
823 * - a bogus or blank incoming number
824 * - a call from a SIP address
825 * - a "call presentation" that doesn't allow the number to be revealed
826 *
827 * In all other cases, we allow the user to respond via SMS.
828 *
829 * Note that this behavior isn't perfect; for example we have no way
830 * to detect whether the incoming call is from a landline (with most
831 * networks at least), so we still enable this feature even though
832 * SMSes to that number will silently fail.
833 */
834 boolean isRespondViaSmsCapable() {
835 if (mState != CallState.RINGING) {
836 return false;
837 }
838
839 if (getHandle() == null) {
840 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
841 // other words, the user should not be able to see the incoming phone number.
842 return false;
843 }
844
845 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
846 // The incoming number is actually a URI (i.e. a SIP address),
847 // not a regular PSTN phone number, and we can't send SMSes to
848 // SIP addresses.
849 // (TODO: That might still be possible eventually, though. Is
850 // there some SIP-specific equivalent to sending a text message?)
851 return false;
852 }
853
854 // Is there a valid SMS application on the phone?
855 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
856 true /*updateIfNeeded*/) == null) {
857 return false;
858 }
859
860 // TODO: with some carriers (in certain countries) you *can* actually
861 // tell whether a given number is a mobile phone or not. So in that
862 // case we could potentially return false here if the incoming call is
863 // from a land line.
864
865 // If none of the above special cases apply, it's OK to enable the
866 // "Respond via SMS" feature.
867 return true;
868 }
869
870 List<String> getCannedSmsResponses() {
871 return mCannedSmsResponses;
872 }
873
874 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800875 * @return True if the call is ringing, else logs the action name.
876 */
877 private boolean isRinging(String actionName) {
878 if (mState == CallState.RINGING) {
879 return true;
880 }
881
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800882 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800883 return false;
884 }
885
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800886 @SuppressWarnings("rawtypes")
887 private void decrementAssociatedCallCount(ServiceBinder binder) {
888 if (binder != null) {
889 binder.decrementAssociatedCallCount();
890 }
891 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700892
893 /**
894 * Looks up contact information based on the current handle.
895 */
896 private void startCallerInfoLookup() {
897 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
898
899 mQueryToken++; // Updated so that previous queries can no longer set the information.
900 mCallerInfo = null;
901 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700902 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700903 CallerInfoAsyncQuery.startQuery(
904 mQueryToken,
905 TelecommApp.getInstance(),
906 number,
907 sCallerInfoQueryListener,
908 this);
909 }
910 }
911
912 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700913 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700914 * that was made.
915 *
916 * @param callerInfo The new caller information to set.
917 * @param token The token used with this query.
918 */
919 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700920 Preconditions.checkNotNull(callerInfo);
921
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700922 if (mQueryToken == token) {
923 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -0700924 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700925
926 if (mCallerInfo.person_id != 0) {
927 Uri personUri =
928 ContentUris.withAppendedId(Contacts.CONTENT_URI, mCallerInfo.person_id);
929 Log.d(this, "Searching person uri %s for call %s", personUri, this);
930 ContactsAsyncHelper.startObtainPhotoAsync(
931 token,
932 TelecommApp.getInstance(),
933 personUri,
934 sPhotoLoadListener,
935 this);
Santos Cordon64c7e962014-07-02 15:15:27 -0700936 } else {
937 for (Listener l : mListeners) {
938 l.onCallerInfoChanged(this);
939 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700940 }
Santos Cordon2174fb52014-05-29 08:22:56 -0700941
942 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700943 }
944 }
945
946 /**
947 * Saves the specified photo information if the specified token matches that of the last query.
948 *
949 * @param photo The photo as a drawable.
950 * @param photoIcon The photo as a small icon.
951 * @param token The token used with this query.
952 */
953 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
954 if (mQueryToken == token) {
955 mCallerInfo.cachedPhoto = photo;
956 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -0700957
958 for (Listener l : mListeners) {
959 l.onCallerInfoChanged(this);
960 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700961 }
962 }
Ihab Awadff7493a2014-06-10 13:47:44 -0700963
964 private void maybeLoadCannedSmsResponses() {
965 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
966 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
967 mCannedSmsResponsesLoadingStarted = true;
968 RespondViaSmsManager.getInstance().loadCannedTextMessages(
969 new Response<Void, List<String>>() {
970 @Override
971 public void onResult(Void request, List<String>... result) {
972 if (result.length > 0) {
973 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
974 mCannedSmsResponses = result[0];
975 for (Listener l : mListeners) {
976 l.onCannedSmsResponsesLoaded(Call.this);
977 }
978 }
979 }
980
981 @Override
982 public void onError(Void request, int code, String msg) {
983 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
984 msg);
985 }
986 }
987 );
988 } else {
989 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
990 }
991 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700992
993 /**
994 * Sets speakerphone option on when call begins.
995 */
996 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
997 mSpeakerphoneOn = startWithSpeakerphone;
998 }
999
1000 /**
1001 * Returns speakerphone option.
1002 *
1003 * @return Whether or not speakerphone should be set automatically when call begins.
1004 */
1005 public boolean getStartWithSpeakerphoneOn() {
1006 return mSpeakerphoneOn;
1007 }
Andrew Leee9a77652014-06-26 13:07:57 -07001008
1009 /**
1010 * Sets a call video provider for the call.
1011 */
Nancy Chena65d41f2014-06-24 12:06:03 -07001012 public void setCallVideoProvider(ICallVideoProvider callVideoProvider) {
1013 mCallVideoProvider = callVideoProvider;
1014 for (Listener l : mListeners) {
1015 l.onCallVideoProviderChanged(Call.this);
1016 }
1017 }
1018
1019 /**
1020 * @return Return the call video Provider binder.
1021 */
1022 public ICallVideoProvider getCallVideoProvider() {
1023 return mCallVideoProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001024 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001025
1026 /**
1027 * Returns the features of this call.
1028 *
1029 * @return The features of this call.
1030 */
1031 public int getFeatures() {
1032 return mFeatures;
1033 }
1034
1035 /**
1036 * Set the features associated with the call and notify any listeners of the change.
1037 *
1038 * @param features The features.
1039 */
1040 public void setFeatures(int features) {
1041 Log.d(this, "setFeatures: %d", features);
1042 mFeatures = features;
1043 for (Listener l : mListeners) {
1044 l.onFeaturesChanged(Call.this);
1045 }
1046 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001047
1048 /**
1049 * The current video state for the call.
1050 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1051 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1052 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1053 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1054 *
1055 * @return True if video is enabled.
1056 */
1057 public int getVideoState() {
1058 return mVideoState;
1059 }
1060
1061 /**
1062 * At the start of the call, determines the desired video state for the call.
1063 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1064 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1065 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1066 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1067 *
1068 * @param videoState The desired video state for the call.
1069 */
1070 public void setVideoState(int videoState) {
1071 mVideoState = videoState;
1072 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001073
1074 public boolean getAudioModeIsVoip() {
1075 return mAudioModeIsVoip;
1076 }
1077
1078 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1079 mAudioModeIsVoip = audioModeIsVoip;
1080 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001081 l.onAudioModeIsVoipChanged(this);
1082 }
1083 }
1084
1085 public StatusHints getStatusHints() {
1086 return mStatusHints;
1087 }
1088
1089 public void setStatusHints(StatusHints statusHints) {
1090 mStatusHints = statusHints;
1091 for (Listener l : mListeners) {
1092 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001093 }
1094 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001095}