blob: b72f383bc284fdd533ec8a5dd871f3160027887e [file] [log] [blame]
Ben Gilad0407fb22014-01-09 16:18:41 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Sailesh Nepal9d58de52014-07-18 14:53:19 -070019import android.app.PendingIntent;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070020import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070022import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070023import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070024import android.os.Handler;
Santos Cordon12d61822014-07-29 16:02:20 -070025import android.telecomm.CallCapabilities;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070026import android.telecomm.CallPropertyPresentation;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080027import android.telecomm.CallState;
Santos Cordon72890ce2014-07-21 01:32:04 -070028import android.telecomm.Connection;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070029import android.telecomm.ConnectionRequest;
Santos Cordon12d61822014-07-29 16:02:20 -070030import android.telecomm.ConnectionService.VideoCallProvider;
Yorke Lee33501632014-03-17 19:24:12 -070031import android.telecomm.GatewayInfo;
Santos Cordon72890ce2014-07-21 01:32:04 -070032import android.telecomm.ParcelableConnection;
Ihab Awadb78b2762014-07-25 15:16:23 -070033import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070034import android.telecomm.PhoneAccountHandle;
Ihab Awadff7493a2014-06-10 13:47:44 -070035import android.telecomm.Response;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070036import android.telecomm.StatusHints;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070037import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070038import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070039import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080040
Andrew Lee3bcf9352014-07-23 12:36:05 -070041import com.android.internal.telecomm.IVideoCallProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070042import com.android.internal.telephony.CallerInfo;
43import com.android.internal.telephony.CallerInfoAsyncQuery;
44import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070045import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070046import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080047import com.google.common.base.Preconditions;
48
Santos Cordon12d61822014-07-29 16:02:20 -070049import java.util.ArrayList;
Ihab Awadff7493a2014-06-10 13:47:44 -070050import java.util.Collections;
Santos Cordona1610702014-06-04 20:22:56 -070051import java.util.LinkedList;
52import java.util.List;
Sailesh Nepal91990782014-03-08 17:45:52 -080053import java.util.Locale;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070054import java.util.Objects;
Ben Gilad61925612014-03-11 19:06:36 -070055import java.util.Set;
Santos Cordondfc66012014-07-15 13:41:40 -070056import java.util.concurrent.CopyOnWriteArraySet;
Ben Gilad0407fb22014-01-09 16:18:41 -080057
Ben Gilad2495d572014-01-09 17:26:19 -080058/**
59 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
60 * from the time the call intent was received by Telecomm (vs. the time the call was
61 * connected etc).
62 */
Sailesh Nepal664837f2014-07-14 16:31:51 -070063final class Call implements CreateConnectionResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070064 /**
65 * Listener for events on the call.
66 */
67 interface Listener {
68 void onSuccessfulOutgoingCall(Call call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -070069 void onFailedOutgoingCall(Call call, int errorCode, String errorMsg);
70 void onCancelledOutgoingCall(Call call);
Sailesh Nepalc92c4362014-07-04 18:33:21 -070071 void onSuccessfulIncomingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070072 void onFailedIncomingCall(Call call);
Ihab Awadcb387ac2014-05-28 16:49:38 -070073 void onRequestingRingback(Call call, boolean requestingRingback);
Evan Charlton352105c2014-06-03 14:10:54 -070074 void onPostDialWait(Call call, String remaining);
Sailesh Nepale20bc972014-07-09 21:22:36 -070075 void onCallCapabilitiesChanged(Call call);
Santos Cordona1610702014-06-04 20:22:56 -070076 void onExpiredConferenceCall(Call call);
77 void onConfirmedConferenceCall(Call call);
78 void onParentChanged(Call call);
79 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -070080 void onCannedSmsResponsesLoaded(Call call);
Andrew Lee3bcf9352014-07-23 12:36:05 -070081 void onVideoCallProviderChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070082 void onCallerInfoChanged(Call call);
Sailesh Nepal7e669572014-07-08 21:29:12 -070083 void onAudioModeIsVoipChanged(Call call);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070084 void onStatusHintsChanged(Call call);
Sailesh Nepale8ecb982014-07-11 17:19:42 -070085 void onHandleChanged(Call call);
86 void onCallerDisplayNameChanged(Call call);
Andrew Lee4a796602014-07-11 17:23:03 -070087 void onVideoStateChanged(Call call);
Sailesh Nepal9d58de52014-07-18 14:53:19 -070088 void onStartActivityFromInCall(Call call, PendingIntent intent);
Ihab Awadb78b2762014-07-25 15:16:23 -070089 void onTargetPhoneAccountChanged(Call call);
90 void onConnectionManagerPhoneAccountChanged(Call call);
Santos Cordon12d61822014-07-29 16:02:20 -070091 void onPhoneAccountChanged(Call call);
92 void onConferenceableCallsChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070093 }
94
95 abstract static class ListenerBase implements Listener {
96 @Override
97 public void onSuccessfulOutgoingCall(Call call) {}
98 @Override
99 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
100 @Override
101 public void onCancelledOutgoingCall(Call call) {}
102 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700103 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700104 @Override
105 public void onFailedIncomingCall(Call call) {}
106 @Override
107 public void onRequestingRingback(Call call, boolean requestingRingback) {}
108 @Override
109 public void onPostDialWait(Call call, String remaining) {}
110 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -0700111 public void onCallCapabilitiesChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700112 @Override
113 public void onExpiredConferenceCall(Call call) {}
114 @Override
115 public void onConfirmedConferenceCall(Call call) {}
116 @Override
117 public void onParentChanged(Call call) {}
118 @Override
119 public void onChildrenChanged(Call call) {}
120 @Override
121 public void onCannedSmsResponsesLoaded(Call call) {}
122 @Override
Andrew Lee3bcf9352014-07-23 12:36:05 -0700123 public void onVideoCallProviderChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700124 @Override
Santos Cordon64c7e962014-07-02 15:15:27 -0700125 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700126 @Override
127 public void onAudioModeIsVoipChanged(Call call) {}
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700128 @Override
129 public void onStatusHintsChanged(Call call) {}
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700130 @Override
131 public void onHandleChanged(Call call) {}
132 @Override
133 public void onCallerDisplayNameChanged(Call call) {}
Andrew Lee4a796602014-07-11 17:23:03 -0700134 @Override
135 public void onVideoStateChanged(Call call) {}
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700136 @Override
137 public void onStartActivityFromInCall(Call call, PendingIntent intent) {}
Ihab Awad69eb0f52014-07-18 11:20:37 -0700138 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700139 public void onTargetPhoneAccountChanged(Call call) {}
140 @Override
141 public void onConnectionManagerPhoneAccountChanged(Call call) {}
Santos Cordon12d61822014-07-29 16:02:20 -0700142 @Override
143 public void onPhoneAccountChanged(Call call) {}
144 @Override
145 public void onConferenceableCallsChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700146 }
147
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700148 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700149 new OnQueryCompleteListener() {
150 /** ${inheritDoc} */
151 @Override
152 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
153 if (cookie != null) {
154 ((Call) cookie).setCallerInfo(callerInfo, token);
155 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700156 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700157 };
158
159 private static final OnImageLoadCompleteListener sPhotoLoadListener =
160 new OnImageLoadCompleteListener() {
161 /** ${inheritDoc} */
162 @Override
163 public void onImageLoadComplete(
164 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
165 if (cookie != null) {
166 ((Call) cookie).setPhoto(photo, photoIcon, token);
167 }
168 }
169 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800170
Sailesh Nepal664837f2014-07-14 16:31:51 -0700171 private final Runnable mDirectToVoicemailRunnable = new Runnable() {
172 @Override
173 public void run() {
174 processDirectToVoicemail();
175 }
176 };
177
Sailesh Nepal810735e2014-03-18 18:15:46 -0700178 /** True if this is an incoming call. */
179 private final boolean mIsIncoming;
180
Ben Gilad0407fb22014-01-09 16:18:41 -0800181 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700182 * The time this call was created. Beyond logging and such, may also be used for bookkeeping
183 * and specifically for marking certain call attempts as failed attempts.
Ben Gilad0407fb22014-01-09 16:18:41 -0800184 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700185 private final long mCreationTimeMillis = System.currentTimeMillis();
186
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700187 /** The gateway information associated with this call. This stores the original call handle
188 * that the user is attempting to connect to via the gateway, the actual handle to dial in
189 * order to connect the call via the gateway, as well as the package name of the gateway
190 * service. */
191 private final GatewayInfo mGatewayInfo;
192
Ihab Awadb78b2762014-07-25 15:16:23 -0700193 private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
194
195 private PhoneAccountHandle mTargetPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700196
Santos Cordon2174fb52014-05-29 08:22:56 -0700197 private final Handler mHandler = new Handler();
198
Santos Cordon12d61822014-07-29 16:02:20 -0700199 private final List<Call> mConferenceableCalls = new ArrayList<>();
200
201 private PhoneAccountHandle mPhoneAccountHandle;
202
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700203 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800204
Santos Cordon61d0f702014-02-19 02:52:23 -0800205 /** The state of the call. */
206 private CallState mState;
207
208 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700209 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800210
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700211 /** The {@link CallPropertyPresentation} that controls how the handle is shown. */
212 private int mHandlePresentation;
213
214 /** The caller display name (CNAP) set by the connection service. */
215 private String mCallerDisplayName;
216
217 /** The {@link CallPropertyPresentation} that controls how the caller display name is shown. */
218 private int mCallerDisplayNamePresentation;
219
Ben Gilad0407fb22014-01-09 16:18:41 -0800220 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700221 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800222 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700223 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700224
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700225 private boolean mIsEmergencyCall;
226
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700227 private boolean mSpeakerphoneOn;
228
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700229 /**
230 * Tracks the video states which were applicable over the duration of a call.
231 * See {@link android.telecomm.VideoCallProfile} for a list of valid video states.
232 */
233 private int mVideoStateHistory;
234
Tyler Gunnc4abd912014-07-08 14:22:10 -0700235 private int mVideoState;
236
Ben Gilad61925612014-03-11 19:06:36 -0700237 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700238 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
239 * See {@link android.telephony.DisconnectCause}.
240 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700241 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700242
243 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700244 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700245 */
246 private String mDisconnectMessage;
247
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700248 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700249 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700250
Santos Cordon766d04f2014-05-06 10:28:25 -0700251 /** Set of listeners on this call. */
Santos Cordondfc66012014-07-15 13:41:40 -0700252 private Set<Listener> mListeners = new CopyOnWriteArraySet<>();
Santos Cordon766d04f2014-05-06 10:28:25 -0700253
Sailesh Nepal664837f2014-07-14 16:31:51 -0700254 private CreateConnectionProcessor mCreateConnectionProcessor;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700255
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700256 /** Caller information retrieved from the latest contact query. */
257 private CallerInfo mCallerInfo;
258
259 /** The latest token used with a contact info query. */
260 private int mQueryToken = 0;
261
Ihab Awadcb387ac2014-05-28 16:49:38 -0700262 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
263 private boolean mRequestingRingback = false;
264
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700265 /** Whether direct-to-voicemail query is pending. */
266 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700267
Sailesh Nepale20bc972014-07-09 21:22:36 -0700268 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700269
270 private boolean mIsConference = false;
271
272 private Call mParentCall = null;
273
274 private List<Call> mChildCalls = new LinkedList<>();
275
Ihab Awadff7493a2014-06-10 13:47:44 -0700276 /** Set of text message responses allowed for this call, if applicable. */
277 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
278
279 /** Whether an attempt has been made to load the text message responses. */
280 private boolean mCannedSmsResponsesLoadingStarted = false;
281
Andrew Lee3bcf9352014-07-23 12:36:05 -0700282 private IVideoCallProvider mVideoCallProvider;
Nancy Chena65d41f2014-06-24 12:06:03 -0700283
Sailesh Nepal7e669572014-07-08 21:29:12 -0700284 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700285 private StatusHints mStatusHints;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700286 private final ConnectionServiceRepository mRepository;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700287
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700288 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800289 * Persists the specified parameters and initializes the new instance.
290 *
291 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700292 * @param gatewayInfo Gateway information to use for the call.
Ihab Awadb78b2762014-07-25 15:16:23 -0700293 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
294 * This account must be one that was registered with the
295 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
296 * @param targetPhoneAccountHandle Account information to use for the call. This account must be
297 * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700298 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800299 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700300 Call(
301 ConnectionServiceRepository repository,
302 Uri handle,
303 GatewayInfo gatewayInfo,
Ihab Awadb78b2762014-07-25 15:16:23 -0700304 PhoneAccountHandle connectionManagerPhoneAccountHandle,
305 PhoneAccountHandle targetPhoneAccountHandle,
Sailesh Nepal664837f2014-07-14 16:31:51 -0700306 boolean isIncoming,
307 boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700308 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700309 mRepository = repository;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700310 setHandle(handle, CallPropertyPresentation.ALLOWED);
Yorke Lee33501632014-03-17 19:24:12 -0700311 mGatewayInfo = gatewayInfo;
Ihab Awadb78b2762014-07-25 15:16:23 -0700312 mConnectionManagerPhoneAccountHandle = connectionManagerPhoneAccountHandle;
313 mTargetPhoneAccountHandle = targetPhoneAccountHandle;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700314 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700315 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700316 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800317 }
318
Santos Cordon766d04f2014-05-06 10:28:25 -0700319 void addListener(Listener listener) {
320 mListeners.add(listener);
321 }
322
323 void removeListener(Listener listener) {
324 mListeners.remove(listener);
325 }
326
Santos Cordon61d0f702014-02-19 02:52:23 -0800327 /** {@inheritDoc} */
Santos Cordon12d61822014-07-29 16:02:20 -0700328 @Override
329 public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700330 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700331 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
332 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700333 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700334
335 return String.format(Locale.US, "[%s, %s, %s, %d]", mState, component,
336 Log.piiHandle(mHandle), getVideoState());
Santos Cordon61d0f702014-02-19 02:52:23 -0800337 }
338
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800339 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700340 if (mIsConference) {
341 if (!mChildCalls.isEmpty()) {
342 // If we have child calls, just return the child call.
343 return mChildCalls.get(0).getState();
344 }
345 return CallState.ACTIVE;
346 } else {
347 return mState;
348 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800349 }
350
351 /**
352 * Sets the call state. Although there exists the notion of appropriate state transitions
353 * (see {@link CallState}), in practice those expectations break down when cellular systems
354 * misbehave and they do this very often. The result is that we do not enforce state transitions
355 * and instead keep the code resilient to unexpected state changes.
356 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700357 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700358 Preconditions.checkState(newState != CallState.DISCONNECTED ||
359 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700360 if (mState != newState) {
361 Log.v(this, "setState %s -> %s", mState, newState);
362 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700363 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700364 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800365 }
366
Ihab Awadcb387ac2014-05-28 16:49:38 -0700367 void setRequestingRingback(boolean requestingRingback) {
368 mRequestingRingback = requestingRingback;
369 for (Listener l : mListeners) {
370 l.onRequestingRingback(this, mRequestingRingback);
371 }
372 }
373
374 boolean isRequestingRingback() {
375 return mRequestingRingback;
376 }
377
Sailesh Nepalce704b92014-03-17 18:31:43 -0700378 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800379 return mHandle;
380 }
381
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700382 int getHandlePresentation() {
383 return mHandlePresentation;
384 }
385
386 void setHandle(Uri handle, int presentation) {
387 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700388 mHandle = handle;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700389 mHandlePresentation = presentation;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700390 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700391 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700392 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700393 for (Listener l : mListeners) {
394 l.onHandleChanged(this);
395 }
396 }
397 }
398
399 String getCallerDisplayName() {
400 return mCallerDisplayName;
401 }
402
403 int getCallerDisplayNamePresentation() {
404 return mCallerDisplayNamePresentation;
405 }
406
407 void setCallerDisplayName(String callerDisplayName, int presentation) {
408 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
409 presentation != mCallerDisplayNamePresentation) {
410 mCallerDisplayName = callerDisplayName;
411 mCallerDisplayNamePresentation = presentation;
412 for (Listener l : mListeners) {
413 l.onCallerDisplayNameChanged(this);
414 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700415 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700416 }
417
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700418 String getName() {
419 return mCallerInfo == null ? null : mCallerInfo.name;
420 }
421
422 Bitmap getPhotoIcon() {
423 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
424 }
425
426 Drawable getPhoto() {
427 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
428 }
429
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700430 /**
431 * @param disconnectCause The reason for the disconnection, any of
432 * {@link android.telephony.DisconnectCause}.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700433 * @param disconnectMessage Optional message about the disconnect.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700434 */
435 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
436 // TODO: Consider combining this method with a setDisconnected() method that is totally
437 // separate from setState.
438 mDisconnectCause = disconnectCause;
439 mDisconnectMessage = disconnectMessage;
440 }
441
442 int getDisconnectCause() {
443 return mDisconnectCause;
444 }
445
446 String getDisconnectMessage() {
447 return mDisconnectMessage;
448 }
449
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700450 boolean isEmergencyCall() {
451 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800452 }
453
Yorke Lee33501632014-03-17 19:24:12 -0700454 /**
455 * @return The original handle this call is associated with. In-call services should use this
456 * handle when indicating in their UI the handle that is being called.
457 */
458 public Uri getOriginalHandle() {
459 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
460 return mGatewayInfo.getOriginalHandle();
461 }
462 return getHandle();
463 }
464
465 GatewayInfo getGatewayInfo() {
466 return mGatewayInfo;
467 }
468
Ihab Awadb78b2762014-07-25 15:16:23 -0700469 PhoneAccountHandle getConnectionManagerPhoneAccount() {
470 return mConnectionManagerPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700471 }
472
Ihab Awadb78b2762014-07-25 15:16:23 -0700473 void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
474 if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
475 mConnectionManagerPhoneAccountHandle = accountHandle;
Ihab Awad69eb0f52014-07-18 11:20:37 -0700476 for (Listener l : mListeners) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700477 l.onConnectionManagerPhoneAccountChanged(this);
478 }
479 }
480
481 }
482
483 PhoneAccountHandle getTargetPhoneAccount() {
484 return mTargetPhoneAccountHandle;
485 }
486
487 void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
488 if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
489 mTargetPhoneAccountHandle = accountHandle;
490 for (Listener l : mListeners) {
491 l.onTargetPhoneAccountChanged(this);
Ihab Awad69eb0f52014-07-18 11:20:37 -0700492 }
493 }
Nancy Chen53ceedc2014-07-08 18:56:51 -0700494 }
495
Sailesh Nepal810735e2014-03-18 18:15:46 -0700496 boolean isIncoming() {
497 return mIsIncoming;
498 }
499
Ben Gilad0407fb22014-01-09 16:18:41 -0800500 /**
501 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700502 * period since this call was added to the set pending outgoing calls, see
503 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800504 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700505 long getAgeMillis() {
506 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800507 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800508
Yorke Leef98fb572014-03-05 10:56:55 -0800509 /**
510 * @return The time when this call object was created and added to the set of pending outgoing
511 * calls.
512 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700513 long getCreationTimeMillis() {
514 return mCreationTimeMillis;
515 }
516
517 long getConnectTimeMillis() {
518 return mConnectTimeMillis;
519 }
520
521 void setConnectTimeMillis(long connectTimeMillis) {
522 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800523 }
524
Sailesh Nepale20bc972014-07-09 21:22:36 -0700525 int getCallCapabilities() {
526 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700527 }
528
Sailesh Nepale20bc972014-07-09 21:22:36 -0700529 void setCallCapabilities(int callCapabilities) {
Santos Cordon12d61822014-07-29 16:02:20 -0700530 Log.v(this, "setCallCapabilities: %s", CallCapabilities.toString(callCapabilities));
Sailesh Nepale20bc972014-07-09 21:22:36 -0700531 if (mCallCapabilities != callCapabilities) {
Santos Cordon12d61822014-07-29 16:02:20 -0700532 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700533 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700534 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700535 }
536 }
537 }
538
539 Call getParentCall() {
540 return mParentCall;
541 }
542
543 List<Call> getChildCalls() {
544 return mChildCalls;
545 }
546
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700547 ConnectionServiceWrapper getConnectionService() {
548 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800549 }
550
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700551 void setConnectionService(ConnectionServiceWrapper service) {
552 Preconditions.checkNotNull(service);
553
554 clearConnectionService();
555
556 service.incrementAssociatedCallCount();
557 mConnectionService = service;
558 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700559 }
560
561 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700562 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700563 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700564 void clearConnectionService() {
565 if (mConnectionService != null) {
566 ConnectionServiceWrapper serviceTemp = mConnectionService;
567 mConnectionService = null;
568 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700569
570 // Decrementing the count can cause the service to unbind, which itself can trigger the
571 // service-death code. Since the service death code tries to clean up any associated
572 // calls, we need to make sure to remove that information (e.g., removeCall()) before
573 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700574 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
575 // to do.
576 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700577 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800578 }
579
Sailesh Nepal664837f2014-07-14 16:31:51 -0700580 private void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700581 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700582 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
583 Log.i(this, "Directing call to voicemail: %s.", this);
584 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
585 // will not need to set RINGING state prior to calling reject.
586 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700587 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700588 } else {
589 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
590 // the call state to RINGING.
591
592 // TODO(santoscordon): Replace this with state transition to RINGING.
593 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700594 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700595 }
596 }
597
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700598 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700599 }
600 }
601
Santos Cordon766d04f2014-05-06 10:28:25 -0700602 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700603 * Starts the create connection sequence. Upon completion, there should exist an active
604 * connection through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700605 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700606 void startCreateConnection() {
607 Preconditions.checkState(mCreateConnectionProcessor == null);
608 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
609 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700610 }
611
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700612 @Override
Santos Cordon72890ce2014-07-21 01:32:04 -0700613 public void handleCreateConnectionSuccessful(
614 ConnectionRequest request, ParcelableConnection connection) {
Santos Cordon12d61822014-07-29 16:02:20 -0700615 Log.v(this, "handleCreateConnectionSuccessful %s", connection);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700616 mCreateConnectionProcessor = null;
Santos Cordon72890ce2014-07-21 01:32:04 -0700617 setState(getStateFromConnectionState(connection.getState()));
Ihab Awadb78b2762014-07-25 15:16:23 -0700618 setTargetPhoneAccount(connection.getPhoneAccount());
Santos Cordon72890ce2014-07-21 01:32:04 -0700619 setHandle(connection.getHandle(), connection.getHandlePresentation());
620 setCallerDisplayName(
621 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
Andrew Lee3bcf9352014-07-23 12:36:05 -0700622
623 setVideoCallProvider(connection.getVideoCallProvider());
Tyler Gunnb1a95a72014-07-23 14:08:19 -0700624 setVideoState(connection.getVideoState());
Sailesh Nepal664837f2014-07-14 16:31:51 -0700625
626 if (mIsIncoming) {
627 // We do not handle incoming calls immediately when they are verified by the connection
628 // service. We allow the caller-info-query code to execute first so that we can read the
629 // direct-to-voicemail property before deciding if we want to show the incoming call to
630 // the user or if we want to reject the call.
631 mDirectToVoicemailQueryPending = true;
632
Sailesh Nepal664837f2014-07-14 16:31:51 -0700633 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
634 // showing the user the incoming call screen.
635 mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
636 } else {
637 for (Listener l : mListeners) {
638 l.onSuccessfulOutgoingCall(this);
639 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700640 }
641 }
642
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700643 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700644 public void handleCreateConnectionFailed(int code, String msg) {
645 mCreateConnectionProcessor = null;
646 if (mIsIncoming) {
647 clearConnectionService();
648 setDisconnectCause(code, null);
649 setState(CallState.DISCONNECTED);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700650
Sailesh Nepal664837f2014-07-14 16:31:51 -0700651 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
652 for (int i = 0; i < listeners.length; i++) {
653 listeners[i].onFailedIncomingCall(this);
654 }
655 } else {
656 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
657 for (int i = 0; i < listeners.length; i++) {
658 listeners[i].onFailedOutgoingCall(this, code, msg);
659 }
660 clearConnectionService();
661 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700662 }
663
664 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700665 public void handleCreateConnectionCancelled() {
666 mCreateConnectionProcessor = null;
667 if (mIsIncoming) {
668 clearConnectionService();
Santos Cordonfd6ca442014-07-24 15:34:01 -0700669 setDisconnectCause(DisconnectCause.OUTGOING_CANCELED, null);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700670 setState(CallState.DISCONNECTED);
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700671
Sailesh Nepal664837f2014-07-14 16:31:51 -0700672 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
673 for (int i = 0; i < listeners.length; i++) {
674 listeners[i].onFailedIncomingCall(this);
675 }
676 } else {
677 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
678 for (int i = 0; i < listeners.length; i++) {
679 listeners[i].onCancelledOutgoingCall(this);
680 }
681 clearConnectionService();
682 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700683 }
684
685 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700686 * Plays the specified DTMF tone.
687 */
688 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700689 if (mConnectionService == null) {
690 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700691 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700692 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
693 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700694 }
695 }
696
697 /**
698 * Stops playing any currently playing DTMF tone.
699 */
700 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700701 if (mConnectionService == null) {
702 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700703 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700704 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
705 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700706 }
707 }
708
709 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700710 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800711 */
712 void disconnect() {
Nancy Chen53ceedc2014-07-08 18:56:51 -0700713 if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700714 Log.v(this, "Aborting call %s", this);
715 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700716 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700717 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700718
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700719 Log.i(this, "Send disconnect to connection service for call: %s", this);
720 // The call isn't officially disconnected until the connection service confirms that the
721 // call was actually disconnected. Only then is the association between call and
722 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
723 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800724 }
725 }
726
Santos Cordon682fe6b2014-05-20 08:56:39 -0700727 void abort() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700728 if (mCreateConnectionProcessor != null) {
729 mCreateConnectionProcessor.abort();
Nancy Chen53ceedc2014-07-08 18:56:51 -0700730 } else if (mState == CallState.PRE_DIAL_WAIT) {
731 handleCreateConnectionFailed(DisconnectCause.LOCAL, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700732 }
733 }
734
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800735 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800736 * Answers the call if it is ringing.
Andrew Lee38931d02014-07-16 10:17:36 -0700737 *
738 * @param videoState The video state in which to answer the call.
Santos Cordon61d0f702014-02-19 02:52:23 -0800739 */
Andrew Lee38931d02014-07-16 10:17:36 -0700740 void answer(int videoState) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700741 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800742
743 // Check to verify that the call is still in the ringing state. A call can change states
744 // between the time the user hits 'answer' and Telecomm receives the command.
745 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700746 // At this point, we are asking the connection service to answer but we don't assume
747 // that it will work. Instead, we wait until confirmation from the connectino service
748 // that the call is in a non-RINGING state before changing the UI. See
749 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
Andrew Lee38931d02014-07-16 10:17:36 -0700750 mConnectionService.answer(this, videoState);
Santos Cordon61d0f702014-02-19 02:52:23 -0800751 }
752 }
753
754 /**
755 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700756 *
757 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
758 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800759 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700760 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700761 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800762
763 // Check to verify that the call is still in the ringing state. A call can change states
764 // between the time the user hits 'reject' and Telecomm receives the command.
765 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700766 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800767 }
768 }
769
770 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700771 * Puts the call on hold if it is currently active.
772 */
773 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700774 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700775
776 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700777 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700778 }
779 }
780
781 /**
782 * Releases the call from hold if it is currently active.
783 */
784 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700785 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700786
787 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700788 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700789 }
790 }
791
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700792 /** Checks if this is a live call or not. */
793 boolean isAlive() {
794 switch (mState) {
795 case NEW:
796 case RINGING:
797 case DISCONNECTED:
798 case ABORTED:
799 return false;
800 default:
801 return true;
802 }
803 }
804
Santos Cordon40f78c22014-04-07 02:11:42 -0700805 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -0700806 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -0700807 }
808
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700809 Bundle getExtras() {
810 return mExtras;
811 }
812
813 void setExtras(Bundle extras) {
814 mExtras = extras;
815 }
816
Santos Cordon5ba7f272014-05-28 13:59:49 -0700817 Uri getRingtone() {
818 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
819 }
820
Evan Charlton352105c2014-06-03 14:10:54 -0700821 void onPostDialWait(String remaining) {
822 for (Listener l : mListeners) {
823 l.onPostDialWait(this, remaining);
824 }
825 }
826
827 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700828 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700829 }
830
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700831 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700832 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700833 }
834
Santos Cordona1610702014-06-04 20:22:56 -0700835 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700836 if (mConnectionService == null) {
837 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700838 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700839 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700840 }
841 }
842
843 void expireConference() {
844 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700845 // connection service...so start shutting down.
846 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700847 for (Listener l : mListeners) {
848 l.onExpiredConferenceCall(this);
849 }
850 }
851
852 void confirmConference() {
853 Log.v(this, "confirming Conf call %s", mListeners);
854 for (Listener l : mListeners) {
855 l.onConfirmedConferenceCall(this);
856 }
857 }
858
859 void splitFromConference() {
860 // TODO(santoscordon): todo
861 }
862
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700863 void swapWithBackgroundCall() {
864 mConnectionService.swapWithBackgroundCall(this);
865 }
866
Santos Cordona1610702014-06-04 20:22:56 -0700867 void setParentCall(Call parentCall) {
868 if (parentCall == this) {
869 Log.e(this, new Exception(), "setting the parent to self");
870 return;
871 }
872 Preconditions.checkState(parentCall == null || mParentCall == null);
873
874 Call oldParent = mParentCall;
875 if (mParentCall != null) {
876 mParentCall.removeChildCall(this);
877 }
878 mParentCall = parentCall;
879 if (mParentCall != null) {
880 mParentCall.addChildCall(this);
881 }
882
883 for (Listener l : mListeners) {
884 l.onParentChanged(this);
885 }
886 }
887
Santos Cordon12d61822014-07-29 16:02:20 -0700888 void setConferenceableCalls(List<Call> conferenceableCalls) {
889 mConferenceableCalls.clear();
890 mConferenceableCalls.addAll(conferenceableCalls);
891 }
892
893 List<Call> getConferenceableCalls() {
894 return mConferenceableCalls;
895 }
896
Santos Cordona1610702014-06-04 20:22:56 -0700897 private void addChildCall(Call call) {
898 if (!mChildCalls.contains(call)) {
899 mChildCalls.add(call);
900
901 for (Listener l : mListeners) {
902 l.onChildrenChanged(this);
903 }
904 }
905 }
906
907 private void removeChildCall(Call call) {
908 if (mChildCalls.remove(call)) {
909 for (Listener l : mListeners) {
910 l.onChildrenChanged(this);
911 }
912 }
913 }
914
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800915 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700916 * Return whether the user can respond to this {@code Call} via an SMS message.
917 *
918 * @return true if the "Respond via SMS" feature should be enabled
919 * for this incoming call.
920 *
921 * The general rule is that we *do* allow "Respond via SMS" except for
922 * the few (relatively rare) cases where we know for sure it won't
923 * work, namely:
924 * - a bogus or blank incoming number
925 * - a call from a SIP address
926 * - a "call presentation" that doesn't allow the number to be revealed
927 *
928 * In all other cases, we allow the user to respond via SMS.
929 *
930 * Note that this behavior isn't perfect; for example we have no way
931 * to detect whether the incoming call is from a landline (with most
932 * networks at least), so we still enable this feature even though
933 * SMSes to that number will silently fail.
934 */
935 boolean isRespondViaSmsCapable() {
936 if (mState != CallState.RINGING) {
937 return false;
938 }
939
940 if (getHandle() == null) {
941 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
942 // other words, the user should not be able to see the incoming phone number.
943 return false;
944 }
945
946 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
947 // The incoming number is actually a URI (i.e. a SIP address),
948 // not a regular PSTN phone number, and we can't send SMSes to
949 // SIP addresses.
950 // (TODO: That might still be possible eventually, though. Is
951 // there some SIP-specific equivalent to sending a text message?)
952 return false;
953 }
954
955 // Is there a valid SMS application on the phone?
956 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
957 true /*updateIfNeeded*/) == null) {
958 return false;
959 }
960
961 // TODO: with some carriers (in certain countries) you *can* actually
962 // tell whether a given number is a mobile phone or not. So in that
963 // case we could potentially return false here if the incoming call is
964 // from a land line.
965
966 // If none of the above special cases apply, it's OK to enable the
967 // "Respond via SMS" feature.
968 return true;
969 }
970
971 List<String> getCannedSmsResponses() {
972 return mCannedSmsResponses;
973 }
974
975 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800976 * @return True if the call is ringing, else logs the action name.
977 */
978 private boolean isRinging(String actionName) {
979 if (mState == CallState.RINGING) {
980 return true;
981 }
982
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800983 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800984 return false;
985 }
986
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800987 @SuppressWarnings("rawtypes")
988 private void decrementAssociatedCallCount(ServiceBinder binder) {
989 if (binder != null) {
990 binder.decrementAssociatedCallCount();
991 }
992 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700993
994 /**
995 * Looks up contact information based on the current handle.
996 */
997 private void startCallerInfoLookup() {
998 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
999
1000 mQueryToken++; // Updated so that previous queries can no longer set the information.
1001 mCallerInfo = null;
1002 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -07001003 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001004 CallerInfoAsyncQuery.startQuery(
1005 mQueryToken,
1006 TelecommApp.getInstance(),
1007 number,
1008 sCallerInfoQueryListener,
1009 this);
1010 }
1011 }
1012
1013 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001014 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001015 * that was made.
1016 *
1017 * @param callerInfo The new caller information to set.
1018 * @param token The token used with this query.
1019 */
1020 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001021 Preconditions.checkNotNull(callerInfo);
1022
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001023 if (mQueryToken == token) {
1024 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -07001025 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001026
Makoto Onukia1662d02014-07-10 15:31:59 -07001027 if (mCallerInfo.contactDisplayPhotoUri != null) {
1028 Log.d(this, "Searching person uri %s for call %s",
1029 mCallerInfo.contactDisplayPhotoUri, this);
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001030 ContactsAsyncHelper.startObtainPhotoAsync(
1031 token,
1032 TelecommApp.getInstance(),
Makoto Onukia1662d02014-07-10 15:31:59 -07001033 mCallerInfo.contactDisplayPhotoUri,
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001034 sPhotoLoadListener,
1035 this);
Makoto Onukia1662d02014-07-10 15:31:59 -07001036 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
Santos Cordon64c7e962014-07-02 15:15:27 -07001037 } else {
1038 for (Listener l : mListeners) {
1039 l.onCallerInfoChanged(this);
1040 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001041 }
Santos Cordon2174fb52014-05-29 08:22:56 -07001042
1043 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001044 }
1045 }
1046
Yorke Lee6f3f7af2014-07-11 10:59:46 -07001047 CallerInfo getCallerInfo() {
1048 return mCallerInfo;
1049 }
1050
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001051 /**
1052 * Saves the specified photo information if the specified token matches that of the last query.
1053 *
1054 * @param photo The photo as a drawable.
1055 * @param photoIcon The photo as a small icon.
1056 * @param token The token used with this query.
1057 */
1058 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1059 if (mQueryToken == token) {
1060 mCallerInfo.cachedPhoto = photo;
1061 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -07001062
1063 for (Listener l : mListeners) {
1064 l.onCallerInfoChanged(this);
1065 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001066 }
1067 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001068
1069 private void maybeLoadCannedSmsResponses() {
1070 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1071 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1072 mCannedSmsResponsesLoadingStarted = true;
1073 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1074 new Response<Void, List<String>>() {
1075 @Override
1076 public void onResult(Void request, List<String>... result) {
1077 if (result.length > 0) {
1078 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1079 mCannedSmsResponses = result[0];
1080 for (Listener l : mListeners) {
1081 l.onCannedSmsResponsesLoaded(Call.this);
1082 }
1083 }
1084 }
1085
1086 @Override
1087 public void onError(Void request, int code, String msg) {
1088 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1089 msg);
1090 }
1091 }
1092 );
1093 } else {
1094 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1095 }
1096 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001097
1098 /**
1099 * Sets speakerphone option on when call begins.
1100 */
1101 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1102 mSpeakerphoneOn = startWithSpeakerphone;
1103 }
1104
1105 /**
1106 * Returns speakerphone option.
1107 *
1108 * @return Whether or not speakerphone should be set automatically when call begins.
1109 */
1110 public boolean getStartWithSpeakerphoneOn() {
1111 return mSpeakerphoneOn;
1112 }
Andrew Leee9a77652014-06-26 13:07:57 -07001113
1114 /**
Andrew Lee3bcf9352014-07-23 12:36:05 -07001115 * Sets a video call provider for the call.
Andrew Leee9a77652014-06-26 13:07:57 -07001116 */
Andrew Lee3bcf9352014-07-23 12:36:05 -07001117 public void setVideoCallProvider(IVideoCallProvider videoCallProvider) {
1118 mVideoCallProvider = videoCallProvider;
Nancy Chena65d41f2014-06-24 12:06:03 -07001119 for (Listener l : mListeners) {
Andrew Lee3bcf9352014-07-23 12:36:05 -07001120 l.onVideoCallProviderChanged(Call.this);
Nancy Chena65d41f2014-06-24 12:06:03 -07001121 }
1122 }
1123
1124 /**
Andrew Lee3bcf9352014-07-23 12:36:05 -07001125 * @return Return the {@link VideoCallProvider} binder.
Nancy Chena65d41f2014-06-24 12:06:03 -07001126 */
Andrew Lee3bcf9352014-07-23 12:36:05 -07001127 public IVideoCallProvider getVideoCallProvider() {
1128 return mVideoCallProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001129 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001130
1131 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07001132 * The current video state for the call.
1133 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1134 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1135 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1136 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1137 *
1138 * @return True if video is enabled.
1139 */
1140 public int getVideoState() {
1141 return mVideoState;
1142 }
1143
1144 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001145 * Returns the video states which were applicable over the duration of a call.
1146 * See {@link android.telecomm.VideoCallProfile} for a list of valid video states.
1147 *
1148 * @return The video states applicable over the duration of the call.
1149 */
1150 public int getVideoStateHistory() {
1151 return mVideoStateHistory;
1152 }
1153
1154 /**
1155 * Determines the current video state for the call.
1156 * For an outgoing call determines the desired video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001157 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1158 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1159 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1160 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1161 *
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001162 * @param videoState The video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001163 */
1164 public void setVideoState(int videoState) {
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001165 // Track which video states were applicable over the duration of the call.
1166 mVideoStateHistory = mVideoStateHistory | videoState;
1167
Tyler Gunnc4abd912014-07-08 14:22:10 -07001168 mVideoState = videoState;
Andrew Lee4a796602014-07-11 17:23:03 -07001169 for (Listener l : mListeners) {
1170 l.onVideoStateChanged(this);
1171 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001172 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001173
1174 public boolean getAudioModeIsVoip() {
1175 return mAudioModeIsVoip;
1176 }
1177
1178 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1179 mAudioModeIsVoip = audioModeIsVoip;
1180 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001181 l.onAudioModeIsVoipChanged(this);
1182 }
1183 }
1184
1185 public StatusHints getStatusHints() {
1186 return mStatusHints;
1187 }
1188
1189 public void setStatusHints(StatusHints statusHints) {
1190 mStatusHints = statusHints;
1191 for (Listener l : mListeners) {
1192 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001193 }
1194 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -07001195
1196 public void startActivityFromInCall(PendingIntent intent) {
1197 if (intent.isActivity()) {
1198 for (Listener l : mListeners) {
1199 l.onStartActivityFromInCall(this, intent);
1200 }
1201 } else {
1202 Log.w(this, "startActivityFromInCall, activity intent required");
1203 }
1204 }
Santos Cordon72890ce2014-07-21 01:32:04 -07001205
1206 private CallState getStateFromConnectionState(int state) {
1207 switch (state) {
1208 case Connection.State.ACTIVE:
1209 return CallState.ACTIVE;
1210 case Connection.State.DIALING:
1211 return CallState.DIALING;
1212 case Connection.State.DISCONNECTED:
1213 return CallState.DISCONNECTED;
1214 case Connection.State.HOLDING:
1215 return CallState.ON_HOLD;
1216 case Connection.State.NEW:
1217 return CallState.NEW;
1218 case Connection.State.RINGING:
1219 return CallState.RINGING;
1220 }
1221 return CallState.DISCONNECTED;
1222 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001223}