blob: d6373a158e2bb3fe11c007cfa13852484be01e29 [file] [log] [blame]
Ben Gilad0407fb22014-01-09 16:18:41 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Sailesh Nepal9d58de52014-07-18 14:53:19 -070019import android.app.PendingIntent;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070020import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
Sailesh Nepalce704b92014-03-17 18:31:43 -070022import android.net.Uri;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -070023import android.os.Bundle;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070024import android.os.Handler;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070025import android.telecomm.CallPropertyPresentation;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080026import android.telecomm.CallState;
Santos Cordon72890ce2014-07-21 01:32:04 -070027import android.telecomm.Connection;
Sailesh Nepalc92c4362014-07-04 18:33:21 -070028import android.telecomm.ConnectionRequest;
Yorke Lee33501632014-03-17 19:24:12 -070029import android.telecomm.GatewayInfo;
Santos Cordon72890ce2014-07-21 01:32:04 -070030import android.telecomm.ParcelableConnection;
Ihab Awadb78b2762014-07-25 15:16:23 -070031import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070032import android.telecomm.PhoneAccountHandle;
Ihab Awadff7493a2014-06-10 13:47:44 -070033import android.telecomm.Response;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070034import android.telecomm.StatusHints;
Santos Cordon79ff2bc2014-03-27 15:31:27 -070035import android.telephony.DisconnectCause;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070036import android.telephony.PhoneNumberUtils;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070037import android.text.TextUtils;
Santos Cordon0b03b4b2014-01-29 18:01:59 -080038
Andrew Lee3bcf9352014-07-23 12:36:05 -070039import com.android.internal.telecomm.IVideoCallProvider;
Santos Cordonfd71f4a2014-05-28 13:59:49 -070040import com.android.internal.telephony.CallerInfo;
41import com.android.internal.telephony.CallerInfoAsyncQuery;
42import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
Ihab Awadff7493a2014-06-10 13:47:44 -070043import com.android.internal.telephony.SmsApplication;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070044import com.android.telecomm.ContactsAsyncHelper.OnImageLoadCompleteListener;
Santos Cordon61d0f702014-02-19 02:52:23 -080045import com.google.common.base.Preconditions;
46
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;
Sailesh Nepale8ecb982014-07-11 17:19:42 -070051import java.util.Objects;
Ben Gilad61925612014-03-11 19:06:36 -070052import java.util.Set;
Santos Cordondfc66012014-07-15 13:41:40 -070053import java.util.concurrent.CopyOnWriteArraySet;
Ben Gilad0407fb22014-01-09 16:18:41 -080054
Ben Gilad2495d572014-01-09 17:26:19 -080055/**
56 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting
57 * from the time the call intent was received by Telecomm (vs. the time the call was
58 * connected etc).
59 */
Sailesh Nepal664837f2014-07-14 16:31:51 -070060final class Call implements CreateConnectionResponse {
Santos Cordon766d04f2014-05-06 10:28:25 -070061 /**
62 * Listener for events on the call.
63 */
64 interface Listener {
65 void onSuccessfulOutgoingCall(Call call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -070066 void onFailedOutgoingCall(Call call, int errorCode, String errorMsg);
67 void onCancelledOutgoingCall(Call call);
Sailesh Nepalc92c4362014-07-04 18:33:21 -070068 void onSuccessfulIncomingCall(Call call);
Santos Cordon766d04f2014-05-06 10:28:25 -070069 void onFailedIncomingCall(Call call);
Ihab Awadcb387ac2014-05-28 16:49:38 -070070 void onRequestingRingback(Call call, boolean requestingRingback);
Evan Charlton352105c2014-06-03 14:10:54 -070071 void onPostDialWait(Call call, String remaining);
Sailesh Nepale20bc972014-07-09 21:22:36 -070072 void onCallCapabilitiesChanged(Call call);
Santos Cordona1610702014-06-04 20:22:56 -070073 void onExpiredConferenceCall(Call call);
74 void onConfirmedConferenceCall(Call call);
75 void onParentChanged(Call call);
76 void onChildrenChanged(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -070077 void onCannedSmsResponsesLoaded(Call call);
Andrew Lee3bcf9352014-07-23 12:36:05 -070078 void onVideoCallProviderChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070079 void onCallerInfoChanged(Call call);
Sailesh Nepal7e669572014-07-08 21:29:12 -070080 void onAudioModeIsVoipChanged(Call call);
Sailesh Nepal35faf8c2014-07-08 22:02:34 -070081 void onStatusHintsChanged(Call call);
Sailesh Nepale8ecb982014-07-11 17:19:42 -070082 void onHandleChanged(Call call);
83 void onCallerDisplayNameChanged(Call call);
Andrew Lee4a796602014-07-11 17:23:03 -070084 void onVideoStateChanged(Call call);
Sailesh Nepal9d58de52014-07-18 14:53:19 -070085 void onStartActivityFromInCall(Call call, PendingIntent intent);
Ihab Awadb78b2762014-07-25 15:16:23 -070086 void onTargetPhoneAccountChanged(Call call);
87 void onConnectionManagerPhoneAccountChanged(Call call);
Santos Cordon64c7e962014-07-02 15:15:27 -070088 }
89
90 abstract static class ListenerBase implements Listener {
91 @Override
92 public void onSuccessfulOutgoingCall(Call call) {}
93 @Override
94 public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
95 @Override
96 public void onCancelledOutgoingCall(Call call) {}
97 @Override
Sailesh Nepalc92c4362014-07-04 18:33:21 -070098 public void onSuccessfulIncomingCall(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -070099 @Override
100 public void onFailedIncomingCall(Call call) {}
101 @Override
102 public void onRequestingRingback(Call call, boolean requestingRingback) {}
103 @Override
104 public void onPostDialWait(Call call, String remaining) {}
105 @Override
Sailesh Nepale20bc972014-07-09 21:22:36 -0700106 public void onCallCapabilitiesChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700107 @Override
108 public void onExpiredConferenceCall(Call call) {}
109 @Override
110 public void onConfirmedConferenceCall(Call call) {}
111 @Override
112 public void onParentChanged(Call call) {}
113 @Override
114 public void onChildrenChanged(Call call) {}
115 @Override
116 public void onCannedSmsResponsesLoaded(Call call) {}
117 @Override
Andrew Lee3bcf9352014-07-23 12:36:05 -0700118 public void onVideoCallProviderChanged(Call call) {}
Santos Cordon64c7e962014-07-02 15:15:27 -0700119 @Override
Santos Cordon64c7e962014-07-02 15:15:27 -0700120 public void onCallerInfoChanged(Call call) {}
Sailesh Nepal7e669572014-07-08 21:29:12 -0700121 @Override
122 public void onAudioModeIsVoipChanged(Call call) {}
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700123 @Override
124 public void onStatusHintsChanged(Call call) {}
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700125 @Override
126 public void onHandleChanged(Call call) {}
127 @Override
128 public void onCallerDisplayNameChanged(Call call) {}
Andrew Lee4a796602014-07-11 17:23:03 -0700129 @Override
130 public void onVideoStateChanged(Call call) {}
Sailesh Nepal9d58de52014-07-18 14:53:19 -0700131 @Override
132 public void onStartActivityFromInCall(Call call, PendingIntent intent) {}
Ihab Awad69eb0f52014-07-18 11:20:37 -0700133 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700134 public void onTargetPhoneAccountChanged(Call call) {}
135 @Override
136 public void onConnectionManagerPhoneAccountChanged(Call call) {}
Santos Cordon766d04f2014-05-06 10:28:25 -0700137 }
138
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700139 private static final OnQueryCompleteListener sCallerInfoQueryListener =
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700140 new OnQueryCompleteListener() {
141 /** ${inheritDoc} */
142 @Override
143 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
144 if (cookie != null) {
145 ((Call) cookie).setCallerInfo(callerInfo, token);
146 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700147 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700148 };
149
150 private static final OnImageLoadCompleteListener sPhotoLoadListener =
151 new OnImageLoadCompleteListener() {
152 /** ${inheritDoc} */
153 @Override
154 public void onImageLoadComplete(
155 int token, Drawable photo, Bitmap photoIcon, Object cookie) {
156 if (cookie != null) {
157 ((Call) cookie).setPhoto(photo, photoIcon, token);
158 }
159 }
160 };
Ben Gilad0407fb22014-01-09 16:18:41 -0800161
Sailesh Nepal664837f2014-07-14 16:31:51 -0700162 private final Runnable mDirectToVoicemailRunnable = new Runnable() {
163 @Override
164 public void run() {
165 processDirectToVoicemail();
166 }
167 };
168
Sailesh Nepal810735e2014-03-18 18:15:46 -0700169 /** True if this is an incoming call. */
170 private final boolean mIsIncoming;
171
Ben Gilad0407fb22014-01-09 16:18:41 -0800172 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700173 * The time this call was created. Beyond logging and such, may also be used for bookkeeping
174 * and specifically for marking certain call attempts as failed attempts.
Ben Gilad0407fb22014-01-09 16:18:41 -0800175 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700176 private final long mCreationTimeMillis = System.currentTimeMillis();
177
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700178 /** The gateway information associated with this call. This stores the original call handle
179 * that the user is attempting to connect to via the gateway, the actual handle to dial in
180 * order to connect the call via the gateway, as well as the package name of the gateway
181 * service. */
182 private final GatewayInfo mGatewayInfo;
183
Ihab Awadb78b2762014-07-25 15:16:23 -0700184 private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
185
186 private PhoneAccountHandle mTargetPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700187
Santos Cordon2174fb52014-05-29 08:22:56 -0700188 private final Handler mHandler = new Handler();
189
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700190 private long mConnectTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800191
Santos Cordon61d0f702014-02-19 02:52:23 -0800192 /** The state of the call. */
193 private CallState mState;
194
195 /** The handle with which to establish this call. */
Sailesh Nepalce704b92014-03-17 18:31:43 -0700196 private Uri mHandle;
Santos Cordon61d0f702014-02-19 02:52:23 -0800197
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700198 /** The {@link CallPropertyPresentation} that controls how the handle is shown. */
199 private int mHandlePresentation;
200
201 /** The caller display name (CNAP) set by the connection service. */
202 private String mCallerDisplayName;
203
204 /** The {@link CallPropertyPresentation} that controls how the caller display name is shown. */
205 private int mCallerDisplayNamePresentation;
206
Ben Gilad0407fb22014-01-09 16:18:41 -0800207 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700208 * The connection service which is attempted or already connecting this call.
Santos Cordon681663d2014-01-30 04:32:15 -0800209 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700210 private ConnectionServiceWrapper mConnectionService;
Ben Gilad61925612014-03-11 19:06:36 -0700211
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700212 private boolean mIsEmergencyCall;
213
Sai Cheemalapatib7157e92014-06-11 17:51:55 -0700214 private boolean mSpeakerphoneOn;
215
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700216 /**
217 * Tracks the video states which were applicable over the duration of a call.
218 * See {@link android.telecomm.VideoCallProfile} for a list of valid video states.
219 */
220 private int mVideoStateHistory;
221
Tyler Gunnc4abd912014-07-08 14:22:10 -0700222 private int mVideoState;
223
Ben Gilad61925612014-03-11 19:06:36 -0700224 /**
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700225 * Disconnect cause for the call. Only valid if the state of the call is DISCONNECTED.
226 * See {@link android.telephony.DisconnectCause}.
227 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700228 private int mDisconnectCause = DisconnectCause.NOT_VALID;
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700229
230 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700231 * Additional disconnect information provided by the connection service.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700232 */
233 private String mDisconnectMessage;
234
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700235 /** Info used by the connection services. */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700236 private Bundle mExtras = Bundle.EMPTY;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700237
Santos Cordon766d04f2014-05-06 10:28:25 -0700238 /** Set of listeners on this call. */
Santos Cordondfc66012014-07-15 13:41:40 -0700239 private Set<Listener> mListeners = new CopyOnWriteArraySet<>();
Santos Cordon766d04f2014-05-06 10:28:25 -0700240
Sailesh Nepal664837f2014-07-14 16:31:51 -0700241 private CreateConnectionProcessor mCreateConnectionProcessor;
Santos Cordon682fe6b2014-05-20 08:56:39 -0700242
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700243 /** Caller information retrieved from the latest contact query. */
244 private CallerInfo mCallerInfo;
245
246 /** The latest token used with a contact info query. */
247 private int mQueryToken = 0;
248
Ihab Awadcb387ac2014-05-28 16:49:38 -0700249 /** Whether this call is requesting that Telecomm play the ringback tone on its behalf. */
250 private boolean mRequestingRingback = false;
251
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700252 /** Whether direct-to-voicemail query is pending. */
253 private boolean mDirectToVoicemailQueryPending;
Santos Cordon2174fb52014-05-29 08:22:56 -0700254
Sailesh Nepale20bc972014-07-09 21:22:36 -0700255 private int mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700256
257 private boolean mIsConference = false;
258
259 private Call mParentCall = null;
260
261 private List<Call> mChildCalls = new LinkedList<>();
262
Ihab Awadff7493a2014-06-10 13:47:44 -0700263 /** Set of text message responses allowed for this call, if applicable. */
264 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
265
266 /** Whether an attempt has been made to load the text message responses. */
267 private boolean mCannedSmsResponsesLoadingStarted = false;
268
Andrew Lee3bcf9352014-07-23 12:36:05 -0700269 private IVideoCallProvider mVideoCallProvider;
Nancy Chena65d41f2014-06-24 12:06:03 -0700270
Sailesh Nepal7e669572014-07-08 21:29:12 -0700271 private boolean mAudioModeIsVoip;
Sailesh Nepal35faf8c2014-07-08 22:02:34 -0700272 private StatusHints mStatusHints;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700273 private final ConnectionServiceRepository mRepository;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700274
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700275 /**
Ben Gilad0407fb22014-01-09 16:18:41 -0800276 * Persists the specified parameters and initializes the new instance.
277 *
278 * @param handle The handle to dial.
Yorke Lee33501632014-03-17 19:24:12 -0700279 * @param gatewayInfo Gateway information to use for the call.
Ihab Awadb78b2762014-07-25 15:16:23 -0700280 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
281 * This account must be one that was registered with the
282 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
283 * @param targetPhoneAccountHandle Account information to use for the call. This account must be
284 * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
Sailesh Nepal810735e2014-03-18 18:15:46 -0700285 * @param isIncoming True if this is an incoming call.
Ben Gilad0407fb22014-01-09 16:18:41 -0800286 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700287 Call(
288 ConnectionServiceRepository repository,
289 Uri handle,
290 GatewayInfo gatewayInfo,
Ihab Awadb78b2762014-07-25 15:16:23 -0700291 PhoneAccountHandle connectionManagerPhoneAccountHandle,
292 PhoneAccountHandle targetPhoneAccountHandle,
Sailesh Nepal664837f2014-07-14 16:31:51 -0700293 boolean isIncoming,
294 boolean isConference) {
Santos Cordona1610702014-06-04 20:22:56 -0700295 mState = isConference ? CallState.ACTIVE : CallState.NEW;
Sailesh Nepal664837f2014-07-14 16:31:51 -0700296 mRepository = repository;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700297 setHandle(handle, CallPropertyPresentation.ALLOWED);
Yorke Lee33501632014-03-17 19:24:12 -0700298 mGatewayInfo = gatewayInfo;
Ihab Awadb78b2762014-07-25 15:16:23 -0700299 mConnectionManagerPhoneAccountHandle = connectionManagerPhoneAccountHandle;
300 mTargetPhoneAccountHandle = targetPhoneAccountHandle;
Sailesh Nepal810735e2014-03-18 18:15:46 -0700301 mIsIncoming = isIncoming;
Santos Cordona1610702014-06-04 20:22:56 -0700302 mIsConference = isConference;
Ihab Awadff7493a2014-06-10 13:47:44 -0700303 maybeLoadCannedSmsResponses();
Ben Gilad0407fb22014-01-09 16:18:41 -0800304 }
305
Santos Cordon766d04f2014-05-06 10:28:25 -0700306 void addListener(Listener listener) {
307 mListeners.add(listener);
308 }
309
310 void removeListener(Listener listener) {
311 mListeners.remove(listener);
312 }
313
Santos Cordon61d0f702014-02-19 02:52:23 -0800314 /** {@inheritDoc} */
315 @Override public String toString() {
Sailesh Nepal4538f012014-04-15 11:40:33 -0700316 String component = null;
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700317 if (mConnectionService != null && mConnectionService.getComponentName() != null) {
318 component = mConnectionService.getComponentName().flattenToShortString();
Sailesh Nepal4538f012014-04-15 11:40:33 -0700319 }
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700320
321 return String.format(Locale.US, "[%s, %s, %s, %d]", mState, component,
322 Log.piiHandle(mHandle), getVideoState());
Santos Cordon61d0f702014-02-19 02:52:23 -0800323 }
324
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800325 CallState getState() {
Santos Cordona1610702014-06-04 20:22:56 -0700326 if (mIsConference) {
327 if (!mChildCalls.isEmpty()) {
328 // If we have child calls, just return the child call.
329 return mChildCalls.get(0).getState();
330 }
331 return CallState.ACTIVE;
332 } else {
333 return mState;
334 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800335 }
336
337 /**
338 * Sets the call state. Although there exists the notion of appropriate state transitions
339 * (see {@link CallState}), in practice those expectations break down when cellular systems
340 * misbehave and they do this very often. The result is that we do not enforce state transitions
341 * and instead keep the code resilient to unexpected state changes.
342 */
Sailesh Nepal810735e2014-03-18 18:15:46 -0700343 void setState(CallState newState) {
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700344 Preconditions.checkState(newState != CallState.DISCONNECTED ||
345 mDisconnectCause != DisconnectCause.NOT_VALID);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700346 if (mState != newState) {
347 Log.v(this, "setState %s -> %s", mState, newState);
348 mState = newState;
Ihab Awadff7493a2014-06-10 13:47:44 -0700349 maybeLoadCannedSmsResponses();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700350 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800351 }
352
Ihab Awadcb387ac2014-05-28 16:49:38 -0700353 void setRequestingRingback(boolean requestingRingback) {
354 mRequestingRingback = requestingRingback;
355 for (Listener l : mListeners) {
356 l.onRequestingRingback(this, mRequestingRingback);
357 }
358 }
359
360 boolean isRequestingRingback() {
361 return mRequestingRingback;
362 }
363
Sailesh Nepalce704b92014-03-17 18:31:43 -0700364 Uri getHandle() {
Ben Gilad0bf5b912014-01-28 17:55:57 -0800365 return mHandle;
366 }
367
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700368 int getHandlePresentation() {
369 return mHandlePresentation;
370 }
371
372 void setHandle(Uri handle, int presentation) {
373 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700374 mHandle = handle;
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700375 mHandlePresentation = presentation;
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700376 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
Yorke Lee66255452014-06-05 08:09:24 -0700377 TelecommApp.getInstance(), mHandle.getSchemeSpecificPart());
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700378 startCallerInfoLookup();
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700379 for (Listener l : mListeners) {
380 l.onHandleChanged(this);
381 }
382 }
383 }
384
385 String getCallerDisplayName() {
386 return mCallerDisplayName;
387 }
388
389 int getCallerDisplayNamePresentation() {
390 return mCallerDisplayNamePresentation;
391 }
392
393 void setCallerDisplayName(String callerDisplayName, int presentation) {
394 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
395 presentation != mCallerDisplayNamePresentation) {
396 mCallerDisplayName = callerDisplayName;
397 mCallerDisplayNamePresentation = presentation;
398 for (Listener l : mListeners) {
399 l.onCallerDisplayNameChanged(this);
400 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700401 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700402 }
403
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700404 String getName() {
405 return mCallerInfo == null ? null : mCallerInfo.name;
406 }
407
408 Bitmap getPhotoIcon() {
409 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
410 }
411
412 Drawable getPhoto() {
413 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
414 }
415
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700416 /**
417 * @param disconnectCause The reason for the disconnection, any of
418 * {@link android.telephony.DisconnectCause}.
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700419 * @param disconnectMessage Optional message about the disconnect.
Santos Cordon79ff2bc2014-03-27 15:31:27 -0700420 */
421 void setDisconnectCause(int disconnectCause, String disconnectMessage) {
422 // TODO: Consider combining this method with a setDisconnected() method that is totally
423 // separate from setState.
424 mDisconnectCause = disconnectCause;
425 mDisconnectMessage = disconnectMessage;
426 }
427
428 int getDisconnectCause() {
429 return mDisconnectCause;
430 }
431
432 String getDisconnectMessage() {
433 return mDisconnectMessage;
434 }
435
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700436 boolean isEmergencyCall() {
437 return mIsEmergencyCall;
Santos Cordon61d0f702014-02-19 02:52:23 -0800438 }
439
Yorke Lee33501632014-03-17 19:24:12 -0700440 /**
441 * @return The original handle this call is associated with. In-call services should use this
442 * handle when indicating in their UI the handle that is being called.
443 */
444 public Uri getOriginalHandle() {
445 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
446 return mGatewayInfo.getOriginalHandle();
447 }
448 return getHandle();
449 }
450
451 GatewayInfo getGatewayInfo() {
452 return mGatewayInfo;
453 }
454
Ihab Awadb78b2762014-07-25 15:16:23 -0700455 PhoneAccountHandle getConnectionManagerPhoneAccount() {
456 return mConnectionManagerPhoneAccountHandle;
Nancy Chen77d2d0e2014-06-24 12:06:03 -0700457 }
458
Ihab Awadb78b2762014-07-25 15:16:23 -0700459 void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
460 if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
461 mConnectionManagerPhoneAccountHandle = accountHandle;
Ihab Awad69eb0f52014-07-18 11:20:37 -0700462 for (Listener l : mListeners) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700463 l.onConnectionManagerPhoneAccountChanged(this);
464 }
465 }
466
467 }
468
469 PhoneAccountHandle getTargetPhoneAccount() {
470 return mTargetPhoneAccountHandle;
471 }
472
473 void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
474 if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
475 mTargetPhoneAccountHandle = accountHandle;
476 for (Listener l : mListeners) {
477 l.onTargetPhoneAccountChanged(this);
Ihab Awad69eb0f52014-07-18 11:20:37 -0700478 }
479 }
Nancy Chen53ceedc2014-07-08 18:56:51 -0700480 }
481
Sailesh Nepal810735e2014-03-18 18:15:46 -0700482 boolean isIncoming() {
483 return mIsIncoming;
484 }
485
Ben Gilad0407fb22014-01-09 16:18:41 -0800486 /**
487 * @return The "age" of this call object in milliseconds, which typically also represents the
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700488 * period since this call was added to the set pending outgoing calls, see
489 * mCreationTimeMillis.
Ben Gilad0407fb22014-01-09 16:18:41 -0800490 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700491 long getAgeMillis() {
492 return System.currentTimeMillis() - mCreationTimeMillis;
Ben Gilad0407fb22014-01-09 16:18:41 -0800493 }
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800494
Yorke Leef98fb572014-03-05 10:56:55 -0800495 /**
496 * @return The time when this call object was created and added to the set of pending outgoing
497 * calls.
498 */
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700499 long getCreationTimeMillis() {
500 return mCreationTimeMillis;
501 }
502
503 long getConnectTimeMillis() {
504 return mConnectTimeMillis;
505 }
506
507 void setConnectTimeMillis(long connectTimeMillis) {
508 mConnectTimeMillis = connectTimeMillis;
Yorke Leef98fb572014-03-05 10:56:55 -0800509 }
510
Sailesh Nepale20bc972014-07-09 21:22:36 -0700511 int getCallCapabilities() {
512 return mCallCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700513 }
514
Sailesh Nepale20bc972014-07-09 21:22:36 -0700515 void setCallCapabilities(int callCapabilities) {
516 if (mCallCapabilities != callCapabilities) {
517 mCallCapabilities = callCapabilities;
Santos Cordona1610702014-06-04 20:22:56 -0700518 for (Listener l : mListeners) {
Sailesh Nepale20bc972014-07-09 21:22:36 -0700519 l.onCallCapabilitiesChanged(this);
Santos Cordona1610702014-06-04 20:22:56 -0700520 }
521 }
522 }
523
524 Call getParentCall() {
525 return mParentCall;
526 }
527
528 List<Call> getChildCalls() {
529 return mChildCalls;
530 }
531
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700532 ConnectionServiceWrapper getConnectionService() {
533 return mConnectionService;
Santos Cordon681663d2014-01-30 04:32:15 -0800534 }
535
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700536 void setConnectionService(ConnectionServiceWrapper service) {
537 Preconditions.checkNotNull(service);
538
539 clearConnectionService();
540
541 service.incrementAssociatedCallCount();
542 mConnectionService = service;
543 mConnectionService.addCall(this);
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700544 }
545
546 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700547 * Clears the associated connection service.
Sailesh Nepal0e5410a2014-04-04 01:20:58 -0700548 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700549 void clearConnectionService() {
550 if (mConnectionService != null) {
551 ConnectionServiceWrapper serviceTemp = mConnectionService;
552 mConnectionService = null;
553 serviceTemp.removeCall(this);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700554
555 // Decrementing the count can cause the service to unbind, which itself can trigger the
556 // service-death code. Since the service death code tries to clean up any associated
557 // calls, we need to make sure to remove that information (e.g., removeCall()) before
558 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700559 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
560 // to do.
561 decrementAssociatedCallCount(serviceTemp);
Yorke Leeadee12d2014-03-13 12:08:30 -0700562 }
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800563 }
564
Sailesh Nepal664837f2014-07-14 16:31:51 -0700565 private void processDirectToVoicemail() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700566 if (mDirectToVoicemailQueryPending) {
Santos Cordon2174fb52014-05-29 08:22:56 -0700567 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
568 Log.i(this, "Directing call to voicemail: %s.", this);
569 // TODO(santoscordon): Once we move State handling from CallsManager to Call, we
570 // will not need to set RINGING state prior to calling reject.
571 setState(CallState.RINGING);
Ihab Awadff7493a2014-06-10 13:47:44 -0700572 reject(false, null);
Santos Cordon2174fb52014-05-29 08:22:56 -0700573 } else {
574 // TODO(santoscordon): Make this class (not CallsManager) responsible for changing
575 // the call state to RINGING.
576
577 // TODO(santoscordon): Replace this with state transition to RINGING.
578 for (Listener l : mListeners) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700579 l.onSuccessfulIncomingCall(this);
Santos Cordon2174fb52014-05-29 08:22:56 -0700580 }
581 }
582
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700583 mDirectToVoicemailQueryPending = false;
Santos Cordon766d04f2014-05-06 10:28:25 -0700584 }
585 }
586
Santos Cordon766d04f2014-05-06 10:28:25 -0700587 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700588 * Starts the create connection sequence. Upon completion, there should exist an active
589 * connection through a connection service (or the call will have failed).
Santos Cordon766d04f2014-05-06 10:28:25 -0700590 */
Sailesh Nepal664837f2014-07-14 16:31:51 -0700591 void startCreateConnection() {
592 Preconditions.checkState(mCreateConnectionProcessor == null);
593 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this);
594 mCreateConnectionProcessor.process();
Santos Cordon766d04f2014-05-06 10:28:25 -0700595 }
596
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700597 @Override
Santos Cordon72890ce2014-07-21 01:32:04 -0700598 public void handleCreateConnectionSuccessful(
599 ConnectionRequest request, ParcelableConnection connection) {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700600 mCreateConnectionProcessor = null;
Santos Cordon72890ce2014-07-21 01:32:04 -0700601 setState(getStateFromConnectionState(connection.getState()));
Ihab Awadb78b2762014-07-25 15:16:23 -0700602 setTargetPhoneAccount(connection.getPhoneAccount());
Santos Cordon72890ce2014-07-21 01:32:04 -0700603 setHandle(connection.getHandle(), connection.getHandlePresentation());
604 setCallerDisplayName(
605 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
Andrew Lee3bcf9352014-07-23 12:36:05 -0700606
607 setVideoCallProvider(connection.getVideoCallProvider());
Tyler Gunnb1a95a72014-07-23 14:08:19 -0700608 setVideoState(connection.getVideoState());
Sailesh Nepal664837f2014-07-14 16:31:51 -0700609
610 if (mIsIncoming) {
611 // We do not handle incoming calls immediately when they are verified by the connection
612 // service. We allow the caller-info-query code to execute first so that we can read the
613 // direct-to-voicemail property before deciding if we want to show the incoming call to
614 // the user or if we want to reject the call.
615 mDirectToVoicemailQueryPending = true;
616
Sailesh Nepal664837f2014-07-14 16:31:51 -0700617 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
618 // showing the user the incoming call screen.
619 mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis());
620 } else {
621 for (Listener l : mListeners) {
622 l.onSuccessfulOutgoingCall(this);
623 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700624 }
625 }
626
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700627 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700628 public void handleCreateConnectionFailed(int code, String msg) {
629 mCreateConnectionProcessor = null;
630 if (mIsIncoming) {
631 clearConnectionService();
632 setDisconnectCause(code, null);
633 setState(CallState.DISCONNECTED);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700634
Sailesh Nepal664837f2014-07-14 16:31:51 -0700635 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
636 for (int i = 0; i < listeners.length; i++) {
637 listeners[i].onFailedIncomingCall(this);
638 }
639 } else {
640 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
641 for (int i = 0; i < listeners.length; i++) {
642 listeners[i].onFailedOutgoingCall(this, code, msg);
643 }
644 clearConnectionService();
645 }
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700646 }
647
648 @Override
Sailesh Nepal664837f2014-07-14 16:31:51 -0700649 public void handleCreateConnectionCancelled() {
650 mCreateConnectionProcessor = null;
651 if (mIsIncoming) {
652 clearConnectionService();
Santos Cordonfd6ca442014-07-24 15:34:01 -0700653 setDisconnectCause(DisconnectCause.OUTGOING_CANCELED, null);
Sailesh Nepal664837f2014-07-14 16:31:51 -0700654 setState(CallState.DISCONNECTED);
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700655
Sailesh Nepal664837f2014-07-14 16:31:51 -0700656 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
657 for (int i = 0; i < listeners.length; i++) {
658 listeners[i].onFailedIncomingCall(this);
659 }
660 } else {
661 Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
662 for (int i = 0; i < listeners.length; i++) {
663 listeners[i].onCancelledOutgoingCall(this);
664 }
665 clearConnectionService();
666 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700667 }
668
669 /**
Ihab Awad74549ec2014-03-10 15:33:25 -0700670 * Plays the specified DTMF tone.
671 */
672 void playDtmfTone(char digit) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700673 if (mConnectionService == null) {
674 Log.w(this, "playDtmfTone() request on a call without a connection service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700675 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700676 Log.i(this, "Send playDtmfTone to connection service for call %s", this);
677 mConnectionService.playDtmfTone(this, digit);
Ihab Awad74549ec2014-03-10 15:33:25 -0700678 }
679 }
680
681 /**
682 * Stops playing any currently playing DTMF tone.
683 */
684 void stopDtmfTone() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700685 if (mConnectionService == null) {
686 Log.w(this, "stopDtmfTone() request on a call without a connectino service.");
Ihab Awad74549ec2014-03-10 15:33:25 -0700687 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700688 Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
689 mConnectionService.stopDtmfTone(this);
Ihab Awad74549ec2014-03-10 15:33:25 -0700690 }
691 }
692
693 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700694 * Attempts to disconnect the call through the connection service.
Santos Cordon049b7b62014-01-30 05:34:26 -0800695 */
696 void disconnect() {
Nancy Chen53ceedc2014-07-08 18:56:51 -0700697 if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT) {
Santos Cordon682fe6b2014-05-20 08:56:39 -0700698 Log.v(this, "Aborting call %s", this);
699 abort();
Santos Cordon74d420b2014-05-07 14:38:47 -0700700 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700701 Preconditions.checkNotNull(mConnectionService);
Santos Cordon766d04f2014-05-06 10:28:25 -0700702
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700703 Log.i(this, "Send disconnect to connection service for call: %s", this);
704 // The call isn't officially disconnected until the connection service confirms that the
705 // call was actually disconnected. Only then is the association between call and
706 // connection service severed, see {@link CallsManager#markCallAsDisconnected}.
707 mConnectionService.disconnect(this);
Santos Cordon049b7b62014-01-30 05:34:26 -0800708 }
709 }
710
Santos Cordon682fe6b2014-05-20 08:56:39 -0700711 void abort() {
Sailesh Nepal664837f2014-07-14 16:31:51 -0700712 if (mCreateConnectionProcessor != null) {
713 mCreateConnectionProcessor.abort();
Nancy Chen53ceedc2014-07-08 18:56:51 -0700714 } else if (mState == CallState.PRE_DIAL_WAIT) {
715 handleCreateConnectionFailed(DisconnectCause.LOCAL, null);
Santos Cordon682fe6b2014-05-20 08:56:39 -0700716 }
717 }
718
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800719 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800720 * Answers the call if it is ringing.
Andrew Lee38931d02014-07-16 10:17:36 -0700721 *
722 * @param videoState The video state in which to answer the call.
Santos Cordon61d0f702014-02-19 02:52:23 -0800723 */
Andrew Lee38931d02014-07-16 10:17:36 -0700724 void answer(int videoState) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700725 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800726
727 // Check to verify that the call is still in the ringing state. A call can change states
728 // between the time the user hits 'answer' and Telecomm receives the command.
729 if (isRinging("answer")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700730 // At this point, we are asking the connection service to answer but we don't assume
731 // that it will work. Instead, we wait until confirmation from the connectino service
732 // that the call is in a non-RINGING state before changing the UI. See
733 // {@link ConnectionServiceAdapter#setActive} and other set* methods.
Andrew Lee38931d02014-07-16 10:17:36 -0700734 mConnectionService.answer(this, videoState);
Santos Cordon61d0f702014-02-19 02:52:23 -0800735 }
736 }
737
738 /**
739 * Rejects the call if it is ringing.
Ihab Awadff7493a2014-06-10 13:47:44 -0700740 *
741 * @param rejectWithMessage Whether to send a text message as part of the call rejection.
742 * @param textMessage An optional text message to send as part of the rejection.
Santos Cordon61d0f702014-02-19 02:52:23 -0800743 */
Ihab Awadff7493a2014-06-10 13:47:44 -0700744 void reject(boolean rejectWithMessage, String textMessage) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700745 Preconditions.checkNotNull(mConnectionService);
Santos Cordon61d0f702014-02-19 02:52:23 -0800746
747 // Check to verify that the call is still in the ringing state. A call can change states
748 // between the time the user hits 'reject' and Telecomm receives the command.
749 if (isRinging("reject")) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700750 mConnectionService.reject(this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800751 }
752 }
753
754 /**
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700755 * Puts the call on hold if it is currently active.
756 */
757 void hold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700758 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700759
760 if (mState == CallState.ACTIVE) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700761 mConnectionService.hold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700762 }
763 }
764
765 /**
766 * Releases the call from hold if it is currently active.
767 */
768 void unhold() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700769 Preconditions.checkNotNull(mConnectionService);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700770
771 if (mState == CallState.ON_HOLD) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700772 mConnectionService.unhold(this);
Yorke Leecdf3ebd2014-03-12 18:31:41 -0700773 }
774 }
775
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700776 /** Checks if this is a live call or not. */
777 boolean isAlive() {
778 switch (mState) {
779 case NEW:
780 case RINGING:
781 case DISCONNECTED:
782 case ABORTED:
783 return false;
784 default:
785 return true;
786 }
787 }
788
Santos Cordon40f78c22014-04-07 02:11:42 -0700789 boolean isActive() {
Ihab Awad84bfe472014-07-13 17:11:57 -0700790 return mState == CallState.ACTIVE;
Santos Cordon40f78c22014-04-07 02:11:42 -0700791 }
792
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700793 Bundle getExtras() {
794 return mExtras;
795 }
796
797 void setExtras(Bundle extras) {
798 mExtras = extras;
799 }
800
Santos Cordon5ba7f272014-05-28 13:59:49 -0700801 Uri getRingtone() {
802 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
803 }
804
Evan Charlton352105c2014-06-03 14:10:54 -0700805 void onPostDialWait(String remaining) {
806 for (Listener l : mListeners) {
807 l.onPostDialWait(this, remaining);
808 }
809 }
810
811 void postDialContinue(boolean proceed) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700812 mConnectionService.onPostDialContinue(this, proceed);
Evan Charlton352105c2014-06-03 14:10:54 -0700813 }
814
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700815 void phoneAccountClicked() {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700816 mConnectionService.onPhoneAccountClicked(this);
Sailesh Nepal77da19e2014-07-02 21:31:16 -0700817 }
818
Santos Cordona1610702014-06-04 20:22:56 -0700819 void conferenceInto(Call conferenceCall) {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700820 if (mConnectionService == null) {
821 Log.w(this, "conference requested on a call without a connection service.");
Santos Cordona1610702014-06-04 20:22:56 -0700822 } else {
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700823 mConnectionService.conference(conferenceCall, this);
Santos Cordona1610702014-06-04 20:22:56 -0700824 }
825 }
826
827 void expireConference() {
828 // The conference call expired before we got a confirmation of the conference from the
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700829 // connection service...so start shutting down.
830 clearConnectionService();
Santos Cordona1610702014-06-04 20:22:56 -0700831 for (Listener l : mListeners) {
832 l.onExpiredConferenceCall(this);
833 }
834 }
835
836 void confirmConference() {
837 Log.v(this, "confirming Conf call %s", mListeners);
838 for (Listener l : mListeners) {
839 l.onConfirmedConferenceCall(this);
840 }
841 }
842
843 void splitFromConference() {
844 // TODO(santoscordon): todo
845 }
846
Sailesh Nepale8ecb982014-07-11 17:19:42 -0700847 void swapWithBackgroundCall() {
848 mConnectionService.swapWithBackgroundCall(this);
849 }
850
Santos Cordona1610702014-06-04 20:22:56 -0700851 void setParentCall(Call parentCall) {
852 if (parentCall == this) {
853 Log.e(this, new Exception(), "setting the parent to self");
854 return;
855 }
856 Preconditions.checkState(parentCall == null || mParentCall == null);
857
858 Call oldParent = mParentCall;
859 if (mParentCall != null) {
860 mParentCall.removeChildCall(this);
861 }
862 mParentCall = parentCall;
863 if (mParentCall != null) {
864 mParentCall.addChildCall(this);
865 }
866
867 for (Listener l : mListeners) {
868 l.onParentChanged(this);
869 }
870 }
871
872 private void addChildCall(Call call) {
873 if (!mChildCalls.contains(call)) {
874 mChildCalls.add(call);
875
876 for (Listener l : mListeners) {
877 l.onChildrenChanged(this);
878 }
879 }
880 }
881
882 private void removeChildCall(Call call) {
883 if (mChildCalls.remove(call)) {
884 for (Listener l : mListeners) {
885 l.onChildrenChanged(this);
886 }
887 }
888 }
889
Santos Cordon0b03b4b2014-01-29 18:01:59 -0800890 /**
Ihab Awadff7493a2014-06-10 13:47:44 -0700891 * Return whether the user can respond to this {@code Call} via an SMS message.
892 *
893 * @return true if the "Respond via SMS" feature should be enabled
894 * for this incoming call.
895 *
896 * The general rule is that we *do* allow "Respond via SMS" except for
897 * the few (relatively rare) cases where we know for sure it won't
898 * work, namely:
899 * - a bogus or blank incoming number
900 * - a call from a SIP address
901 * - a "call presentation" that doesn't allow the number to be revealed
902 *
903 * In all other cases, we allow the user to respond via SMS.
904 *
905 * Note that this behavior isn't perfect; for example we have no way
906 * to detect whether the incoming call is from a landline (with most
907 * networks at least), so we still enable this feature even though
908 * SMSes to that number will silently fail.
909 */
910 boolean isRespondViaSmsCapable() {
911 if (mState != CallState.RINGING) {
912 return false;
913 }
914
915 if (getHandle() == null) {
916 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
917 // other words, the user should not be able to see the incoming phone number.
918 return false;
919 }
920
921 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
922 // The incoming number is actually a URI (i.e. a SIP address),
923 // not a regular PSTN phone number, and we can't send SMSes to
924 // SIP addresses.
925 // (TODO: That might still be possible eventually, though. Is
926 // there some SIP-specific equivalent to sending a text message?)
927 return false;
928 }
929
930 // Is there a valid SMS application on the phone?
931 if (SmsApplication.getDefaultRespondViaMessageApplication(TelecommApp.getInstance(),
932 true /*updateIfNeeded*/) == null) {
933 return false;
934 }
935
936 // TODO: with some carriers (in certain countries) you *can* actually
937 // tell whether a given number is a mobile phone or not. So in that
938 // case we could potentially return false here if the incoming call is
939 // from a land line.
940
941 // If none of the above special cases apply, it's OK to enable the
942 // "Respond via SMS" feature.
943 return true;
944 }
945
946 List<String> getCannedSmsResponses() {
947 return mCannedSmsResponses;
948 }
949
950 /**
Santos Cordon61d0f702014-02-19 02:52:23 -0800951 * @return True if the call is ringing, else logs the action name.
952 */
953 private boolean isRinging(String actionName) {
954 if (mState == CallState.RINGING) {
955 return true;
956 }
957
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800958 Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
Santos Cordon61d0f702014-02-19 02:52:23 -0800959 return false;
960 }
961
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800962 @SuppressWarnings("rawtypes")
963 private void decrementAssociatedCallCount(ServiceBinder binder) {
964 if (binder != null) {
965 binder.decrementAssociatedCallCount();
966 }
967 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700968
969 /**
970 * Looks up contact information based on the current handle.
971 */
972 private void startCallerInfoLookup() {
973 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();
974
975 mQueryToken++; // Updated so that previous queries can no longer set the information.
976 mCallerInfo = null;
977 if (!TextUtils.isEmpty(number)) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700978 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700979 CallerInfoAsyncQuery.startQuery(
980 mQueryToken,
981 TelecommApp.getInstance(),
982 number,
983 sCallerInfoQueryListener,
984 this);
985 }
986 }
987
988 /**
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700989 * Saves the specified caller info if the specified token matches that of the last query
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700990 * that was made.
991 *
992 * @param callerInfo The new caller information to set.
993 * @param token The token used with this query.
994 */
995 private void setCallerInfo(CallerInfo callerInfo, int token) {
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700996 Preconditions.checkNotNull(callerInfo);
997
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700998 if (mQueryToken == token) {
999 mCallerInfo = callerInfo;
Santos Cordon5ba7f272014-05-28 13:59:49 -07001000 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001001
Makoto Onukia1662d02014-07-10 15:31:59 -07001002 if (mCallerInfo.contactDisplayPhotoUri != null) {
1003 Log.d(this, "Searching person uri %s for call %s",
1004 mCallerInfo.contactDisplayPhotoUri, this);
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001005 ContactsAsyncHelper.startObtainPhotoAsync(
1006 token,
1007 TelecommApp.getInstance(),
Makoto Onukia1662d02014-07-10 15:31:59 -07001008 mCallerInfo.contactDisplayPhotoUri,
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001009 sPhotoLoadListener,
1010 this);
Makoto Onukia1662d02014-07-10 15:31:59 -07001011 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto().
Santos Cordon64c7e962014-07-02 15:15:27 -07001012 } else {
1013 for (Listener l : mListeners) {
1014 l.onCallerInfoChanged(this);
1015 }
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001016 }
Santos Cordon2174fb52014-05-29 08:22:56 -07001017
1018 processDirectToVoicemail();
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001019 }
1020 }
1021
Yorke Lee6f3f7af2014-07-11 10:59:46 -07001022 CallerInfo getCallerInfo() {
1023 return mCallerInfo;
1024 }
1025
Santos Cordon99c8a6f2014-05-28 18:28:47 -07001026 /**
1027 * Saves the specified photo information if the specified token matches that of the last query.
1028 *
1029 * @param photo The photo as a drawable.
1030 * @param photoIcon The photo as a small icon.
1031 * @param token The token used with this query.
1032 */
1033 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) {
1034 if (mQueryToken == token) {
1035 mCallerInfo.cachedPhoto = photo;
1036 mCallerInfo.cachedPhotoIcon = photoIcon;
Santos Cordon64c7e962014-07-02 15:15:27 -07001037
1038 for (Listener l : mListeners) {
1039 l.onCallerInfoChanged(this);
1040 }
Santos Cordonfd71f4a2014-05-28 13:59:49 -07001041 }
1042 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001043
1044 private void maybeLoadCannedSmsResponses() {
1045 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
1046 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1047 mCannedSmsResponsesLoadingStarted = true;
1048 RespondViaSmsManager.getInstance().loadCannedTextMessages(
1049 new Response<Void, List<String>>() {
1050 @Override
1051 public void onResult(Void request, List<String>... result) {
1052 if (result.length > 0) {
1053 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1054 mCannedSmsResponses = result[0];
1055 for (Listener l : mListeners) {
1056 l.onCannedSmsResponsesLoaded(Call.this);
1057 }
1058 }
1059 }
1060
1061 @Override
1062 public void onError(Void request, int code, String msg) {
1063 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1064 msg);
1065 }
1066 }
1067 );
1068 } else {
1069 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1070 }
1071 }
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001072
1073 /**
1074 * Sets speakerphone option on when call begins.
1075 */
1076 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1077 mSpeakerphoneOn = startWithSpeakerphone;
1078 }
1079
1080 /**
1081 * Returns speakerphone option.
1082 *
1083 * @return Whether or not speakerphone should be set automatically when call begins.
1084 */
1085 public boolean getStartWithSpeakerphoneOn() {
1086 return mSpeakerphoneOn;
1087 }
Andrew Leee9a77652014-06-26 13:07:57 -07001088
1089 /**
Andrew Lee3bcf9352014-07-23 12:36:05 -07001090 * Sets a video call provider for the call.
Andrew Leee9a77652014-06-26 13:07:57 -07001091 */
Andrew Lee3bcf9352014-07-23 12:36:05 -07001092 public void setVideoCallProvider(IVideoCallProvider videoCallProvider) {
1093 mVideoCallProvider = videoCallProvider;
Nancy Chena65d41f2014-06-24 12:06:03 -07001094 for (Listener l : mListeners) {
Andrew Lee3bcf9352014-07-23 12:36:05 -07001095 l.onVideoCallProviderChanged(Call.this);
Nancy Chena65d41f2014-06-24 12:06:03 -07001096 }
1097 }
1098
1099 /**
Andrew Lee3bcf9352014-07-23 12:36:05 -07001100 * @return Return the {@link VideoCallProvider} binder.
Nancy Chena65d41f2014-06-24 12:06:03 -07001101 */
Andrew Lee3bcf9352014-07-23 12:36:05 -07001102 public IVideoCallProvider getVideoCallProvider() {
1103 return mVideoCallProvider;
Andrew Leee9a77652014-06-26 13:07:57 -07001104 }
Tyler Gunne19cc002014-07-01 11:32:53 -07001105
1106 /**
Tyler Gunnc4abd912014-07-08 14:22:10 -07001107 * The current video state for the call.
1108 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1109 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1110 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1111 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1112 *
1113 * @return True if video is enabled.
1114 */
1115 public int getVideoState() {
1116 return mVideoState;
1117 }
1118
1119 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001120 * Returns the video states which were applicable over the duration of a call.
1121 * See {@link android.telecomm.VideoCallProfile} for a list of valid video states.
1122 *
1123 * @return The video states applicable over the duration of the call.
1124 */
1125 public int getVideoStateHistory() {
1126 return mVideoStateHistory;
1127 }
1128
1129 /**
1130 * Determines the current video state for the call.
1131 * For an outgoing call determines the desired video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001132 * Valid values: {@link android.telecomm.VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
1133 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
1134 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_TX_ENABLED},
1135 * {@link android.telecomm.VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
1136 *
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001137 * @param videoState The video state for the call.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001138 */
1139 public void setVideoState(int videoState) {
Tyler Gunn0a388fc2014-07-17 12:21:17 -07001140 // Track which video states were applicable over the duration of the call.
1141 mVideoStateHistory = mVideoStateHistory | videoState;
1142
Tyler Gunnc4abd912014-07-08 14:22:10 -07001143 mVideoState = videoState;
Andrew Lee4a796602014-07-11 17:23:03 -07001144 for (Listener l : mListeners) {
1145 l.onVideoStateChanged(this);
1146 }
Tyler Gunnc4abd912014-07-08 14:22:10 -07001147 }
Sailesh Nepal7e669572014-07-08 21:29:12 -07001148
1149 public boolean getAudioModeIsVoip() {
1150 return mAudioModeIsVoip;
1151 }
1152
1153 public void setAudioModeIsVoip(boolean audioModeIsVoip) {
1154 mAudioModeIsVoip = audioModeIsVoip;
1155 for (Listener l : mListeners) {
Sailesh Nepal35faf8c2014-07-08 22:02:34 -07001156 l.onAudioModeIsVoipChanged(this);
1157 }
1158 }
1159
1160 public StatusHints getStatusHints() {
1161 return mStatusHints;
1162 }
1163
1164 public void setStatusHints(StatusHints statusHints) {
1165 mStatusHints = statusHints;
1166 for (Listener l : mListeners) {
1167 l.onStatusHintsChanged(this);
Sailesh Nepal7e669572014-07-08 21:29:12 -07001168 }
1169 }
Sailesh Nepal9d58de52014-07-18 14:53:19 -07001170
1171 public void startActivityFromInCall(PendingIntent intent) {
1172 if (intent.isActivity()) {
1173 for (Listener l : mListeners) {
1174 l.onStartActivityFromInCall(this, intent);
1175 }
1176 } else {
1177 Log.w(this, "startActivityFromInCall, activity intent required");
1178 }
1179 }
Santos Cordon72890ce2014-07-21 01:32:04 -07001180
1181 private CallState getStateFromConnectionState(int state) {
1182 switch (state) {
1183 case Connection.State.ACTIVE:
1184 return CallState.ACTIVE;
1185 case Connection.State.DIALING:
1186 return CallState.DIALING;
1187 case Connection.State.DISCONNECTED:
1188 return CallState.DISCONNECTED;
1189 case Connection.State.HOLDING:
1190 return CallState.ON_HOLD;
1191 case Connection.State.NEW:
1192 return CallState.NEW;
1193 case Connection.State.RINGING:
1194 return CallState.RINGING;
1195 }
1196 return CallState.DISCONNECTED;
1197 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08001198}