blob: 58d3a675c24dda929f21e8a981e8bba4a5b1e11c [file] [log] [blame]
Santos Cordon8e8b8d22013-12-19 14:14:05 -08001/*
2 * Copyright (C) 2013 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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Ben Gilad9f2bed32013-12-12 17:43:26 -080018
Santos Cordonf0f99f32016-02-18 16:13:57 -080019import android.app.ActivityManager;
Hall Liu18c94492018-04-09 17:27:01 -070020import android.app.KeyguardManager;
Tyler Gunnaee51f22018-03-09 02:33:54 +000021import android.content.BroadcastReceiver;
Tyler Gunn91d43cf2014-09-17 12:19:39 -070022import android.content.Context;
Tony Maka9930942016-01-15 10:57:14 +000023import android.content.pm.UserInfo;
Xueren Zhangc0e8e9d2015-03-06 14:16:55 +010024import android.content.Intent;
Tyler Gunnaee51f22018-03-09 02:33:54 +000025import android.content.IntentFilter;
Hall Liufbed0162016-01-14 18:06:29 -080026import android.media.AudioManager;
Sailesh Nepalce704b92014-03-17 18:31:43 -070027import android.net.Uri;
Evan Charltona05805b2014-03-05 08:21:46 -080028import android.os.Bundle;
Santos Cordoncf5b2912014-11-05 21:57:54 -080029import android.os.Handler;
Ihab Awade6dbc9d2015-03-26 10:33:44 -070030import android.os.Looper;
Tony Maka9930942016-01-15 10:57:14 +000031import android.os.Process;
Sailesh Nepal703a1af2016-04-14 20:10:12 -070032import android.os.SystemClock;
Rekha Kumard240fe82015-04-01 21:45:57 -070033import android.os.SystemProperties;
Brad Ebingerd931a012015-10-21 12:54:08 -070034import android.os.SystemVibrator;
Yorke Leee4a9c412014-11-14 16:59:42 -080035import android.os.Trace;
Tony Mak578a4e62015-11-23 11:18:51 +000036import android.os.UserHandle;
Tony Maka9930942016-01-15 10:57:14 +000037import android.os.UserManager;
Tyler Gunnaee51f22018-03-09 02:33:54 +000038import android.provider.BlockedNumberContract.SystemContract;
Santos Cordonf193ba42014-09-12 06:37:39 -070039import android.provider.CallLog.Calls;
Hall Liuf1422e72016-01-27 11:07:07 -080040import android.provider.Settings;
Yorke Lee2a66f7b2015-05-13 14:21:19 -070041import android.telecom.CallAudioState;
Tyler Gunnd92e7372015-01-09 15:59:45 -080042import android.telecom.Conference;
Ihab Awad07bc5ee2014-11-12 13:42:52 -080043import android.telecom.Connection;
Andrew Lee701dc002014-09-11 21:29:12 -070044import android.telecom.DisconnectCause;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070045import android.telecom.GatewayInfo;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070046import android.telecom.Log;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070047import android.telecom.ParcelableConference;
Tyler Gunn6e2b94e2014-10-30 11:05:22 -070048import android.telecom.ParcelableConnection;
Tyler Gunn5b452df2014-10-01 15:02:58 -070049import android.telecom.PhoneAccount;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070050import android.telecom.PhoneAccountHandle;
Brad Ebingera3eccfe2016-10-05 15:45:22 -070051import android.telecom.Logging.Runnable;
Tyler Gunn5b452df2014-10-01 15:02:58 -070052import android.telecom.TelecomManager;
Andrew Lee45506232014-10-22 17:54:33 -070053import android.telecom.VideoProfile;
Tyler Gunnaee51f22018-03-09 02:33:54 +000054import android.telephony.CarrierConfigManager;
Nancy Chena469f762014-12-09 18:29:20 -080055import android.telephony.PhoneNumberUtils;
Sailesh Nepal8aa6a002014-09-12 10:52:49 -070056import android.telephony.TelephonyManager;
Yorke Lee8d3f2692015-05-08 11:44:39 -070057import android.text.TextUtils;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080058
Ihab Awada3653902015-01-23 13:44:46 -080059import com.android.internal.annotations.VisibleForTesting;
Abhijith Shastrycf27f682016-03-01 18:28:44 -080060import com.android.internal.telephony.AsyncEmergencyContactNotifier;
Rekha Kumard240fe82015-04-01 21:45:57 -070061import com.android.internal.telephony.PhoneConstants;
62import com.android.internal.telephony.TelephonyProperties;
Tyler Gunn91d43cf2014-09-17 12:19:39 -070063import com.android.internal.util.IndentingPrintWriter;
Hall Liu486cb192016-10-21 18:23:33 -070064import com.android.server.telecom.bluetooth.BluetoothRouteManager;
Hall Liu784a4962018-03-06 11:03:17 -080065import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
Hall Liu6d4b66d2016-04-01 16:31:13 -070066import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
67import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
68import com.android.server.telecom.callfiltering.CallFilterResultCallback;
69import com.android.server.telecom.callfiltering.CallFilteringResult;
70import com.android.server.telecom.callfiltering.CallScreeningServiceFilter;
71import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
72import com.android.server.telecom.callfiltering.IncomingCallFilter;
Xueren Zhangc0e8e9d2015-03-06 14:16:55 +010073import com.android.server.telecom.components.ErrorDialogActivity;
Tyler Gunnaee51f22018-03-09 02:33:54 +000074import com.android.server.telecom.settings.BlockedNumbersUtil;
Tyler Gunnbbd78a72017-04-30 14:16:07 -070075import com.android.server.telecom.ui.ConfirmCallDialogActivity;
Tyler Gunn2b17f232017-03-08 08:51:00 -080076import com.android.server.telecom.ui.IncomingCallNotifier;
Yorke Leee4a9c412014-11-14 16:59:42 -080077
Hall Liu6d4b66d2016-04-01 16:31:13 -070078import java.util.ArrayList;
Tyler Gunnc74b3e22017-11-07 15:03:24 -080079import java.util.Arrays;
Santos Cordon4bc02452014-11-19 15:51:12 -080080import java.util.Collection;
Jay Shraunera82c8f72014-08-14 15:49:16 -070081import java.util.Collections;
Hall Liu32587202015-11-18 11:10:08 -080082import java.util.HashMap;
Ihab Awad5b281322014-09-25 18:25:29 -070083import java.util.HashSet;
Brad Ebingere680e662016-08-26 11:45:43 -070084import java.util.Iterator;
Santos Cordon7cdb9092014-07-24 21:33:33 -070085import java.util.List;
Hall Liu32587202015-11-18 11:10:08 -080086import java.util.Map;
Santos Cordonf193ba42014-09-12 06:37:39 -070087import java.util.Objects;
Tyler Gunn9b618b82016-10-17 15:54:35 -070088import java.util.Optional;
Santos Cordona56f2762014-03-24 15:55:53 -070089import java.util.Set;
Jay Shraunera82c8f72014-08-14 15:49:16 -070090import java.util.concurrent.ConcurrentHashMap;
Tyler Gunn9e806ee2017-02-06 20:49:24 -080091import java.util.concurrent.CountDownLatch;
92import java.util.concurrent.TimeUnit;
Tyler Gunna90ba732017-01-26 07:24:08 -080093import java.util.stream.Collectors;
94import java.util.stream.IntStream;
95import java.util.stream.Stream;
Ben Gilad9f2bed32013-12-12 17:43:26 -080096
Ben Giladdd8c6082013-12-30 14:44:08 -080097/**
98 * Singleton.
99 *
Jay Shrauneracb91eb2014-08-08 16:04:53 -0700100 * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
Ben Giladdd8c6082013-12-30 14:44:08 -0800101 * access from other packages specifically refraining from passing the CallsManager instance
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700102 * beyond the com.android.server.telecom package boundary.
Ben Giladdd8c6082013-12-30 14:44:08 -0800103 */
Ihab Awadaa383cc2015-03-22 22:12:28 -0700104@VisibleForTesting
Sailesh Nepalae925952016-01-24 18:56:58 -0800105public class CallsManager extends Call.ListenerBase
Tyler Gunn1bf0e6b2016-11-30 15:19:13 -0800106 implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy {
Ben Gilada0d9f752014-02-26 11:49:03 -0800107
Santos Cordondf399862014-08-06 04:39:15 -0700108 // TODO: Consider renaming this CallsManagerPlugin.
Brad Ebinger53855132015-10-30 10:58:19 -0700109 @VisibleForTesting
110 public interface CallsManagerListener {
Sailesh Nepal810735e2014-03-18 18:15:46 -0700111 void onCallAdded(Call call);
112 void onCallRemoved(Call call);
Ihab Awad6fb37c82014-08-07 19:48:57 -0700113 void onCallStateChanged(Call call, int oldState, int newState);
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700114 void onConnectionServiceChanged(
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700115 Call call,
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700116 ConnectionServiceWrapper oldService,
117 ConnectionServiceWrapper newService);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700118 void onIncomingCallAnswered(Call call);
Ihab Awadff7493a2014-06-10 13:47:44 -0700119 void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700120 void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState);
Andrew Lee5be64bc2014-09-08 18:35:15 -0700121 void onRingbackRequested(Call call, boolean ringback);
Santos Cordona1610702014-06-04 20:22:56 -0700122 void onIsConferencedChanged(Call call);
Andrew Lee5be64bc2014-09-08 18:35:15 -0700123 void onIsVoipAudioModeChanged(Call call);
Hall Liu9696c212016-06-24 16:09:02 -0700124 void onVideoStateChanged(Call call, int previousVideoState, int newVideoState);
Santos Cordon91bd74c2014-11-07 16:10:22 -0800125 void onCanAddCallChanged(boolean canAddCall);
Tyler Gunn86014fc2015-06-12 14:31:25 -0700126 void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
Tyler Gunndb821912016-02-16 14:35:25 -0800127 void onHoldToneRequested(Call call);
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700128 void onExternalCallChanged(Call call, boolean isExternalCall);
Honggangb3ccbb32016-05-31 14:46:22 +0800129 void onDisconnectedTonePlaying(boolean isTonePlaying);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700130 }
131
Pengquan Meng4832f202017-12-20 16:13:04 -0800132 /** Interface used to define the action which is executed delay under some condition. */
133 interface PendingAction {
134 void performAction();
135 }
136
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700137 private static final String TAG = "CallsManager";
Ben Gilad9f2bed32013-12-12 17:43:26 -0800138
Tyler Gunnbaf105b2017-04-11 15:21:03 -0700139 /**
140 * Call filter specifier used with
141 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only
142 * self-managed calls should be included.
143 */
144 private static final int CALL_FILTER_SELF_MANAGED = 1;
145
146 /**
147 * Call filter specifier used with
148 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only
149 * managed calls should be included.
150 */
151 private static final int CALL_FILTER_MANAGED = 2;
152
153 /**
154 * Call filter specifier used with
155 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate both managed
156 * and self-managed calls should be included.
157 */
158 private static final int CALL_FILTER_ALL = 3;
159
Tyler Gunnea4c6fb2017-04-14 13:46:21 -0700160 private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION =
161 "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION";
162
Tyler Gunn9e806ee2017-02-06 20:49:24 -0800163 private static final int HANDLER_WAIT_TIMEOUT = 10000;
Santos Cordonf193ba42014-09-12 06:37:39 -0700164 private static final int MAXIMUM_LIVE_CALLS = 1;
165 private static final int MAXIMUM_HOLD_CALLS = 1;
166 private static final int MAXIMUM_RINGING_CALLS = 1;
Roshan Pius4de4a892015-08-24 11:06:58 -0700167 private static final int MAXIMUM_DIALING_CALLS = 1;
Santos Cordonf193ba42014-09-12 06:37:39 -0700168 private static final int MAXIMUM_OUTGOING_CALLS = 1;
Santos Cordon91bd74c2014-11-07 16:10:22 -0800169 private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
Tyler Gunna90ba732017-01-26 07:24:08 -0800170 private static final int MAXIMUM_SELF_MANAGED_CALLS = 10;
Santos Cordonf193ba42014-09-12 06:37:39 -0700171
172 private static final int[] OUTGOING_CALL_STATES =
Tyler Gunn1e37be52016-07-11 08:54:23 -0700173 {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
174 CallState.PULLING};
Santos Cordonf193ba42014-09-12 06:37:39 -0700175
Tyler Gunnbaf105b2017-04-11 15:21:03 -0700176 /**
177 * These states are used by {@link #makeRoomForOutgoingCall(Call, boolean)} to determine which
178 * call should be ended first to make room for a new outgoing call.
179 */
Santos Cordon91bd74c2014-11-07 16:10:22 -0800180 private static final int[] LIVE_CALL_STATES =
Santos Cordonf0f99f32016-02-18 16:13:57 -0800181 {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
Tyler Gunn1e37be52016-07-11 08:54:23 -0700182 CallState.PULLING, CallState.ACTIVE};
183
Tyler Gunnbaf105b2017-04-11 15:21:03 -0700184 /**
185 * These states determine which calls will cause {@link TelecomManager#isInCall()} or
186 * {@link TelecomManager#isInManagedCall()} to return true.
187 *
188 * See also {@link PhoneStateBroadcaster}, which considers a similar set of states as being
189 * off-hook.
190 */
191 public static final int[] ONGOING_CALL_STATES =
192 {CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.PULLING, CallState.ACTIVE,
193 CallState.ON_HOLD, CallState.RINGING};
194
Tyler Gunna90ba732017-01-26 07:24:08 -0800195 private static final int[] ANY_CALL_STATE =
196 {CallState.NEW, CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
197 CallState.RINGING, CallState.ACTIVE, CallState.ON_HOLD, CallState.DISCONNECTED,
198 CallState.ABORTED, CallState.DISCONNECTING, CallState.PULLING};
199
Tyler Gunn8452be02015-09-17 09:57:02 -0700200 public static final String TELECOM_CALL_ID_PREFIX = "TC@";
Santos Cordon91bd74c2014-11-07 16:10:22 -0800201
Hall Liu32587202015-11-18 11:10:08 -0800202 // Maps call technologies in PhoneConstants to those in Analytics.
203 private static final Map<Integer, Integer> sAnalyticsTechnologyMap;
204 static {
205 sAnalyticsTechnologyMap = new HashMap<>(5);
206 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE);
207 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE);
208 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE);
209 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE);
210 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY,
211 Analytics.THIRD_PARTY_PHONE);
212 }
213
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800214 /**
Sailesh Nepale59bb192014-04-01 18:33:59 -0700215 * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
216 * calls are added to the map and removed when the calls move to the disconnected state.
Santos Cordoncf5b2912014-11-05 21:57:54 -0800217 *
Jay Shraunera82c8f72014-08-14 15:49:16 -0700218 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
219 * load factor before resizing, 1 means we only expect a single thread to
220 * access the map so make only a single shard
Santos Cordon681663d2014-01-30 04:32:15 -0800221 */
Jay Shraunera82c8f72014-08-14 15:49:16 -0700222 private final Set<Call> mCalls = Collections.newSetFromMap(
223 new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
Santos Cordon681663d2014-01-30 04:32:15 -0800224
Tyler Gunn8452be02015-09-17 09:57:02 -0700225 /**
Tyler Gunnbbd78a72017-04-30 14:16:07 -0700226 * A pending call is one which requires user-intervention in order to be placed.
227 * Used by {@link #startCallConfirmation(Call)}.
228 */
229 private Call mPendingCall;
230
231 /**
Tyler Gunn8452be02015-09-17 09:57:02 -0700232 * The current telecom call ID. Used when creating new instances of {@link Call}. Should
233 * only be accessed using the {@link #getNextCallId()} method which synchronizes on the
234 * {@link #mLock} sync root.
235 */
236 private int mCallId = 0;
237
Hall Liuaeece4e2017-02-14 16:42:12 -0800238 private int mRttRequestId = 0;
Santos Cordonf0f99f32016-02-18 16:13:57 -0800239 /**
240 * Stores the current foreground user.
241 */
242 private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
243
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700244 private final ConnectionServiceRepository mConnectionServiceRepository;
245 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
246 private final InCallController mInCallController;
Santos Cordonf3671a62014-05-29 21:51:53 -0700247 private final CallAudioManager mCallAudioManager;
Tyler Gunn5d66e1d2018-01-25 20:22:49 -0800248 private final CallRecordingTonePlayer mCallRecordingTonePlayer;
Ihab Awad8de76912015-02-17 12:25:52 -0800249 private RespondViaSmsManager mRespondViaSmsManager;
Santos Cordonf3671a62014-05-29 21:51:53 -0700250 private final Ringer mRinger;
Santos Cordon61e6f352014-12-03 02:53:34 -0800251 private final InCallWakeLockController mInCallWakeLockController;
Jay Shraunera82c8f72014-08-14 15:49:16 -0700252 // For this set initial table size to 16 because we add 13 listeners in
253 // the CallsManager constructor.
254 private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
255 new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
Santos Cordondeb8c892014-05-30 01:38:03 -0700256 private final HeadsetMediaButton mHeadsetMediaButton;
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700257 private final WiredHeadsetManager mWiredHeadsetManager;
Hall Liu486cb192016-10-21 18:23:33 -0700258 private final BluetoothRouteManager mBluetoothRouteManager;
Santos Cordona04bdca2015-03-04 16:57:55 -0800259 private final DockManager mDockManager;
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700260 private final TtyManager mTtyManager;
Yorke Leed1346872014-07-28 14:38:45 -0700261 private final ProximitySensorManager mProximitySensorManager;
Yorke Leef86db2e2014-09-12 17:56:48 -0700262 private final PhoneStateBroadcaster mPhoneStateBroadcaster;
Santos Cordonf193ba42014-09-12 06:37:39 -0700263 private final CallLogManager mCallLogManager;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700264 private final Context mContext;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700265 private final TelecomSystem.SyncRoot mLock;
266 private final ContactsAsyncHelper mContactsAsyncHelper;
Ihab Awadabcbce42015-04-07 14:04:01 -0700267 private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700268 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
269 private final MissedCallNotifier mMissedCallNotifier;
Tyler Gunn2b17f232017-03-08 08:51:00 -0800270 private IncomingCallNotifier mIncomingCallNotifier;
Hall Liu5b70c1c2016-03-03 18:42:57 -0800271 private final CallerInfoLookupHelper mCallerInfoLookupHelper;
Hall Liu7c928322016-12-06 18:15:39 -0800272 private final DefaultDialerCache mDefaultDialerCache;
Hall Liu6d4b66d2016-04-01 16:31:13 -0700273 private final Timeouts.Adapter mTimeoutsAdapter;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700274 private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700275 private final ClockProxy mClockProxy;
Ihab Awad5b281322014-09-25 18:25:29 -0700276 private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
Santos Cordoncf5b2912014-11-05 21:57:54 -0800277 private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
Pengquan Meng4832f202017-12-20 16:13:04 -0800278 private final ConnectionServiceFocusManager mConnectionSvrFocusMgr;
Santos Cordoncf5b2912014-11-05 21:57:54 -0800279 /* Handler tied to thread in which CallManager was initialized. */
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700280 private final Handler mHandler = new Handler(Looper.getMainLooper());
mike dooley66f26d12016-12-19 13:25:47 -0800281 private final EmergencyCallHelper mEmergencyCallHelper;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700282
Pengquan Meng4832f202017-12-20 16:13:04 -0800283 private final ConnectionServiceFocusManager.CallsManagerRequester mRequester =
284 new ConnectionServiceFocusManager.CallsManagerRequester() {
285 @Override
286 public void releaseConnectionService(
287 ConnectionServiceFocusManager.ConnectionServiceFocus connectionService) {
288 mCalls.stream()
289 .filter(c -> c.getConnectionServiceWrapper().equals(connectionService))
Tyler Gunnf4f05392018-03-26 18:57:59 +0000290 .forEach(c -> c.disconnect("release " +
291 connectionService.getComponentName().getPackageName()));
Pengquan Meng4832f202017-12-20 16:13:04 -0800292 }
293
294 @Override
295 public void setCallsManagerListener(CallsManagerListener listener) {
296 mListeners.add(listener);
297 }
298 };
299
Santos Cordon91bd74c2014-11-07 16:10:22 -0800300 private boolean mCanAddCall = true;
301
Brad Ebinger3da57642016-04-04 17:32:19 -0700302 private TelephonyManager.MultiSimVariants mRadioSimVariants = null;
303
Nancy Chena469f762014-12-09 18:29:20 -0800304 private Runnable mStopTone;
305
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700306 /**
Tyler Gunnea4c6fb2017-04-14 13:46:21 -0700307 * Listener to PhoneAccountRegistrar events.
308 */
309 private PhoneAccountRegistrar.Listener mPhoneAccountListener =
310 new PhoneAccountRegistrar.Listener() {
311 public void onPhoneAccountRegistered(PhoneAccountRegistrar registrar,
312 PhoneAccountHandle handle) {
313 broadcastRegisterIntent(handle);
314 }
315 public void onPhoneAccountUnRegistered(PhoneAccountRegistrar registrar,
316 PhoneAccountHandle handle) {
317 broadcastUnregisterIntent(handle);
318 }
319 };
320
321 /**
Tyler Gunnaee51f22018-03-09 02:33:54 +0000322 * Receiver for enhanced call blocking feature to update the emergency call notification
323 * in below cases:
324 * 1) Carrier config changed.
325 * 2) Blocking suppression state changed.
326 */
327 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
328 @Override
329 public void onReceive(Context context, Intent intent) {
330 String action = intent.getAction();
331 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
332 || SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED.equals(action)) {
333 BlockedNumbersUtil.updateEmergencyCallNotification(context,
334 SystemContract.shouldShowEmergencyCallNotification(context));
335 }
336 }
337 };
338
339 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700340 * Initializes the required Telecom components.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800341 */
Tyler Gunnc74b3e22017-11-07 15:03:24 -0800342 @VisibleForTesting
343 public CallsManager(
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700344 Context context,
345 TelecomSystem.SyncRoot lock,
346 ContactsAsyncHelper contactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -0700347 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700348 MissedCallNotifier missedCallNotifier,
349 PhoneAccountRegistrar phoneAccountRegistrar,
350 HeadsetMediaButtonFactory headsetMediaButtonFactory,
351 ProximitySensorManagerFactory proximitySensorManagerFactory,
Hall Liu8fb1fb72015-10-22 15:24:40 -0700352 InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
Pengquan Meng4832f202017-12-20 16:13:04 -0800353 ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory
354 connectionServiceFocusManagerFactory,
Hall Liuf62630a2015-10-27 14:53:39 -0700355 CallAudioManager.AudioServiceFactory audioServiceFactory,
Hall Liu486cb192016-10-21 18:23:33 -0700356 BluetoothRouteManager bluetoothManager,
Santos Cordonf78a72f2016-01-21 22:10:32 +0000357 WiredHeadsetManager wiredHeadsetManager,
Santos Cordon501b9b32016-03-07 14:40:07 -0800358 SystemStateProvider systemStateProvider,
Hall Liu7c928322016-12-06 18:15:39 -0800359 DefaultDialerCache defaultDialerCache,
Hall Liu6d4b66d2016-04-01 16:31:13 -0700360 Timeouts.Adapter timeoutsAdapter,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700361 AsyncRingtonePlayer asyncRingtonePlayer,
Tyler Gunneaaf0742016-09-15 15:36:38 -0700362 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
Brad Ebinger7bba1112017-06-08 13:57:28 -0700363 EmergencyCallHelper emergencyCallHelper,
Tyler Gunn02e00dd2017-08-24 15:44:02 -0700364 InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory,
Tyler Gunnc74b3e22017-11-07 15:03:24 -0800365 ClockProxy clockProxy,
Hall Liu784a4962018-03-06 11:03:17 -0800366 BluetoothStateReceiver bluetoothStateReceiver,
Tyler Gunnc74b3e22017-11-07 15:03:24 -0800367 InCallControllerFactory inCallControllerFactory) {
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700368 mContext = context;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700369 mLock = lock;
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700370 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700371 mContactsAsyncHelper = contactsAsyncHelper;
Ihab Awadabcbce42015-04-07 14:04:01 -0700372 mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700373 mPhoneAccountRegistrar = phoneAccountRegistrar;
Tyler Gunnea4c6fb2017-04-14 13:46:21 -0700374 mPhoneAccountRegistrar.addListener(mPhoneAccountListener);
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700375 mMissedCallNotifier = missedCallNotifier;
376 StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
Hall Liuf62630a2015-10-27 14:53:39 -0700377 mWiredHeadsetManager = wiredHeadsetManager;
Hall Liu7c928322016-12-06 18:15:39 -0800378 mDefaultDialerCache = defaultDialerCache;
Hall Liu486cb192016-10-21 18:23:33 -0700379 mBluetoothRouteManager = bluetoothManager;
Santos Cordona04bdca2015-03-04 16:57:55 -0800380 mDockManager = new DockManager(context);
Hall Liu6d4b66d2016-04-01 16:31:13 -0700381 mTimeoutsAdapter = timeoutsAdapter;
mike dooley66f26d12016-12-19 13:25:47 -0800382 mEmergencyCallHelper = emergencyCallHelper;
Hall Liu5b70c1c2016-03-03 18:42:57 -0800383 mCallerInfoLookupHelper = new CallerInfoLookupHelper(context, mCallerInfoAsyncQueryFactory,
Hall Liu46598272016-04-05 12:58:51 -0700384 mContactsAsyncHelper, mLock);
Hall Liue091ab92015-12-18 17:05:30 -0800385
Hall Liu55076712017-02-28 19:59:30 -0800386 mDtmfLocalTonePlayer =
387 new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy());
Hall Liuf62630a2015-10-27 14:53:39 -0700388 CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
389 context,
390 this,
391 bluetoothManager,
392 wiredHeadsetManager,
393 statusBarNotifier,
Hall Liua3e9dda2015-12-15 17:52:50 -0800394 audioServiceFactory,
Scott Randolph1b97fdd2017-11-01 18:34:19 -0700395 CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT
Hall Liuf62630a2015-10-27 14:53:39 -0700396 );
Hall Liu29855702015-12-11 17:42:03 -0800397 callAudioRouteStateMachine.initialize();
398
Hall Liuf62630a2015-10-27 14:53:39 -0700399 CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter =
400 new CallAudioRoutePeripheralAdapter(
401 callAudioRouteStateMachine,
402 bluetoothManager,
403 wiredHeadsetManager,
404 mDockManager);
405
Hall Liue091ab92015-12-18 17:05:30 -0800406 InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(
Brad Ebinger7bba1112017-06-08 13:57:28 -0700407 callAudioRoutePeripheralAdapter, lock, toneGeneratorFactory);
Hall Liuf62630a2015-10-27 14:53:39 -0700408
Hall Liue091ab92015-12-18 17:05:30 -0800409 SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
Brad Ebingerc9286f42016-03-10 16:02:54 -0800410 RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
Brad Ebingerd931a012015-10-21 12:54:08 -0700411 SystemVibrator systemVibrator = new SystemVibrator(context);
Tyler Gunnc74b3e22017-11-07 15:03:24 -0800412 mInCallController = inCallControllerFactory.create(context, mLock, this,
413 systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
mike dooley66f26d12016-12-19 13:25:47 -0800414 emergencyCallHelper);
Hall Liue091ab92015-12-18 17:05:30 -0800415 mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
Santos Cordonf0f99f32016-02-18 16:13:57 -0800416 ringtoneFactory, systemVibrator, mInCallController);
Tyler Gunn5d66e1d2018-01-25 20:22:49 -0800417 mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext,
418 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), mLock);
Hall Liue091ab92015-12-18 17:05:30 -0800419 mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
Hall Liufbed0162016-01-14 18:06:29 -0800420 this,new CallAudioModeStateMachine((AudioManager)
421 mContext.getSystemService(Context.AUDIO_SERVICE)),
Hall Liu784a4962018-03-06 11:03:17 -0800422 playerFactory, mRinger, new RingbackPlayer(playerFactory),
423 bluetoothStateReceiver, mDtmfLocalTonePlayer);
Hall Liue091ab92015-12-18 17:05:30 -0800424
Pengquan Meng4832f202017-12-20 16:13:04 -0800425 mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
426 mRequester, Looper.getMainLooper());
Hall Liuf62630a2015-10-27 14:53:39 -0700427 mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700428 mTtyManager = new TtyManager(context, mWiredHeadsetManager);
Ihab Awad8de76912015-02-17 12:25:52 -0800429 mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
430 mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
Ta-wei Yen982c0bd2016-04-14 13:59:54 -0700431 mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
Ihab Awad8de76912015-02-17 12:25:52 -0800432 mConnectionServiceRepository =
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700433 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
Ihab Awad8de76912015-02-17 12:25:52 -0800434 mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700435 mClockProxy = clockProxy;
Santos Cordonae193062014-05-21 21:21:49 -0700436
Tyler Gunnae694b12015-11-11 17:46:03 -0800437 mListeners.add(mInCallWakeLockController);
Santos Cordondeb8c892014-05-30 01:38:03 -0700438 mListeners.add(statusBarNotifier);
Santos Cordonf193ba42014-09-12 06:37:39 -0700439 mListeners.add(mCallLogManager);
Yorke Leef86db2e2014-09-12 17:56:48 -0700440 mListeners.add(mPhoneStateBroadcaster);
Santos Cordonf3671a62014-05-29 21:51:53 -0700441 mListeners.add(mInCallController);
Santos Cordona56f2762014-03-24 15:55:53 -0700442 mListeners.add(mCallAudioManager);
Tyler Gunn5d66e1d2018-01-25 20:22:49 -0800443 mListeners.add(mCallRecordingTonePlayer);
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700444 mListeners.add(missedCallNotifier);
Santos Cordon81289982014-06-03 16:03:08 -0700445 mListeners.add(mHeadsetMediaButton);
Yorke Leed1346872014-07-28 14:38:45 -0700446 mListeners.add(mProximitySensorManager);
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700447
Tony Maka9930942016-01-15 10:57:14 +0000448 // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
449 final UserManager userManager = UserManager.get(mContext);
450 // Don't load missed call if it is run in split user model.
451 if (userManager.isPrimaryUser()) {
452 onUserSwitch(Process.myUserHandle());
453 }
Tyler Gunnaee51f22018-03-09 02:33:54 +0000454 // Register BroadcastReceiver to handle enhanced call blocking feature related event.
455 IntentFilter intentFilter = new IntentFilter(
456 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
457 intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
458 context.registerReceiver(mReceiver, intentFilter);
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800459 }
460
Tyler Gunn2b17f232017-03-08 08:51:00 -0800461 public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
462 if (mIncomingCallNotifier != null) {
463 mListeners.remove(mIncomingCallNotifier);
464 }
465 mIncomingCallNotifier = incomingCallNotifier;
466 mListeners.add(mIncomingCallNotifier);
467 }
468
Ihab Awad8de76912015-02-17 12:25:52 -0800469 public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
470 if (mRespondViaSmsManager != null) {
471 mListeners.remove(mRespondViaSmsManager);
472 }
473 mRespondViaSmsManager = respondViaSmsManager;
474 mListeners.add(respondViaSmsManager);
475 }
476
477 public RespondViaSmsManager getRespondViaSmsManager() {
478 return mRespondViaSmsManager;
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800479 }
480
Hall Liu5b70c1c2016-03-03 18:42:57 -0800481 public CallerInfoLookupHelper getCallerInfoLookupHelper() {
482 return mCallerInfoLookupHelper;
483 }
484
Santos Cordon766d04f2014-05-06 10:28:25 -0700485 @Override
Ihab Awad6fb37c82014-08-07 19:48:57 -0700486 public void onSuccessfulOutgoingCall(Call call, int callState) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700487 Log.v(this, "onSuccessfulOutgoingCall, %s", call);
Yorke Leeb701f6d2014-08-12 14:04:19 -0700488
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700489 setCallState(call, callState, "successful outgoing call");
Yorke Leeb701f6d2014-08-12 14:04:19 -0700490 if (!mCalls.contains(call)) {
491 // Call was not added previously in startOutgoingCall due to it being a potential MMI
492 // code, so add it now.
493 addCall(call);
494 }
495
496 // The call's ConnectionService has been updated.
497 for (CallsManagerListener listener : mListeners) {
498 listener.onConnectionServiceChanged(call, null, call.getConnectionService());
Santos Cordon766d04f2014-05-06 10:28:25 -0700499 }
Santos Cordon6cb7ba92014-05-23 14:09:32 -0700500
501 markCallAsDialing(call);
Santos Cordon766d04f2014-05-06 10:28:25 -0700502 }
503
504 @Override
Andrew Lee701dc002014-09-11 21:29:12 -0700505 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700506 Log.v(this, "onFailedOutgoingCall, call: %s", call);
Yorke Leeb701f6d2014-08-12 14:04:19 -0700507
Andrew Leee6288a72014-10-01 13:50:02 -0700508 markCallAsRemoved(call);
Sailesh Nepal5a73b032014-06-25 15:53:21 -0700509 }
510
511 @Override
Hall Liu6d4b66d2016-04-01 16:31:13 -0700512 public void onSuccessfulIncomingCall(Call incomingCall) {
Santos Cordon766d04f2014-05-06 10:28:25 -0700513 Log.d(this, "onSuccessfulIncomingCall");
Hall Liuddf3f9c2016-08-30 13:38:52 -0700514 if (incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
515 Log.i(this, "Skipping call filtering due to ECBM");
516 onCallFilteringComplete(incomingCall, new CallFilteringResult(true, false, true, true));
517 return;
518 }
519
Hall Liu6d4b66d2016-04-01 16:31:13 -0700520 List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();
521 filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));
Tyler Gunnaee51f22018-03-09 02:33:54 +0000522 filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter(),
523 mCallerInfoLookupHelper));
Hall Liu6d4b66d2016-04-01 16:31:13 -0700524 filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar,
Hall Liu7c928322016-12-06 18:15:39 -0800525 mDefaultDialerCache, new ParcelableCallUtils.Converter(), mLock));
Hall Liu6d4b66d2016-04-01 16:31:13 -0700526 new IncomingCallFilter(mContext, this, incomingCall, mLock,
527 mTimeoutsAdapter, filters).performFiltering();
Sailesh Nepalae925952016-01-24 18:56:58 -0800528 }
529
530 @Override
Hall Liu6d4b66d2016-04-01 16:31:13 -0700531 public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) {
Hall Liue091ab92015-12-18 17:05:30 -0800532 // Only set the incoming call as ringing if it isn't already disconnected. It is possible
Tyler Gunne6276e12015-10-22 20:48:50 -0700533 // that the connection service disconnected the call before it was even added to Telecom, in
534 // which case it makes no sense to set it back to a ringing state.
535 if (incomingCall.getState() != CallState.DISCONNECTED &&
536 incomingCall.getState() != CallState.DISCONNECTING) {
537 setCallState(incomingCall, CallState.RINGING,
Hall Liu6d4b66d2016-04-01 16:31:13 -0700538 result.shouldAllowCall ? "successful incoming call" : "blocking call");
Tyler Gunne6276e12015-10-22 20:48:50 -0700539 } else {
Hall Liu6d4b66d2016-04-01 16:31:13 -0700540 Log.i(this, "onCallFilteringCompleted: call already disconnected.");
Tyler Gunn2ecfd882016-09-16 15:32:11 -0700541 return;
Tyler Gunne6276e12015-10-22 20:48:50 -0700542 }
Santos Cordonf193ba42014-09-12 06:37:39 -0700543
Hall Liu6d4b66d2016-04-01 16:31:13 -0700544 if (result.shouldAllowCall) {
Tyler Gunna90ba732017-01-26 07:24:08 -0800545 if (hasMaximumManagedRingingCalls(incomingCall)) {
Wei Liu13791b92016-10-04 17:46:44 -0700546 if (shouldSilenceInsteadOfReject(incomingCall)) {
547 incomingCall.silence();
548 } else {
549 Log.i(this, "onCallFilteringCompleted: Call rejected! " +
550 "Exceeds maximum number of ringing calls.");
551 rejectCallAndLog(incomingCall);
552 }
Tyler Gunna90ba732017-01-26 07:24:08 -0800553 } else if (hasMaximumManagedDialingCalls(incomingCall)) {
Hall Liu6d4b66d2016-04-01 16:31:13 -0700554 Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " +
Sailesh Nepalae925952016-01-24 18:56:58 -0800555 "dialing calls.");
556 rejectCallAndLog(incomingCall);
557 } else {
558 addCall(incomingCall);
559 }
Santos Cordonf193ba42014-09-12 06:37:39 -0700560 } else {
Hall Liu6d4b66d2016-04-01 16:31:13 -0700561 if (result.shouldReject) {
562 Log.i(this, "onCallFilteringCompleted: blocked call, rejecting.");
Sailesh Nepalae925952016-01-24 18:56:58 -0800563 incomingCall.reject(false, null);
564 }
Hall Liu6d4b66d2016-04-01 16:31:13 -0700565 if (result.shouldAddToCallLog) {
Sailesh Nepalae925952016-01-24 18:56:58 -0800566 Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log.");
Hall Liu6d4b66d2016-04-01 16:31:13 -0700567 if (result.shouldShowNotification) {
Ta-wei Yen982c0bd2016-04-14 13:59:54 -0700568 Log.w(this, "onCallScreeningCompleted: blocked call, showing notification.");
569 }
Hall Liu6d4b66d2016-04-01 16:31:13 -0700570 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
571 result.shouldShowNotification);
572 } else if (result.shouldShowNotification) {
Sailesh Nepalae925952016-01-24 18:56:58 -0800573 Log.i(this, "onCallScreeningCompleted: blocked call, showing notification.");
Hall Liu3037ac62016-09-22 14:40:03 -0700574 mMissedCallNotifier.showMissedCallNotification(
575 new MissedCallNotifier.CallInfo(incomingCall));
Sailesh Nepalae925952016-01-24 18:56:58 -0800576 }
Santos Cordonf193ba42014-09-12 06:37:39 -0700577 }
Santos Cordon766d04f2014-05-06 10:28:25 -0700578 }
579
Wei Liu13791b92016-10-04 17:46:44 -0700580 /**
581 * Whether allow (silence rather than reject) the incoming call if it has a different source
582 * (connection service) from the existing ringing call when reaching maximum ringing calls.
583 */
584 private boolean shouldSilenceInsteadOfReject(Call incomingCall) {
585 if (!mContext.getResources().getBoolean(
586 R.bool.silence_incoming_when_different_service_and_maximum_ringing)) {
587 return false;
588 }
589
590 Call ringingCall = null;
591
592 for (Call call : mCalls) {
593 // Only operate on top-level calls
594 if (call.getParentCall() != null) {
595 continue;
596 }
597
598 if (call.isExternalCall()) {
599 continue;
600 }
601
602 if (CallState.RINGING == call.getState() &&
603 call.getConnectionService() == incomingCall.getConnectionService()) {
604 return false;
605 }
606 }
607
608 return true;
609 }
610
Santos Cordon766d04f2014-05-06 10:28:25 -0700611 @Override
612 public void onFailedIncomingCall(Call call) {
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700613 setCallState(call, CallState.DISCONNECTED, "failed incoming call");
Santos Cordon766d04f2014-05-06 10:28:25 -0700614 call.removeListener(this);
Ben Gilada0d9f752014-02-26 11:49:03 -0800615 }
616
Ihab Awadcb387ac2014-05-28 16:49:38 -0700617 @Override
Yorke Lee9250e5f2014-10-01 13:39:09 -0700618 public void onSuccessfulUnknownCall(Call call, int callState) {
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700619 setCallState(call, callState, "successful unknown call");
Yorke Lee9250e5f2014-10-01 13:39:09 -0700620 Log.i(this, "onSuccessfulUnknownCall for call %s", call);
621 addCall(call);
622 }
623
624 @Override
625 public void onFailedUnknownCall(Call call) {
626 Log.i(this, "onFailedUnknownCall for call %s", call);
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700627 setCallState(call, CallState.DISCONNECTED, "failed unknown call");
Yorke Lee9250e5f2014-10-01 13:39:09 -0700628 call.removeListener(this);
629 }
630
631 @Override
Andrew Lee5be64bc2014-09-08 18:35:15 -0700632 public void onRingbackRequested(Call call, boolean ringback) {
Ihab Awadcb387ac2014-05-28 16:49:38 -0700633 for (CallsManagerListener listener : mListeners) {
Andrew Lee5be64bc2014-09-08 18:35:15 -0700634 listener.onRingbackRequested(call, ringback);
Ihab Awadcb387ac2014-05-28 16:49:38 -0700635 }
636 }
637
Evan Charlton352105c2014-06-03 14:10:54 -0700638 @Override
639 public void onPostDialWait(Call call, String remaining) {
640 mInCallController.onPostDialWait(call, remaining);
641 }
642
Santos Cordona1610702014-06-04 20:22:56 -0700643 @Override
Nancy Chena469f762014-12-09 18:29:20 -0800644 public void onPostDialChar(final Call call, char nextChar) {
645 if (PhoneNumberUtils.is12Key(nextChar)) {
646 // Play tone if it is one of the dialpad digits, canceling out the previously queued
647 // up stopTone runnable since playing a new tone automatically stops the previous tone.
648 if (mStopTone != null) {
Brad Ebingere62e9e82016-02-01 18:26:40 -0800649 mHandler.removeCallbacks(mStopTone.getRunnableToCancel());
650 mStopTone.cancel();
Nancy Chena469f762014-12-09 18:29:20 -0800651 }
652
653 mDtmfLocalTonePlayer.playTone(call, nextChar);
654
Brad Ebingerf5e06662016-08-25 16:16:27 -0700655 mStopTone = new Runnable("CM.oPDC", mLock) {
Nancy Chena469f762014-12-09 18:29:20 -0800656 @Override
Brad Ebingere62e9e82016-02-01 18:26:40 -0800657 public void loggedRun() {
Brad Ebingerf5e06662016-08-25 16:16:27 -0700658 // Set a timeout to stop the tone in case there isn't another tone to
659 // follow.
660 mDtmfLocalTonePlayer.stopTone(call);
Nancy Chena469f762014-12-09 18:29:20 -0800661 }
662 };
Brad Ebingere62e9e82016-02-01 18:26:40 -0800663 mHandler.postDelayed(mStopTone.prepare(),
Nancy Chena469f762014-12-09 18:29:20 -0800664 Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
665 } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
666 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
667 // Stop the tone if a tone is playing, removing any other stopTone callbacks since
668 // the previous tone is being stopped anyway.
669 if (mStopTone != null) {
Brad Ebingere62e9e82016-02-01 18:26:40 -0800670 mHandler.removeCallbacks(mStopTone.getRunnableToCancel());
671 mStopTone.cancel();
Nancy Chena469f762014-12-09 18:29:20 -0800672 }
673 mDtmfLocalTonePlayer.stopTone(call);
674 } else {
675 Log.w(this, "onPostDialChar: invalid value %d", nextChar);
676 }
677 }
678
679 @Override
Santos Cordona1610702014-06-04 20:22:56 -0700680 public void onParentChanged(Call call) {
Santos Cordon66fe8822014-10-10 16:10:58 -0700681 // parent-child relationship affects which call should be foreground, so do an update.
Hall Liufc130b22016-06-15 17:54:48 -0700682 updateCanAddCall();
Santos Cordona1610702014-06-04 20:22:56 -0700683 for (CallsManagerListener listener : mListeners) {
684 listener.onIsConferencedChanged(call);
685 }
686 }
687
688 @Override
689 public void onChildrenChanged(Call call) {
Santos Cordon66fe8822014-10-10 16:10:58 -0700690 // parent-child relationship affects which call should be foreground, so do an update.
Hall Liufc130b22016-06-15 17:54:48 -0700691 updateCanAddCall();
Santos Cordona1610702014-06-04 20:22:56 -0700692 for (CallsManagerListener listener : mListeners) {
693 listener.onIsConferencedChanged(call);
694 }
695 }
696
Ihab Awadff7493a2014-06-10 13:47:44 -0700697 @Override
Andrew Lee5be64bc2014-09-08 18:35:15 -0700698 public void onIsVoipAudioModeChanged(Call call) {
Sailesh Nepal7e669572014-07-08 21:29:12 -0700699 for (CallsManagerListener listener : mListeners) {
Andrew Lee5be64bc2014-09-08 18:35:15 -0700700 listener.onIsVoipAudioModeChanged(call);
Sailesh Nepal7e669572014-07-08 21:29:12 -0700701 }
702 }
703
Andrew Lee4a796602014-07-11 17:23:03 -0700704 @Override
Hall Liu9696c212016-06-24 16:09:02 -0700705 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
Andrew Lee4a796602014-07-11 17:23:03 -0700706 for (CallsManagerListener listener : mListeners) {
Hall Liu9696c212016-06-24 16:09:02 -0700707 listener.onVideoStateChanged(call, previousVideoState, newVideoState);
Andrew Lee4a796602014-07-11 17:23:03 -0700708 }
709 }
710
Santos Cordoncf5b2912014-11-05 21:57:54 -0800711 @Override
Hall Liu63e690c2017-02-14 18:05:03 -0800712 public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call,
713 long disconnectionTimeout) {
Santos Cordoncf5b2912014-11-05 21:57:54 -0800714 mPendingCallsToDisconnect.add(call);
Brad Ebingerf5e06662016-08-25 16:16:27 -0700715 mHandler.postDelayed(new Runnable("CM.oCVNOCB", mLock) {
Santos Cordoncf5b2912014-11-05 21:57:54 -0800716 @Override
Brad Ebingere62e9e82016-02-01 18:26:40 -0800717 public void loggedRun() {
Brad Ebingerf5e06662016-08-25 16:16:27 -0700718 if (mPendingCallsToDisconnect.remove(call)) {
719 Log.i(this, "Delayed disconnection of call: %s", call);
720 call.disconnect();
Santos Cordoncf5b2912014-11-05 21:57:54 -0800721 }
722 }
Hall Liu63e690c2017-02-14 18:05:03 -0800723 }.prepare(), disconnectionTimeout);
Santos Cordoncf5b2912014-11-05 21:57:54 -0800724
725 return true;
726 }
727
Tyler Gunn86014fc2015-06-12 14:31:25 -0700728 /**
729 * Handles changes to the {@link Connection.VideoProvider} for a call. Adds the
730 * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created
731 * in {@link Call#setVideoProvider(IVideoProvider)}. This allows the {@link CallsManager} to
732 * respond to callbacks from the {@link VideoProviderProxy}.
733 *
734 * @param call The call.
735 */
736 @Override
737 public void onVideoCallProviderChanged(Call call) {
738 VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy();
739
740 if (videoProviderProxy == null) {
741 return;
742 }
743
744 videoProviderProxy.addListener(this);
745 }
746
747 /**
748 * Handles session modification requests received via the {@link TelecomVideoCallCallback} for
749 * a call. Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session
750 * modification request.
751 *
752 * @param call The call.
753 * @param videoProfile The {@link VideoProfile}.
754 */
755 @Override
756 public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
757 int videoState = videoProfile != null ? videoProfile.getVideoState() :
758 VideoProfile.STATE_AUDIO_ONLY;
759 Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
760 .videoStateToString(videoState));
761
762 for (CallsManagerListener listener : mListeners) {
763 listener.onSessionModifyRequestReceived(call, videoProfile);
764 }
765 }
766
Brad Ebinger53855132015-10-30 10:58:19 -0700767 public Collection<Call> getCalls() {
Santos Cordon4bc02452014-11-19 15:51:12 -0800768 return Collections.unmodifiableCollection(mCalls);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700769 }
770
Tyler Gunndb821912016-02-16 14:35:25 -0800771 /**
772 * Play or stop a call hold tone for a call. Triggered via
773 * {@link Connection#sendConnectionEvent(String)} when the
774 * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
775 * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
776 *
777 * @param call The call which requested the hold tone.
778 */
779 @Override
780 public void onHoldToneRequested(Call call) {
781 for (CallsManagerListener listener : mListeners) {
782 listener.onHoldToneRequested(call);
783 }
784 }
785
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700786 /**
787 * A {@link Call} managed by the {@link CallsManager} has requested a handover to another
788 * {@link PhoneAccount}.
789 * @param call The call.
790 * @param handoverTo The {@link PhoneAccountHandle} to handover the call to.
791 * @param videoState The desired video state of the call after handover.
Tyler Gunn6f6f1c52017-04-17 18:22:04 -0700792 * @param extras
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700793 */
794 @Override
Tyler Gunn6f6f1c52017-04-17 18:22:04 -0700795 public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -0800796 Bundle extras, boolean isLegacy) {
797 if (isLegacy) {
798 requestHandoverViaEvents(call, handoverTo, videoState, extras);
799 } else {
800 requestHandover(call, handoverTo, videoState, extras);
801 }
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700802 }
803
Brad Ebingerd931a012015-10-21 12:54:08 -0700804 @VisibleForTesting
805 public Call getForegroundCall() {
Hall Liue091ab92015-12-18 17:05:30 -0800806 if (mCallAudioManager == null) {
807 // Happens when getForegroundCall is called before full initialization.
808 return null;
809 }
810 return mCallAudioManager.getForegroundCall();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700811 }
812
Tyler Gunn1bf0e6b2016-11-30 15:19:13 -0800813 @Override
Santos Cordonf0f99f32016-02-18 16:13:57 -0800814 public UserHandle getCurrentUserHandle() {
815 return mCurrentUserHandle;
816 }
817
Hall Liu9ecbb1c2016-04-14 14:35:48 -0700818 public CallAudioManager getCallAudioManager() {
Hall Liue091ab92015-12-18 17:05:30 -0800819 return mCallAudioManager;
Santos Cordonae193062014-05-21 21:21:49 -0700820 }
821
Santos Cordonf3671a62014-05-29 21:51:53 -0700822 InCallController getInCallController() {
823 return mInCallController;
824 }
825
mike dooley66f26d12016-12-19 13:25:47 -0800826 EmergencyCallHelper getEmergencyCallHelper() {
827 return mEmergencyCallHelper;
828 }
829
Santos Cordonf0f99f32016-02-18 16:13:57 -0800830 @VisibleForTesting
831 public boolean hasEmergencyCall() {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700832 for (Call call : mCalls) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700833 if (call.isEmergencyCall()) {
834 return true;
835 }
836 }
837 return false;
838 }
839
Hall Liuccd793f2018-03-16 14:48:59 -0700840 public boolean hasEmergencyRttCall() {
841 for (Call call : mCalls) {
842 if (call.isEmergencyCall() && call.isRttCall()) {
843 return true;
844 }
845 }
846 return false;
847 }
848
Honggangb3ccbb32016-05-31 14:46:22 +0800849 @VisibleForTesting
850 public boolean hasOnlyDisconnectedCalls() {
851 if (mCalls.size() == 0) {
852 return false;
853 }
Roshan Pius669f90b2015-09-02 16:21:39 -0700854 for (Call call : mCalls) {
855 if (!call.isDisconnected()) {
856 return false;
857 }
858 }
859 return true;
860 }
861
Hall Liu5b8551e2017-03-10 17:05:23 -0800862 public boolean hasVideoCall() {
Andrew Lee45506232014-10-22 17:54:33 -0700863 for (Call call : mCalls) {
Tyler Gunn467b64f2015-06-09 13:48:47 -0700864 if (VideoProfile.isVideo(call.getVideoState())) {
Andrew Lee45506232014-10-22 17:54:33 -0700865 return true;
866 }
867 }
868 return false;
869 }
870
Hall Liu28b82f02016-07-26 17:38:56 -0700871 @VisibleForTesting
872 public CallAudioState getAudioState() {
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700873 return mCallAudioManager.getCallAudioState();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700874 }
875
Sailesh Nepalb88795a2014-07-15 14:53:27 -0700876 boolean isTtySupported() {
877 return mTtyManager.isTtySupported();
878 }
879
880 int getCurrentTtyMode() {
881 return mTtyManager.getCurrentTtyMode();
882 }
883
Brad Ebinger53855132015-10-30 10:58:19 -0700884 @VisibleForTesting
885 public void addListener(CallsManagerListener listener) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700886 mListeners.add(listener);
887 }
888
889 void removeListener(CallsManagerListener listener) {
890 mListeners.remove(listener);
891 }
892
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800893 /**
Sailesh Nepal664837f2014-07-14 16:31:51 -0700894 * Starts the process to attach the call to a connection service.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800895 *
Nancy Chenbc9ef172014-08-15 17:11:57 -0700896 * @param phoneAccountHandle The phone account which contains the component name of the
897 * connection service to use for this call.
Evan Charltona05805b2014-03-05 08:21:46 -0800898 * @param extras The optional extras Bundle passed with the intent used for the incoming call.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800899 */
Evan Charlton89176372014-07-19 18:23:09 -0700900 void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800901 Log.d(this, "processIncomingCallIntent");
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700902 boolean isHandover = extras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER);
Yorke Lee69fa8972015-06-08 11:25:32 -0700903 Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
904 if (handle == null) {
905 // Required for backwards compatibility
906 handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
907 }
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700908 Call call = new Call(
Tyler Gunn8452be02015-09-17 09:57:02 -0700909 getNextCallId(),
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700910 mContext,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800911 this,
Ihab Awade6dbc9d2015-03-26 10:33:44 -0700912 mLock,
Sailesh Nepal664837f2014-07-14 16:31:51 -0700913 mConnectionServiceRepository,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700914 mContactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -0700915 mCallerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -0700916 mPhoneNumberUtilsAdapter,
Sailesh Nepal8aa6a002014-09-12 10:52:49 -0700917 handle,
Sailesh Nepal664837f2014-07-14 16:31:51 -0700918 null /* gatewayInfo */,
Ihab Awadb78b2762014-07-25 15:16:23 -0700919 null /* connectionManagerPhoneAccount */,
Evan Charlton89176372014-07-19 18:23:09 -0700920 phoneAccountHandle,
Hall Liu32587202015-11-18 11:10:08 -0800921 Call.CALL_DIRECTION_INCOMING /* callDirection */,
922 false /* forceAttachToExistingConnection */,
Tyler Gunn8bb2b012017-08-04 09:28:59 -0700923 false, /* isConference */
924 mClockProxy);
Hall Liu32587202015-11-18 11:10:08 -0800925
Tyler Gunnacb3bc82017-01-09 09:43:56 -0800926 // Ensure new calls related to self-managed calls/connections are set as such. This will
927 // be overridden when the actual connection is returned in startCreateConnection, however
928 // doing this now ensures the logs and any other logic will treat this call as self-managed
929 // from the moment it is created.
930 PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
931 phoneAccountHandle);
932 if (phoneAccount != null) {
933 call.setIsSelfManaged(phoneAccount.isSelfManaged());
Tyler Gunn2b17f232017-03-08 08:51:00 -0800934 if (call.isSelfManaged()) {
935 // Self managed calls will always be voip audio mode.
936 call.setIsVoipAudioMode(true);
Pengquan Mengfc9c8ff2018-01-17 05:50:49 +0000937 } else {
Pengquan Meng4832f202017-12-20 16:13:04 -0800938 // Incoming call is managed, the active call is self-managed and can't be held.
939 // We need to set extras on it to indicate whether answering will cause a
940 // active self-managed call to drop.
941 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
942 if (activeCall != null && !canHold(activeCall) && activeCall.isSelfManaged()) {
Pengquan Mengfc9c8ff2018-01-17 05:50:49 +0000943 Bundle dropCallExtras = new Bundle();
944 dropCallExtras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
Tyler Gunn2b17f232017-03-08 08:51:00 -0800945
Pengquan Mengfc9c8ff2018-01-17 05:50:49 +0000946 // Include the name of the app which will drop the call.
Pengquan Meng4832f202017-12-20 16:13:04 -0800947 CharSequence droppedApp = activeCall.getTargetPhoneAccountLabel();
948 dropCallExtras.putCharSequence(
949 Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME, droppedApp);
950 Log.i(this, "Incoming managed call will drop %s call.", droppedApp);
Pengquan Mengfc9c8ff2018-01-17 05:50:49 +0000951 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, dropCallExtras);
Tyler Gunn2b17f232017-03-08 08:51:00 -0800952 }
953 }
Srikanth Chintala5b8facb2015-02-17 11:39:21 +0530954
Tony Mak59a314c2017-06-20 16:31:44 +0100955 if (extras.getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
Srikanth Chintala5b8facb2015-02-17 11:39:21 +0530956 Log.d(this, "processIncomingCallIntent: defaulting to voip mode for call %s",
957 call.getId());
958 call.setIsVoipAudioMode(true);
959 }
Tyler Gunnacb3bc82017-01-09 09:43:56 -0800960 }
Hall Liudb81e6a2018-03-12 18:28:37 -0700961 if (isRttSettingOn() ||
Hall Liufb8d5ab2018-01-29 17:34:54 -0800962 extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
Hall Liuf71cca92018-02-27 13:05:25 -0800963 Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn());
Hall Liudd68bc32017-01-25 17:14:23 -0800964 if (phoneAccount != null &&
965 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
Hall Liu996ecdb2018-02-09 16:48:57 -0800966 call.createRttStreams();
Hall Liudd68bc32017-01-25 17:14:23 -0800967 }
Hall Liufb8d5ab2018-01-29 17:34:54 -0800968 // Even if the phone account doesn't support RTT yet, the connection manager might
969 // change that. Set this to check it later.
970 call.setRequestedToStartWithRtt();
Hall Liudd68bc32017-01-25 17:14:23 -0800971 }
Tyler Gunn2b17f232017-03-08 08:51:00 -0800972 // If the extras specifies a video state, set it on the call if the PhoneAccount supports
973 // video.
Tyler Gunnb821f922017-06-21 14:40:19 -0700974 int videoState = VideoProfile.STATE_AUDIO_ONLY;
Tyler Gunn2b17f232017-03-08 08:51:00 -0800975 if (extras.containsKey(TelecomManager.EXTRA_INCOMING_VIDEO_STATE) &&
976 phoneAccount != null && phoneAccount.hasCapabilities(
977 PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
Tyler Gunnb821f922017-06-21 14:40:19 -0700978 videoState = extras.getInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE);
979 call.setVideoState(videoState);
Tyler Gunn2b17f232017-03-08 08:51:00 -0800980 }
Tyler Gunnacb3bc82017-01-09 09:43:56 -0800981
Hall Liu32587202015-11-18 11:10:08 -0800982 call.initAnalytics();
Hall Liue091ab92015-12-18 17:05:30 -0800983 if (getForegroundCall() != null) {
984 getForegroundCall().getAnalytics().setCallIsInterrupted(true);
Hall Liu32587202015-11-18 11:10:08 -0800985 call.getAnalytics().setCallIsAdditional(true);
986 }
Sailesh Nepal703a1af2016-04-14 20:10:12 -0700987 setIntentExtrasAndStartTime(call, extras);
Santos Cordondf399862014-08-06 04:39:15 -0700988 // TODO: Move this to be a part of addCall()
Santos Cordon766d04f2014-05-06 10:28:25 -0700989 call.addListener(this);
Tyler Gunna90ba732017-01-26 07:24:08 -0800990
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700991 boolean isHandoverAllowed = true;
992 if (isHandover) {
993 if (!isHandoverInProgress() &&
994 isHandoverToPhoneAccountSupported(phoneAccountHandle)) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700995 final String handleScheme = handle.getSchemeSpecificPart();
996 Call fromCall = mCalls.stream()
997 .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber(
998 c.getHandle().getSchemeSpecificPart(), handleScheme))
999 .findFirst()
1000 .orElse(null);
1001 if (fromCall != null) {
1002 if (!isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount())) {
1003 Log.w(this, "processIncomingCallIntent: From account doesn't support " +
Tyler Gunn141ef582017-05-26 13:38:13 -07001004 "handover.");
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001005 isHandoverAllowed = false;
1006 }
1007 } else {
1008 Log.w(this, "processIncomingCallIntent: handover fail; can't find from call.");
1009 isHandoverAllowed = false;
1010 }
1011
1012 if (isHandoverAllowed) {
1013 // Link the calls so we know we're handing over.
Tyler Gunn141ef582017-05-26 13:38:13 -07001014 fromCall.setHandoverDestinationCall(call);
1015 call.setHandoverSourceCall(fromCall);
1016 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
1017 fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001018 Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER,
1019 "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId());
1020 Log.addEvent(call, LogUtils.Events.START_HANDOVER,
1021 "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId());
Tyler Gunnb821f922017-06-21 14:40:19 -07001022 if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) {
1023 // Ensure when the call goes active that it will go to speakerphone if the
1024 // handover to call is a video call.
1025 call.setStartWithSpeakerphoneOn(true);
1026 }
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001027 }
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07001028 } else {
1029 Log.w(this, "processIncomingCallIntent: To account doesn't support handover.");
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001030 }
1031 }
Tyler Gunn141ef582017-05-26 13:38:13 -07001032
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07001033 if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call,
1034 call.getTargetPhoneAccount()))) {
Tyler Gunna90ba732017-01-26 07:24:08 -08001035 notifyCreateConnectionFailed(phoneAccountHandle, call);
1036 } else {
1037 call.startCreateConnection(mPhoneAccountRegistrar);
1038 }
Ben Gilada0d9f752014-02-26 11:49:03 -08001039 }
1040
Yorke Lee9250e5f2014-10-01 13:39:09 -07001041 void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
1042 Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
1043 Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
1044 Call call = new Call(
Tyler Gunn8452be02015-09-17 09:57:02 -07001045 getNextCallId(),
Yorke Lee9250e5f2014-10-01 13:39:09 -07001046 mContext,
Ihab Awad78a5e6b2015-02-06 10:13:05 -08001047 this,
Ihab Awade6dbc9d2015-03-26 10:33:44 -07001048 mLock,
Yorke Lee9250e5f2014-10-01 13:39:09 -07001049 mConnectionServiceRepository,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -07001050 mContactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -07001051 mCallerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07001052 mPhoneNumberUtilsAdapter,
Yorke Lee9250e5f2014-10-01 13:39:09 -07001053 handle,
1054 null /* gatewayInfo */,
1055 null /* connectionManagerPhoneAccount */,
1056 phoneAccountHandle,
Hall Liu32587202015-11-18 11:10:08 -08001057 Call.CALL_DIRECTION_UNKNOWN /* callDirection */,
Yorke Lee9250e5f2014-10-01 13:39:09 -07001058 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
1059 // to the existing connection instead of trying to create a new one.
Hall Liu32587202015-11-18 11:10:08 -08001060 true /* forceAttachToExistingConnection */,
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001061 false, /* isConference */
1062 mClockProxy);
Hall Liu32587202015-11-18 11:10:08 -08001063 call.initAnalytics();
1064
Sailesh Nepal703a1af2016-04-14 20:10:12 -07001065 setIntentExtrasAndStartTime(call, extras);
Yorke Lee9250e5f2014-10-01 13:39:09 -07001066 call.addListener(this);
1067 call.startCreateConnection(mPhoneAccountRegistrar);
1068 }
1069
Yorke Lee8d3f2692015-05-08 11:44:39 -07001070 private boolean areHandlesEqual(Uri handle1, Uri handle2) {
1071 if (handle1 == null || handle2 == null) {
1072 return handle1 == handle2;
1073 }
1074
1075 if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) {
1076 return false;
1077 }
1078
1079 final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart());
1080 final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart());
1081 return TextUtils.equals(number1, number2);
1082 }
1083
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001084 private Call reuseOutgoingCall(Uri handle) {
1085 // Check to see if we can reuse any of the calls that are waiting to disconnect.
Santos Cordoncf5b2912014-11-05 21:57:54 -08001086 // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
Yorke Lee59979512014-12-02 01:03:40 -08001087 Call reusedCall = null;
Brad Ebingere680e662016-08-26 11:45:43 -07001088 for (Iterator<Call> callIter = mPendingCallsToDisconnect.iterator(); callIter.hasNext();) {
1089 Call pendingCall = callIter.next();
Yorke Lee8d3f2692015-05-08 11:44:39 -07001090 if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) {
Brad Ebingere680e662016-08-26 11:45:43 -07001091 callIter.remove();
Santos Cordoncf5b2912014-11-05 21:57:54 -08001092 Log.i(this, "Reusing disconnected call %s", pendingCall);
Yorke Lee59979512014-12-02 01:03:40 -08001093 reusedCall = pendingCall;
1094 } else {
Yorke Lee8d3f2692015-05-08 11:44:39 -07001095 Log.i(this, "Not reusing disconnected call %s", pendingCall);
Hall Liu10139b22017-03-06 19:28:04 -08001096 callIter.remove();
Yorke Lee59979512014-12-02 01:03:40 -08001097 pendingCall.disconnect();
Santos Cordoncf5b2912014-11-05 21:57:54 -08001098 }
1099 }
1100
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001101 return reusedCall;
Santos Cordoncf5b2912014-11-05 21:57:54 -08001102 }
1103
Nancy Chen0d3076c2014-07-30 14:45:44 -07001104 /**
Tyler Gunna90ba732017-01-26 07:24:08 -08001105 * Kicks off the first steps to creating an outgoing call.
1106 *
1107 * For managed connections, this is the first step to launching the Incall UI.
1108 * For self-managed connections, we don't expect the Incall UI to launch, but this is still a
1109 * first step in getting the self-managed ConnectionService to create the connection.
Nancy Chena9d91da2014-08-12 14:31:06 -07001110 * @param handle Handle to connect the call with.
Nancy Chenbc9ef172014-08-15 17:11:57 -07001111 * @param phoneAccountHandle The phone account which contains the component name of the
1112 * connection service to use for this call.
1113 * @param extras The optional extras Bundle passed with the intent used for the incoming call.
Tony Mak578a4e62015-11-23 11:18:51 +00001114 * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
Tyler Gunnbbd78a72017-04-30 14:16:07 -07001115 * @param originalIntent
Nancy Chen0d3076c2014-07-30 14:45:44 -07001116 */
Pengquan Meng4832f202017-12-20 16:13:04 -08001117 @VisibleForTesting
1118 public Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
Tyler Gunnbbd78a72017-04-30 14:16:07 -07001119 UserHandle initiatingUser, Intent originalIntent) {
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001120 boolean isReusedCall = true;
1121 Call call = reuseOutgoingCall(handle);
1122
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001123 PhoneAccount account =
1124 mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001125 boolean isSelfManaged = account != null && account.isSelfManaged();
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001126
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001127 // Create a call with original handle. The handle may be changed when the call is attached
1128 // to a connection service, but in most cases will remain the same.
1129 if (call == null) {
1130 call = new Call(getNextCallId(), mContext,
1131 this,
1132 mLock,
1133 mConnectionServiceRepository,
1134 mContactsAsyncHelper,
1135 mCallerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07001136 mPhoneNumberUtilsAdapter,
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001137 handle,
1138 null /* gatewayInfo */,
1139 null /* connectionManagerPhoneAccount */,
1140 null /* phoneAccountHandle */,
Hall Liu32587202015-11-18 11:10:08 -08001141 Call.CALL_DIRECTION_OUTGOING /* callDirection */,
1142 false /* forceAttachToExistingConnection */,
Tyler Gunn8bb2b012017-08-04 09:28:59 -07001143 false, /* isConference */
1144 mClockProxy);
Hall Liu32587202015-11-18 11:10:08 -08001145 call.initAnalytics();
1146
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001147 // Ensure new calls related to self-managed calls/connections are set as such. This
1148 // will be overridden when the actual connection is returned in startCreateConnection,
1149 // however doing this now ensures the logs and any other logic will treat this call as
1150 // self-managed from the moment it is created.
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001151 call.setIsSelfManaged(isSelfManaged);
1152 if (isSelfManaged) {
1153 // Self-managed calls will ALWAYS use voip audio mode.
1154 call.setIsVoipAudioMode(true);
Tyler Gunnacb3bc82017-01-09 09:43:56 -08001155 }
Hall Liu874c0f82016-04-29 18:13:18 -07001156 call.setInitiatingUser(initiatingUser);
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001157 isReusedCall = false;
1158 }
Nancy Chen1c5926f2014-09-17 14:44:14 -07001159
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001160 int videoState = VideoProfile.STATE_AUDIO_ONLY;
Tyler Gunn228f95e2016-03-03 16:24:45 -08001161 if (extras != null) {
Hall Liudd68bc32017-01-25 17:14:23 -08001162 // Set the video state on the call early so that when it is added to the InCall UI the
1163 // UI knows to configure itself as a video call immediately.
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001164 videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
Tyler Gunnf8156382016-03-30 08:35:29 -07001165 VideoProfile.STATE_AUDIO_ONLY);
1166
1167 // If this is an emergency video call, we need to check if the phone account supports
1168 // emergency video calling.
Tyler Gunn10274f72016-07-26 15:38:16 -07001169 // Also, ensure we don't try to place an outgoing call with video if video is not
1170 // supported.
1171 if (VideoProfile.isVideo(videoState)) {
Tyler Gunn10274f72016-07-26 15:38:16 -07001172 if (call.isEmergencyCall() && account != null &&
Tyler Gunnf8156382016-03-30 08:35:29 -07001173 !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
1174 // Phone account doesn't support emergency video calling, so fallback to
1175 // audio-only now to prevent the InCall UI from setting up video surfaces
1176 // needlessly.
1177 Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
1178 "falling back to audio-only");
1179 videoState = VideoProfile.STATE_AUDIO_ONLY;
Tyler Gunn10274f72016-07-26 15:38:16 -07001180 } else if (account != null &&
1181 !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
1182 // Phone account doesn't support video calling, so fallback to audio-only.
1183 Log.i(this, "startOutgoingCall - video calls not supported; fallback to " +
1184 "audio-only.");
1185 videoState = VideoProfile.STATE_AUDIO_ONLY;
Tyler Gunnf8156382016-03-30 08:35:29 -07001186 }
1187 }
1188
1189 call.setVideoState(videoState);
Omkar Kolangade005612a2015-08-25 11:08:18 -07001190 }
1191
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001192 List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
1193 phoneAccountHandle, handle, VideoProfile.isVideo(videoState), initiatingUser);
1194 if (potentialPhoneAccounts.size() == 1) {
1195 phoneAccountHandle = potentialPhoneAccounts.get(0);
Tyler Gunna90ba732017-01-26 07:24:08 -08001196 } else {
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001197 phoneAccountHandle = null;
Nancy Chenbc9ef172014-08-15 17:11:57 -07001198 }
Nancy Chen1c5926f2014-09-17 14:44:14 -07001199 call.setTargetPhoneAccount(phoneAccountHandle);
Nancy Chenbc9ef172014-08-15 17:11:57 -07001200
Tyler Gunna90ba732017-01-26 07:24:08 -08001201 boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;
Santos Cordonf193ba42014-09-12 06:37:39 -07001202
1203 // Do not support any more live calls. Our options are to move a call to hold, disconnect
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001204 // a call, or cancel this call altogether. If a call is being reused, then it has already
1205 // passed the makeRoomForOutgoingCall check once and will fail the second time due to the
1206 // call transitioning into the CONNECTING state.
Pengquan Meng4832f202017-12-20 16:13:04 -08001207 if (!isPotentialInCallMMICode && (!isReusedCall
1208 && !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
1209 Call foregroundCall = getForegroundCall();
1210 Log.d(this, "No more room for outgoing call %s ", call);
1211 if (foregroundCall.isSelfManaged()) {
1212 // If the ongoing call is a self-managed call, then prompt the user to ask if they'd
1213 // like to disconnect their ongoing call and place the outgoing call.
1214 call.setOriginalCallIntent(originalIntent);
1215 startCallConfirmation(call);
1216 } else {
1217 // If the ongoing call is a managed call, we will prevent the outgoing call from
1218 // dialing.
1219 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
Santos Cordoncf5b2912014-11-05 21:57:54 -08001220 }
Santos Cordonf193ba42014-09-12 06:37:39 -07001221 return null;
1222 }
1223
Pengquan Meng4832f202017-12-20 16:13:04 -08001224 // The outgoing call can be placed, go forward.
1225
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001226 boolean needsAccountSelection =
1227 phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
1228 && !call.isEmergencyCall() && !isSelfManaged;
Nancy Chen8d92f452014-10-20 21:43:44 -07001229 if (needsAccountSelection) {
Nancy Chenbc9ef172014-08-15 17:11:57 -07001230 // This is the state where the user is expected to select an account
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07001231 call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
Pawit Pornkitprasan1b2fd282015-03-15 08:33:32 +07001232 // Create our own instance to modify (since extras may be Bundle.EMPTY)
1233 extras = new Bundle(extras);
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001234 extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS,
1235 potentialPhoneAccounts);
Nancy Chenbc9ef172014-08-15 17:11:57 -07001236 } else {
Srikanth Chintala5b8facb2015-02-17 11:39:21 +05301237 PhoneAccount accountToUse =
1238 mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
1239 if (accountToUse != null && accountToUse.getExtras() != null) {
1240 if (accountToUse.getExtras()
1241 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
1242 Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s",
1243 call.getId());
1244 call.setIsVoipAudioMode(true);
1245 }
1246 }
1247
Roshan Pius781d0252015-06-25 14:13:07 -07001248 call.setState(
1249 CallState.CONNECTING,
1250 phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
Pengquan Meng4832f202017-12-20 16:13:04 -08001251
Hall Liufb8d5ab2018-01-29 17:34:54 -08001252 if (isRttSettingOn() || (extras != null
1253 && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false))) {
1254 Log.d(this, "Outgoing call requesting RTT, rtt setting is %b", isRttSettingOn());
Hall Liudd68bc32017-01-25 17:14:23 -08001255 if (accountToUse != null
1256 && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
Hall Liu996ecdb2018-02-09 16:48:57 -08001257 call.createRttStreams();
Hall Liudd68bc32017-01-25 17:14:23 -08001258 }
Hall Liufb8d5ab2018-01-29 17:34:54 -08001259 // Even if the phone account doesn't support RTT yet, the connection manager might
1260 // change that. Set this to check it later.
1261 call.setRequestedToStartWithRtt();
Hall Liudd68bc32017-01-25 17:14:23 -08001262 }
Nancy Chenbc9ef172014-08-15 17:11:57 -07001263 }
Sailesh Nepal703a1af2016-04-14 20:10:12 -07001264 setIntentExtrasAndStartTime(call, extras);
Nancy Chen1c5926f2014-09-17 14:44:14 -07001265
Pengquan Meng4832f202017-12-20 16:13:04 -08001266 if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
Tyler Gunnbbd78a72017-04-30 14:16:07 -07001267 // Do not add the call if it is a potential MMI code.
Yorke Leeb701f6d2014-08-12 14:04:19 -07001268 call.addListener(this);
Santos Cordoncf5b2912014-11-05 21:57:54 -08001269 } else if (!mCalls.contains(call)) {
1270 // We check if mCalls already contains the call because we could potentially be reusing
Brad Ebingerb1e3d752015-11-09 17:54:17 -08001271 // a call which was previously added (See {@link #reuseOutgoingCall}).
Tyler Gunn5b452df2014-10-01 15:02:58 -07001272 addCall(call);
Yorke Leeb701f6d2014-08-12 14:04:19 -07001273 }
Nancy Chen0d3076c2014-07-30 14:45:44 -07001274
1275 return call;
1276 }
1277
Ben Gilada0d9f752014-02-26 11:49:03 -08001278 /**
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001279 * Finds the {@link PhoneAccountHandle}(s) which could potentially be used to place an outgoing
1280 * call. Takes into account the following:
1281 * 1. Any pre-chosen {@link PhoneAccountHandle} which was specified on the
1282 * {@link Intent#ACTION_CALL} intent. If one was chosen it will be used if possible.
1283 * 2. Whether the call is a video call. If the call being placed is a video call, an attempt is
1284 * first made to consider video capable phone accounts. If no video capable phone accounts are
1285 * found, the usual non-video capable phone accounts will be considered.
1286 * 3. Whether there is a user-chosen default phone account; that one will be used if possible.
1287 *
1288 * @param targetPhoneAccountHandle The pre-chosen {@link PhoneAccountHandle} passed in when the
1289 * call was placed. Will be {@code null} if the
1290 * {@link Intent#ACTION_CALL} intent did not specify a target
1291 * phone account.
1292 * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching
1293 * phone accounts.
1294 * @param isVideo {@code true} if the call is a video call, {@code false} otherwise.
1295 * @param initiatingUser The {@link UserHandle} the call is placed on.
1296 * @return
1297 */
1298 @VisibleForTesting
1299 public List<PhoneAccountHandle> findOutgoingCallPhoneAccount(
1300 PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo,
1301 UserHandle initiatingUser) {
1302 boolean isSelfManaged = isSelfManaged(targetPhoneAccountHandle, initiatingUser);
1303
1304 List<PhoneAccountHandle> accounts;
1305 if (!isSelfManaged) {
1306 // Try to find a potential phone account, taking into account whether this is a video
1307 // call.
1308 accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo);
1309 if (isVideo && accounts.size() == 0) {
1310 // Placing a video call but no video capable accounts were found, so consider any
1311 // call capable accounts (we can fallback to audio).
1312 accounts = constructPossiblePhoneAccounts(handle, initiatingUser,
1313 false /* isVideo */);
1314 }
1315 Log.v(this, "findOutgoingCallPhoneAccount: accounts = " + accounts);
1316
1317 // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this
1318 // call as if a phoneAccount was not specified (does the default behavior instead).
1319 // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
1320 if (targetPhoneAccountHandle != null) {
1321 if (!accounts.contains(targetPhoneAccountHandle)) {
1322 targetPhoneAccountHandle = null;
Tyler Gunnb8ce2aa2017-11-15 13:10:13 -08001323 } else {
1324 // The target phone account is valid and was found.
1325 return Arrays.asList(targetPhoneAccountHandle);
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001326 }
1327 }
1328
1329 if (targetPhoneAccountHandle == null && accounts.size() > 0) {
1330 // No preset account, check if default exists that supports the URI scheme for the
1331 // handle and verify it can be used.
1332 if (accounts.size() > 1) {
1333 PhoneAccountHandle defaultPhoneAccountHandle =
1334 mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
1335 handle.getScheme(), initiatingUser);
1336 if (defaultPhoneAccountHandle != null &&
1337 accounts.contains(defaultPhoneAccountHandle)) {
1338 accounts.clear();
1339 accounts.add(defaultPhoneAccountHandle);
1340 }
1341 }
1342 }
1343 } else {
1344 // Self-managed ConnectionServices can only have a single potential account.
1345 accounts = Arrays.asList(targetPhoneAccountHandle);
1346 }
1347 return accounts;
1348 }
1349
Tyler Gunncf210322018-01-23 09:18:24 -08001350 /**
1351 * Determines if a {@link PhoneAccountHandle} is for a self-managed ConnectionService.
1352 * @param targetPhoneAccountHandle The phone account to check.
1353 * @param initiatingUser The user associated with the account.
1354 * @return {@code true} if the phone account is self-managed, {@code false} otherwise.
1355 */
1356 public boolean isSelfManaged(PhoneAccountHandle targetPhoneAccountHandle,
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001357 UserHandle initiatingUser) {
1358 PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
1359 targetPhoneAccountHandle, initiatingUser);
1360 return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();
1361 }
1362
1363 /**
Yorke Lee33501632014-03-17 19:24:12 -07001364 * Attempts to issue/connect the specified call.
Santos Cordon8e8b8d22013-12-19 14:14:05 -08001365 *
Yorke Lee33501632014-03-17 19:24:12 -07001366 * @param handle Handle to connect the call with.
Yorke Lee33501632014-03-17 19:24:12 -07001367 * @param gatewayInfo Optional gateway information that can be used to route the call to the
Nancy Chen53ceedc2014-07-08 18:56:51 -07001368 * actual dialed handle via a gateway provider. May be null.
Sai Cheemalapatib7157e92014-06-11 17:51:55 -07001369 * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
Tyler Gunnc4abd912014-07-08 14:22:10 -07001370 * @param videoState The desired video state for the outgoing call.
Santos Cordon8e8b8d22013-12-19 14:14:05 -08001371 */
Hall Liu220b4192015-12-11 11:33:08 -08001372 @VisibleForTesting
1373 public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
1374 boolean speakerphoneOn, int videoState) {
Nancy Chen0d3076c2014-07-30 14:45:44 -07001375 if (call == null) {
1376 // don't do anything if the call no longer exists
1377 Log.i(this, "Canceling unknown call.");
Santos Cordonb7af09e2014-08-06 04:30:01 -07001378 return;
1379 }
1380
Nancy Chen201b4372014-09-08 14:18:24 -07001381 final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
Yorke Lee33501632014-03-17 19:24:12 -07001382
1383 if (gatewayInfo == null) {
Santos Cordonb58f4532014-05-29 11:59:06 -07001384 Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
Yorke Lee33501632014-03-17 19:24:12 -07001385 } else {
1386 Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
1387 Log.pii(uriHandle), Log.pii(handle));
1388 }
Santos Cordon766d04f2014-05-06 10:28:25 -07001389
Nancy Chen0d3076c2014-07-30 14:45:44 -07001390 call.setHandle(uriHandle);
1391 call.setGatewayInfo(gatewayInfo);
Tyler Gunnb492f4c2015-12-15 08:15:43 -08001392
1393 final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
1394 R.bool.use_speaker_when_docked);
John Eckerdal63486002015-11-20 13:17:29 +01001395 final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
Hall Liu9696c212016-06-24 16:09:02 -07001396 final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);
Tyler Gunnb492f4c2015-12-15 08:15:43 -08001397
1398 // Auto-enable speakerphone if the originating intent specified to do so, if the call
1399 // is a video call, of if using speaker when docked
1400 call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
John Eckerdal63486002015-11-20 13:17:29 +01001401 || (useSpeakerWhenDocked && useSpeakerForDock));
Tyler Gunnc4abd912014-07-08 14:22:10 -07001402 call.setVideoState(videoState);
Santos Cordon766d04f2014-05-06 10:28:25 -07001403
Santos Cordona04bdca2015-03-04 16:57:55 -08001404 if (speakerphoneOn) {
1405 Log.i(this, "%s Starting with speakerphone as requested", call);
John Eckerdal63486002015-11-20 13:17:29 +01001406 } else if (useSpeakerWhenDocked && useSpeakerForDock) {
Santos Cordona04bdca2015-03-04 16:57:55 -08001407 Log.i(this, "%s Starting with speakerphone because car is docked.", call);
Tyler Gunnb492f4c2015-12-15 08:15:43 -08001408 } else if (useSpeakerForVideoCall) {
1409 Log.i(this, "%s Starting with speakerphone because its a video call.", call);
Santos Cordona04bdca2015-03-04 16:57:55 -08001410 }
Bryce Lee88f900a2015-11-06 08:51:22 -08001411
Tyler Gunn6ffe5312015-08-12 08:19:20 -07001412 if (call.isEmergencyCall()) {
Abhijith Shastrycf27f682016-03-01 18:28:44 -08001413 new AsyncEmergencyContactNotifier(mContext).execute();
Nancy Chen53ceedc2014-07-08 18:56:51 -07001414 }
Nancy Chend9de92c2014-07-21 16:09:13 -07001415
Bryce Lee6afefa42015-10-25 22:25:53 -07001416 final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
1417 com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
Tyler Gunn2b17f232017-03-08 08:51:00 -08001418 final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
1419 call.getTargetPhoneAccount());
Tyler Gunn6ffe5312015-08-12 08:19:20 -07001420 if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
Nancy Chenbc9ef172014-08-15 17:11:57 -07001421 // If the account has been set, proceed to place the outgoing call.
1422 // Otherwise the connection will be initiated when the account is set by the user.
Tyler Gunn2b17f232017-03-08 08:51:00 -08001423 if (call.isSelfManaged() && !isOutgoingCallPermitted) {
Tyler Gunna90ba732017-01-26 07:24:08 -08001424 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
1425 } else {
Tyler Gunn2b17f232017-03-08 08:51:00 -08001426 if (call.isEmergencyCall()) {
Tyler Gunnfe791132018-03-30 07:37:51 -07001427 // Drop any ongoing self-managed calls to make way for an emergency call.
1428 disconnectSelfManagedCalls("place emerg call" /* reason */);
Tyler Gunn2b17f232017-03-08 08:51:00 -08001429 }
1430
Tyler Gunna90ba732017-01-26 07:24:08 -08001431 call.startCreateConnection(mPhoneAccountRegistrar);
1432 }
Bryce Lee6afefa42015-10-25 22:25:53 -07001433 } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
Tony Mak240656f2015-12-04 11:36:22 +00001434 requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false,
1435 call.getInitiatingUser()).isEmpty()) {
Bryce Leede0ac372015-10-23 16:06:00 -07001436 // If there are no call capable accounts, disconnect the call.
1437 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
1438 "No registered PhoneAccounts"));
1439 markCallAsRemoved(call);
Nancy Chend9de92c2014-07-21 16:09:13 -07001440 }
Yorke Leef98fb572014-03-05 10:56:55 -08001441 }
1442
1443 /**
Santos Cordona1610702014-06-04 20:22:56 -07001444 * Attempts to start a conference call for the specified call.
1445 *
Santos Cordon12d61822014-07-29 16:02:20 -07001446 * @param call The call to conference.
1447 * @param otherCall The other call to conference with.
Santos Cordona1610702014-06-04 20:22:56 -07001448 */
Brad Ebinger53855132015-10-30 10:58:19 -07001449 @VisibleForTesting
1450 public void conference(Call call, Call otherCall) {
Santos Cordon0fbe6322014-08-14 04:04:25 -07001451 call.conferenceWith(otherCall);
Santos Cordona1610702014-06-04 20:22:56 -07001452 }
1453
1454 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001455 * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
1456 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
Santos Cordone3d76ab2014-01-28 17:25:20 -08001457 * the user opting to answer said call.
Andrew Lee38931d02014-07-16 10:17:36 -07001458 *
1459 * @param call The call to answer.
1460 * @param videoState The video state in which to answer the call.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001461 */
Brad Ebinger53855132015-10-30 10:58:19 -07001462 @VisibleForTesting
1463 public void answerCall(Call call, int videoState) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001464 if (!mCalls.contains(call)) {
1465 Log.i(this, "Request to answer a non-existent call %s", call);
Santos Cordon61d0f702014-02-19 02:52:23 -08001466 } else {
Pengquan Meng4832f202017-12-20 16:13:04 -08001467 // Hold or disconnect the active call and request call focus for the incoming call.
1468 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
1469 Log.d(this, "Incoming call = %s Ongoing call %s", call, activeCall);
Pengquan Meng2fc55272018-03-19 17:36:32 -07001470 holdActiveCallForNewCall(call);
Pengquan Meng4832f202017-12-20 16:13:04 -08001471 mConnectionSvrFocusMgr.requestFocus(
1472 call,
1473 new RequestCallback(new ActionAnswerCall(call, videoState)));
Santos Cordon61d0f702014-02-19 02:52:23 -08001474 }
Santos Cordone3d76ab2014-01-28 17:25:20 -08001475 }
1476
Tyler Gunnb360f0d2015-12-01 14:34:03 -08001477 /**
Pooja Jainad4ebc02017-12-28 14:23:13 +05301478 * Instructs Telecom to deflect the specified call. Intended to be invoked by the in-call
1479 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
1480 * the user opting to deflect said call.
1481 */
1482 @VisibleForTesting
1483 public void deflectCall(Call call, Uri address) {
1484 if (!mCalls.contains(call)) {
1485 Log.i(this, "Request to deflect a non-existent call %s", call);
1486 } else {
1487 call.deflect(address);
1488 }
1489 }
1490
1491 /**
Tyler Gunnb360f0d2015-12-01 14:34:03 -08001492 * Determines if the speakerphone should be automatically enabled for the call. Speakerphone
1493 * should be enabled if the call is a video call and bluetooth or the wired headset are not in
1494 * use.
1495 *
1496 * @param videoState The video state of the call.
1497 * @return {@code true} if the speakerphone should be enabled.
1498 */
Hall Liu9696c212016-06-24 16:09:02 -07001499 public boolean isSpeakerphoneAutoEnabledForVideoCalls(int videoState) {
Tyler Gunnb360f0d2015-12-01 14:34:03 -08001500 return VideoProfile.isVideo(videoState) &&
1501 !mWiredHeadsetManager.isPluggedIn() &&
Hall Liu486cb192016-10-21 18:23:33 -07001502 !mBluetoothRouteManager.isBluetoothAvailable() &&
Tyler Gunnb360f0d2015-12-01 14:34:03 -08001503 isSpeakerEnabledForVideoCalls();
1504 }
1505
1506 /**
John Eckerdal63486002015-11-20 13:17:29 +01001507 * Determines if the speakerphone should be enabled for when docked. Speakerphone
1508 * should be enabled if the device is docked and bluetooth or the wired headset are
1509 * not in use.
1510 *
1511 * @return {@code true} if the speakerphone should be enabled for the dock.
1512 */
1513 private boolean isSpeakerphoneEnabledForDock() {
1514 return mDockManager.isDocked() &&
1515 !mWiredHeadsetManager.isPluggedIn() &&
Hall Liu486cb192016-10-21 18:23:33 -07001516 !mBluetoothRouteManager.isBluetoothAvailable();
John Eckerdal63486002015-11-20 13:17:29 +01001517 }
1518
1519 /**
Tyler Gunnb360f0d2015-12-01 14:34:03 -08001520 * Determines if the speakerphone should be automatically enabled for video calls.
1521 *
1522 * @return {@code true} if the speakerphone should automatically be enabled.
1523 */
Rekha Kumard240fe82015-04-01 21:45:57 -07001524 private static boolean isSpeakerEnabledForVideoCalls() {
1525 return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
1526 PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
1527 PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
1528 }
1529
Santos Cordone3d76ab2014-01-28 17:25:20 -08001530 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001531 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
1532 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
Santos Cordone3d76ab2014-01-28 17:25:20 -08001533 * the user opting to reject said call.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001534 */
Brad Ebinger53855132015-10-30 10:58:19 -07001535 @VisibleForTesting
1536 public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001537 if (!mCalls.contains(call)) {
1538 Log.i(this, "Request to reject a non-existent call %s", call);
Santos Cordon61d0f702014-02-19 02:52:23 -08001539 } else {
Santos Cordona56f2762014-03-24 15:55:53 -07001540 for (CallsManagerListener listener : mListeners) {
Ihab Awadff7493a2014-06-10 13:47:44 -07001541 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
Santos Cordona56f2762014-03-24 15:55:53 -07001542 }
Ihab Awadff7493a2014-06-10 13:47:44 -07001543 call.reject(rejectWithMessage, textMessage);
Santos Cordon61d0f702014-02-19 02:52:23 -08001544 }
Santos Cordone3d76ab2014-01-28 17:25:20 -08001545 }
1546
1547 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001548 * Instructs Telecom to play the specified DTMF tone within the specified call.
Ihab Awad74549ec2014-03-10 15:33:25 -07001549 *
Ihab Awad74549ec2014-03-10 15:33:25 -07001550 * @param digit The DTMF digit to play.
1551 */
Brad Ebinger53855132015-10-30 10:58:19 -07001552 @VisibleForTesting
1553 public void playDtmfTone(Call call, char digit) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001554 if (!mCalls.contains(call)) {
1555 Log.i(this, "Request to play DTMF in a non-existent call %s", call);
Ihab Awad74549ec2014-03-10 15:33:25 -07001556 } else {
Tyler Gunnad972422017-11-13 13:36:35 -08001557 if (call.getState() != CallState.ON_HOLD) {
1558 call.playDtmfTone(digit);
1559 mDtmfLocalTonePlayer.playTone(call, digit);
1560 } else {
1561 Log.i(this, "Request to play DTMF tone for held call %s", call.getId());
1562 }
Ihab Awad74549ec2014-03-10 15:33:25 -07001563 }
1564 }
1565
1566 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001567 * Instructs Telecom to stop the currently playing DTMF tone, if any.
Ihab Awad74549ec2014-03-10 15:33:25 -07001568 */
Brad Ebinger53855132015-10-30 10:58:19 -07001569 @VisibleForTesting
1570 public void stopDtmfTone(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001571 if (!mCalls.contains(call)) {
1572 Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
Ihab Awad74549ec2014-03-10 15:33:25 -07001573 } else {
1574 call.stopDtmfTone();
Santos Cordon92a2d812014-04-30 15:19:01 -07001575 mDtmfLocalTonePlayer.stopTone(call);
Ihab Awad74549ec2014-03-10 15:33:25 -07001576 }
1577 }
1578
1579 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001580 * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
Ihab Awad74549ec2014-03-10 15:33:25 -07001581 */
Evan Charlton352105c2014-06-03 14:10:54 -07001582 void postDialContinue(Call call, boolean proceed) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001583 if (!mCalls.contains(call)) {
1584 Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
Ihab Awad74549ec2014-03-10 15:33:25 -07001585 } else {
Evan Charlton352105c2014-06-03 14:10:54 -07001586 call.postDialContinue(proceed);
Ihab Awad74549ec2014-03-10 15:33:25 -07001587 }
1588 }
1589
1590 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001591 * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
Santos Cordone3d76ab2014-01-28 17:25:20 -08001592 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
1593 * the user hitting the end-call button.
Santos Cordone3d76ab2014-01-28 17:25:20 -08001594 */
Brad Ebinger53855132015-10-30 10:58:19 -07001595 @VisibleForTesting
1596 public void disconnectCall(Call call) {
Santos Cordon682fe6b2014-05-20 08:56:39 -07001597 Log.v(this, "disconnectCall %s", call);
1598
Sailesh Nepale59bb192014-04-01 18:33:59 -07001599 if (!mCalls.contains(call)) {
1600 Log.w(this, "Unknown call (%s) asked to disconnect", call);
Santos Cordon049b7b62014-01-30 05:34:26 -08001601 } else {
Ihab Awad5b281322014-09-25 18:25:29 -07001602 mLocallyDisconnectingCalls.add(call);
Santos Cordon049b7b62014-01-30 05:34:26 -08001603 call.disconnect();
1604 }
Santos Cordone3d76ab2014-01-28 17:25:20 -08001605 }
Santos Cordon681663d2014-01-30 04:32:15 -08001606
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001607 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001608 * Instructs Telecom to disconnect all calls.
Tyler Gunn61b92102014-08-19 07:42:20 -07001609 */
1610 void disconnectAllCalls() {
1611 Log.v(this, "disconnectAllCalls");
1612
1613 for (Call call : mCalls) {
1614 disconnectCall(call);
1615 }
1616 }
1617
Tyler Gunn2b17f232017-03-08 08:51:00 -08001618 /**
1619 * Disconnects calls for any other {@link PhoneAccountHandle} but the one specified.
1620 * Note: As a protective measure, will NEVER disconnect an emergency call. Although that
1621 * situation should never arise, its a good safeguard.
1622 * @param phoneAccountHandle Calls owned by {@link PhoneAccountHandle}s other than this one will
1623 * be disconnected.
1624 */
1625 private void disconnectOtherCalls(PhoneAccountHandle phoneAccountHandle) {
1626 mCalls.stream()
1627 .filter(c -> !c.isEmergencyCall() &&
1628 !c.getTargetPhoneAccount().equals(phoneAccountHandle))
1629 .forEach(c -> disconnectCall(c));
1630 }
Nancy Chenbc9ef172014-08-15 17:11:57 -07001631
Tyler Gunn61b92102014-08-19 07:42:20 -07001632 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001633 * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001634 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
1635 * the user hitting the hold button during an active call.
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001636 */
Brad Ebinger53855132015-10-30 10:58:19 -07001637 @VisibleForTesting
1638 public void holdCall(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001639 if (!mCalls.contains(call)) {
1640 Log.w(this, "Unknown call (%s) asked to be put on hold", call);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001641 } else {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001642 Log.d(this, "Putting call on hold: (%s)", call);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001643 call.hold();
1644 }
1645 }
1646
1647 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -07001648 * Instructs Telecom to release the specified call from hold. Intended to be invoked by
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001649 * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
1650 * by the user hitting the hold button during a held call.
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001651 */
Brad Ebinger53855132015-10-30 10:58:19 -07001652 @VisibleForTesting
1653 public void unholdCall(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -07001654 if (!mCalls.contains(call)) {
1655 Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001656 } else {
Pengquan Meng4832f202017-12-20 16:13:04 -08001657 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
Tyler Gunnf4f05392018-03-26 18:57:59 +00001658 String activeCallId = null;
Pengquan Meng4832f202017-12-20 16:13:04 -08001659 if (activeCall != null) {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001660 activeCallId = activeCall.getId();
Pengquan Meng4832f202017-12-20 16:13:04 -08001661 if (canHold(activeCall)) {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001662 activeCall.hold("Swap to " + call.getId());
1663 Log.addEvent(activeCall, LogUtils.Events.SWAP, "To " + call.getId());
1664 Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId());
Pengquan Meng4832f202017-12-20 16:13:04 -08001665 } else {
1666 // This call does not support hold. If it is from a different connection
1667 // service, then disconnect it, otherwise invoke call.hold() and allow the
1668 // connection service to handle the situation.
1669 if (activeCall.getConnectionService() != call.getConnectionService()) {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001670 activeCall.disconnect("Swap to " + call.getId());
Pengquan Meng4832f202017-12-20 16:13:04 -08001671 } else {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001672 activeCall.hold("Swap to " + call.getId());
Pengquan Meng4832f202017-12-20 16:13:04 -08001673 }
Sai Cheemalapati45b3e3f2014-08-15 09:33:00 -07001674 }
1675 }
Pengquan Meng4832f202017-12-20 16:13:04 -08001676 mConnectionSvrFocusMgr.requestFocus(
1677 call,
Tyler Gunnf4f05392018-03-26 18:57:59 +00001678 new RequestCallback(new ActionUnHoldCall(call, activeCallId)));
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001679 }
1680 }
1681
Hall Liu32587202015-11-18 11:10:08 -08001682 @Override
Ravi Paluri6f4a7152017-12-01 15:55:53 +05301683 public void onExtrasRemoved(Call c, int source, List<String> keys) {
1684 if (source != Call.SOURCE_CONNECTION_SERVICE) {
1685 return;
1686 }
1687 updateCanAddCall();
1688 }
1689
1690 @Override
Tyler Gunn961694a2016-03-21 16:01:40 -07001691 public void onExtrasChanged(Call c, int source, Bundle extras) {
1692 if (source != Call.SOURCE_CONNECTION_SERVICE) {
1693 return;
1694 }
1695 handleCallTechnologyChange(c);
Brad Ebingerbaf52ba2016-03-21 16:38:32 -07001696 handleChildAddressChange(c);
Hall Liufc130b22016-06-15 17:54:48 -07001697 updateCanAddCall();
Tyler Gunn961694a2016-03-21 16:01:40 -07001698 }
1699
Brad Ebinger3da57642016-04-04 17:32:19 -07001700 // Construct the list of possible PhoneAccounts that the outgoing call can use based on the
1701 // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,
1702 // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001703 @VisibleForTesting
1704 public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
1705 boolean isVideo) {
Brad Ebinger3da57642016-04-04 17:32:19 -07001706 if (handle == null) {
1707 return Collections.emptyList();
1708 }
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001709 // If we're specifically looking for video capable accounts, then include that capability,
1710 // otherwise specify no additional capability constraints.
Brad Ebinger3da57642016-04-04 17:32:19 -07001711 List<PhoneAccountHandle> allAccounts =
Tyler Gunnc74b3e22017-11-07 15:03:24 -08001712 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user,
1713 isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0 /* any */);
Brad Ebinger3da57642016-04-04 17:32:19 -07001714 // First check the Radio SIM Technology
1715 if(mRadioSimVariants == null) {
1716 TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
1717 Context.TELEPHONY_SERVICE);
1718 // Cache Sim Variants
1719 mRadioSimVariants = tm.getMultiSimConfiguration();
1720 }
1721 // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount
1722 // Should be available if a call is already active on the SIM account.
1723 if(mRadioSimVariants != TelephonyManager.MultiSimVariants.DSDA) {
1724 List<PhoneAccountHandle> simAccounts =
1725 mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser();
1726 PhoneAccountHandle ongoingCallAccount = null;
1727 for (Call c : mCalls) {
1728 if (!c.isDisconnected() && !c.isNew() && simAccounts.contains(
1729 c.getTargetPhoneAccount())) {
1730 ongoingCallAccount = c.getTargetPhoneAccount();
1731 break;
1732 }
1733 }
1734 if (ongoingCallAccount != null) {
1735 // Remove all SIM accounts that are not the active SIM from the list.
1736 simAccounts.remove(ongoingCallAccount);
1737 allAccounts.removeAll(simAccounts);
1738 }
1739 }
1740 return allAccounts;
1741 }
1742
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07001743 /**
1744 * Informs listeners (notably {@link CallAudioManager} of a change to the call's external
1745 * property.
1746 * .
1747 * @param call The call whose external property changed.
1748 * @param isExternalCall {@code True} if the call is now external, {@code false} otherwise.
1749 */
1750 @Override
1751 public void onExternalCallChanged(Call call, boolean isExternalCall) {
1752 Log.v(this, "onConnectionPropertiesChanged: %b", isExternalCall);
1753 for (CallsManagerListener listener : mListeners) {
1754 listener.onExternalCallChanged(call, isExternalCall);
1755 }
1756 }
1757
Tyler Gunn961694a2016-03-21 16:01:40 -07001758 private void handleCallTechnologyChange(Call call) {
Hall Liu32587202015-11-18 11:10:08 -08001759 if (call.getExtras() != null
1760 && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) {
1761
1762 Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get(
1763 call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE));
1764 if (analyticsCallTechnology == null) {
1765 analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE;
1766 }
1767 call.getAnalytics().addCallTechnology(analyticsCallTechnology);
1768 }
1769 }
1770
Brad Ebingerbaf52ba2016-03-21 16:38:32 -07001771 public void handleChildAddressChange(Call call) {
1772 if (call.getExtras() != null
1773 && call.getExtras().containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
1774
1775 String viaNumber = call.getExtras().getString(Connection.EXTRA_CHILD_ADDRESS);
1776 call.setViaNumber(viaNumber);
1777 }
1778 }
1779
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001780 /** Called by the in-call UI to change the mute state. */
1781 void mute(boolean shouldMute) {
Hall Liuccd793f2018-03-16 14:48:59 -07001782 if (hasEmergencyCall() && shouldMute) {
1783 Log.i(this, "Refusing to turn on mute because we're in an emergency call");
1784 shouldMute = false;
1785 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001786 mCallAudioManager.mute(shouldMute);
1787 }
1788
1789 /**
1790 * Called by the in-call UI to change the audio route, for example to change from earpiece to
1791 * speaker phone.
1792 */
Hall Liu9086fb12017-11-07 18:01:53 -08001793 void setAudioRoute(int route, String bluetoothAddress) {
Hall Liuccd793f2018-03-16 14:48:59 -07001794 if (hasEmergencyRttCall() && route != CallAudioState.ROUTE_SPEAKER) {
1795 Log.i(this, "In an emergency RTT call. Forcing route to speaker.");
1796 route = CallAudioState.ROUTE_SPEAKER;
1797 bluetoothAddress = null;
1798 }
Hall Liu9086fb12017-11-07 18:01:53 -08001799 mCallAudioManager.setAudioRoute(route, bluetoothAddress);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001800 }
1801
Yorke Leed1346872014-07-28 14:38:45 -07001802 /** Called by the in-call UI to turn the proximity sensor on. */
1803 void turnOnProximitySensor() {
1804 mProximitySensorManager.turnOn();
1805 }
1806
1807 /**
1808 * Called by the in-call UI to turn the proximity sensor off.
1809 * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
1810 * the screen will be kept off until the proximity sensor goes negative.
1811 */
1812 void turnOffProximitySensor(boolean screenOnImmediately) {
1813 mProximitySensorManager.turnOff(screenOnImmediately);
1814 }
1815
Hall Liufb8d5ab2018-01-29 17:34:54 -08001816 private boolean isRttSettingOn() {
Hall Liu4eba63b2018-04-02 15:40:15 -07001817 return Settings.Secure.getInt(mContext.getContentResolver(),
1818 Settings.Secure.RTT_CALLING_MODE, 0) != 0;
Hall Liufb8d5ab2018-01-29 17:34:54 -08001819 }
1820
Nancy Chenf5e5d3c2014-10-21 18:45:56 -07001821 void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
Nancy Chen53ceedc2014-07-08 18:56:51 -07001822 if (!mCalls.contains(call)) {
Santos Cordonf193ba42014-09-12 06:37:39 -07001823 Log.i(this, "Attempted to add account to unknown call %s", call);
Nancy Chen53ceedc2014-07-08 18:56:51 -07001824 } else {
Ihab Awadb78b2762014-07-25 15:16:23 -07001825 call.setTargetPhoneAccount(account);
Srikanth Chintala5b8facb2015-02-17 11:39:21 +05301826 PhoneAccount realPhoneAccount =
1827 mPhoneAccountRegistrar.getPhoneAccountUnchecked(account);
1828 if (realPhoneAccount != null && realPhoneAccount.getExtras() != null
1829 && realPhoneAccount.getExtras()
1830 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
1831 Log.d("phoneAccountSelected: default to voip mode for call %s", call.getId());
1832 call.setIsVoipAudioMode(true);
1833 }
Hall Liufb8d5ab2018-01-29 17:34:54 -08001834 if (isRttSettingOn() || call.getIntentExtras()
Hall Liudd68bc32017-01-25 17:14:23 -08001835 .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
Hall Liufb8d5ab2018-01-29 17:34:54 -08001836 Log.d(this, "Outgoing call after account selection requesting RTT," +
1837 " rtt setting is %b", isRttSettingOn());
Hall Liudd68bc32017-01-25 17:14:23 -08001838 if (realPhoneAccount != null
1839 && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
Hall Liu996ecdb2018-02-09 16:48:57 -08001840 call.createRttStreams();
Hall Liudd68bc32017-01-25 17:14:23 -08001841 }
Hall Liufb8d5ab2018-01-29 17:34:54 -08001842 // Even if the phone account doesn't support RTT yet, the connection manager might
1843 // change that. Set this to check it later.
1844 call.setRequestedToStartWithRtt();
Hall Liudd68bc32017-01-25 17:14:23 -08001845 }
1846
Hall Liu4b1759d2016-02-02 18:17:40 -08001847 if (!call.isNewOutgoingCallIntentBroadcastDone()) {
1848 return;
1849 }
1850
Santos Cordonf193ba42014-09-12 06:37:39 -07001851 // Note: emergency calls never go through account selection dialog so they never
1852 // arrive here.
1853 if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
Tyler Gunn91d43cf2014-09-17 12:19:39 -07001854 call.startCreateConnection(mPhoneAccountRegistrar);
Santos Cordonf193ba42014-09-12 06:37:39 -07001855 } else {
Tyler Gunnf4f05392018-03-26 18:57:59 +00001856 call.disconnect("no room");
Santos Cordonf193ba42014-09-12 06:37:39 -07001857 }
Nancy Chenf5e5d3c2014-10-21 18:45:56 -07001858
1859 if (setDefault) {
Tony Mak4a3e2fd2015-12-04 11:58:38 +00001860 mPhoneAccountRegistrar
1861 .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
Nancy Chenf5e5d3c2014-10-21 18:45:56 -07001862 }
Nancy Chen53ceedc2014-07-08 18:56:51 -07001863 }
1864 }
1865
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001866 /** Called when the audio state changes. */
Hall Liuf62630a2015-10-27 14:53:39 -07001867 @VisibleForTesting
1868 public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState
1869 newAudioState) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001870 Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
Santos Cordona56f2762014-03-24 15:55:53 -07001871 for (CallsManagerListener listener : mListeners) {
Yorke Lee2a66f7b2015-05-13 14:21:19 -07001872 listener.onCallAudioStateChanged(oldAudioState, newAudioState);
Santos Cordona56f2762014-03-24 15:55:53 -07001873 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -07001874 }
1875
Honggangb3ccbb32016-05-31 14:46:22 +08001876 /**
1877 * Called when disconnect tone is started or stopped, including any InCallTone
1878 * after disconnected call.
1879 *
1880 * @param isTonePlaying true if the disconnected tone is started, otherwise the disconnected
1881 * tone is stopped.
1882 */
Hall Liu561cede2018-01-03 15:38:38 -08001883 @VisibleForTesting
1884 public void onDisconnectedTonePlaying(boolean isTonePlaying) {
Honggangb3ccbb32016-05-31 14:46:22 +08001885 Log.v(this, "onDisconnectedTonePlaying, %s", isTonePlaying ? "started" : "stopped");
1886 for (CallsManagerListener listener : mListeners) {
1887 listener.onDisconnectedTonePlaying(isTonePlaying);
1888 }
1889 }
1890
Sailesh Nepale59bb192014-04-01 18:33:59 -07001891 void markCallAsRinging(Call call) {
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07001892 setCallState(call, CallState.RINGING, "ringing set explicitly");
Santos Cordon681663d2014-01-30 04:32:15 -08001893 }
1894
Sailesh Nepale59bb192014-04-01 18:33:59 -07001895 void markCallAsDialing(Call call) {
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07001896 setCallState(call, CallState.DIALING, "dialing set explicitly");
Santos Cordond6782cd2015-04-16 09:57:50 -07001897 maybeMoveToSpeakerPhone(call);
Omata Shou6bc865c2017-07-26 13:39:27 +09001898 maybeTurnOffMute(call);
Santos Cordon681663d2014-01-30 04:32:15 -08001899 }
1900
Tyler Gunn1e37be52016-07-11 08:54:23 -07001901 void markCallAsPulling(Call call) {
1902 setCallState(call, CallState.PULLING, "pulling set explicitly");
1903 maybeMoveToSpeakerPhone(call);
1904 }
1905
Pengquan Meng2fc55272018-03-19 17:36:32 -07001906 /**
1907 * Returns true if the active call is held.
1908 */
1909 boolean holdActiveCallForNewCall(Call call) {
1910 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
1911 if (activeCall != null && activeCall != call) {
1912 if (canHold(activeCall)) {
1913 activeCall.hold();
1914 return true;
1915 } else if (supportsHold(call)) {
1916 Call heldCall = getHeldCallByConnectionService(call.getConnectionService());
1917 if (heldCall != null) {
1918 heldCall.disconnect();
1919 Log.i(this, "Disconnecting held call %s before holding active call.", heldCall);
1920 }
1921 activeCall.hold();
1922 return true;
1923 } else {
1924 // This call does not support hold. If it is from a different connection
1925 // service, then disconnect it, otherwise allow the connection service to
1926 // figure out the right states.
1927 if (activeCall.getConnectionService() != call.getConnectionService()) {
1928 activeCall.disconnect();
1929 }
1930 }
1931 }
1932 return false;
1933 }
1934
1935 @VisibleForTesting
1936 public void markCallAsActive(Call call) {
Pengquan Meng4832f202017-12-20 16:13:04 -08001937 if (call.isSelfManaged()) {
1938 // backward compatibility, the self-managed connection service will set the call state
Pengquan Meng2fc55272018-03-19 17:36:32 -07001939 // to active directly. We should hold or disconnect the current active call based on the
1940 // holdability, and request the call focus for the self-managed call before the state
1941 // change.
1942 holdActiveCallForNewCall(call);
Pengquan Meng4832f202017-12-20 16:13:04 -08001943 mConnectionSvrFocusMgr.requestFocus(
1944 call,
1945 new RequestCallback(new ActionSetCallState(
1946 call,
1947 CallState.ACTIVE,
1948 "active set explicitly for self-managed")));
1949 } else {
1950 setCallState(call, CallState.ACTIVE, "active set explicitly");
1951 maybeMoveToSpeakerPhone(call);
1952 }
Santos Cordon681663d2014-01-30 04:32:15 -08001953 }
1954
Tyler Gunnad972422017-11-13 13:36:35 -08001955 @VisibleForTesting
1956 public void markCallAsOnHold(Call call) {
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07001957 setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
Yorke Leecdf3ebd2014-03-12 18:31:41 -07001958 }
1959
Santos Cordon049b7b62014-01-30 05:34:26 -08001960 /**
Santos Cordonf193ba42014-09-12 06:37:39 -07001961 * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
1962 * last live call, then also disconnect from the in-call controller.
Santos Cordon049b7b62014-01-30 05:34:26 -08001963 *
Ihab Awad78a5e6b2015-02-06 10:13:05 -08001964 * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
Santos Cordon049b7b62014-01-30 05:34:26 -08001965 */
Andrew Lee701dc002014-09-11 21:29:12 -07001966 void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
1967 call.setDisconnectCause(disconnectCause);
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07001968 setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly");
Sailesh Nepal6ab6fb72014-04-01 20:03:19 -07001969 }
1970
Ben Gilada0d9f752014-02-26 11:49:03 -08001971 /**
Ihab Awada02bef52014-08-13 18:18:42 -07001972 * Removes an existing disconnected call, and notifies the in-call app.
1973 */
1974 void markCallAsRemoved(Call call) {
Tyler Gunn141ef582017-05-26 13:38:13 -07001975 call.maybeCleanupHandover();
Ihab Awada02bef52014-08-13 18:18:42 -07001976 removeCall(call);
Tyler Gunn7d58ba52016-07-25 15:05:48 -07001977 Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
Ihab Awad5b281322014-09-25 18:25:29 -07001978 if (mLocallyDisconnectingCalls.contains(call)) {
Srikanth Chintala5bf75022017-02-03 10:10:35 +05301979 boolean isDisconnectingChildCall = call.isDisconnectingChildCall();
1980 Log.v(this, "markCallAsRemoved: isDisconnectingChildCall = "
1981 + isDisconnectingChildCall + "call -> %s", call);
Ihab Awad5b281322014-09-25 18:25:29 -07001982 mLocallyDisconnectingCalls.remove(call);
Srikanth Chintala5bf75022017-02-03 10:10:35 +05301983 // Auto-unhold the foreground call due to a locally disconnected call, except if the
1984 // call which was disconnected is a member of a conference (don't want to auto un-hold
1985 // the conference if we remove a member of the conference).
1986 if (!isDisconnectingChildCall && foregroundCall != null
1987 && foregroundCall.getState() == CallState.ON_HOLD) {
Hall Liue091ab92015-12-18 17:05:30 -08001988 foregroundCall.unhold();
Ihab Awad5b281322014-09-25 18:25:29 -07001989 }
Tyler Gunn7d58ba52016-07-25 15:05:48 -07001990 } else if (foregroundCall != null &&
1991 !foregroundCall.can(Connection.CAPABILITY_SUPPORT_HOLD) &&
1992 foregroundCall.getState() == CallState.ON_HOLD) {
1993
1994 // The new foreground call is on hold, however the carrier does not display the hold
1995 // button in the UI. Therefore, we need to auto unhold the held call since the user has
1996 // no means of unholding it themselves.
1997 Log.i(this, "Auto-unholding held foreground call (call doesn't support hold)");
1998 foregroundCall.unhold();
Ihab Awad5b281322014-09-25 18:25:29 -07001999 }
Ihab Awada02bef52014-08-13 18:18:42 -07002000 }
2001
2002 /**
Tyler Gunnbbd78a72017-04-30 14:16:07 -07002003 * Given a call, marks the call as disconnected and removes it. Set the error message to
2004 * indicate to the user that the call cannot me placed due to an ongoing call in another app.
2005 *
2006 * Used when there are ongoing self-managed calls and the user tries to make an outgoing managed
2007 * call. Called by {@link #startCallConfirmation(Call)} when the user is already confirming an
2008 * outgoing call. Realistically this should almost never be called since in practice the user
2009 * won't make multiple outgoing calls at the same time.
2010 *
2011 * @param call The call to mark as disconnected.
2012 */
2013 void markCallDisconnectedDueToSelfManagedCall(Call call) {
2014 Call activeCall = getActiveCall();
2015 CharSequence errorMessage;
2016 if (activeCall == null) {
2017 // Realistically this shouldn't happen, but best to handle gracefully
2018 errorMessage = mContext.getText(R.string.cant_call_due_to_ongoing_unknown_call);
2019 } else {
2020 errorMessage = mContext.getString(R.string.cant_call_due_to_ongoing_call,
2021 activeCall.getTargetPhoneAccountLabel());
2022 }
2023 // Call is managed and there are ongoing self-managed calls.
2024 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR,
2025 errorMessage, errorMessage, "Ongoing call in another app."));
2026 markCallAsRemoved(call);
2027 }
2028
2029 /**
Sailesh Nepalc92c4362014-07-04 18:33:21 -07002030 * Cleans up any calls currently associated with the specified connection service when the
Sailesh Nepal905dfba2014-07-14 08:20:41 -07002031 * service binder disconnects unexpectedly.
Santos Cordon4b2c1192014-03-19 18:15:38 -07002032 *
Sailesh Nepalc92c4362014-07-04 18:33:21 -07002033 * @param service The connection service that disconnected.
Santos Cordon4b2c1192014-03-19 18:15:38 -07002034 */
Sailesh Nepalc92c4362014-07-04 18:33:21 -07002035 void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
Jay Shrauneracb91eb2014-08-08 16:04:53 -07002036 if (service != null) {
Tyler Gunn1a97fb22017-06-21 19:55:54 -07002037 Log.i(this, "handleConnectionServiceDeath: service %s died", service);
Jay Shraunera82c8f72014-08-14 15:49:16 -07002038 for (Call call : mCalls) {
Jay Shrauneracb91eb2014-08-08 16:04:53 -07002039 if (call.getConnectionService() == service) {
Yorke Lee2af16b62014-11-11 17:36:57 -08002040 if (call.getState() != CallState.DISCONNECTED) {
Tyler Gunn1a97fb22017-06-21 19:55:54 -07002041 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR,
2042 "CS_DEATH"));
Yorke Lee2af16b62014-11-11 17:36:57 -08002043 }
2044 markCallAsRemoved(call);
Jay Shrauneracb91eb2014-08-08 16:04:53 -07002045 }
Santos Cordon4b2c1192014-03-19 18:15:38 -07002046 }
2047 }
2048 }
2049
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07002050 /**
2051 * Determines if the {@link CallsManager} has any non-external calls.
2052 *
2053 * @return {@code True} if there are any non-external calls, {@code false} otherwise.
2054 */
Santos Cordondeb8c892014-05-30 01:38:03 -07002055 boolean hasAnyCalls() {
Tyler Gunn1a40c4f2016-04-14 14:29:45 -07002056 if (mCalls.isEmpty()) {
2057 return false;
2058 }
2059
2060 for (Call call : mCalls) {
2061 if (!call.isExternalCall()) {
2062 return true;
2063 }
2064 }
2065 return false;
Santos Cordondeb8c892014-05-30 01:38:03 -07002066 }
2067
Santos Cordonc7e85d42014-05-22 02:51:48 -07002068 boolean hasActiveOrHoldingCall() {
Santos Cordon23baed32014-06-27 14:45:39 -07002069 return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
Santos Cordonc7e85d42014-05-22 02:51:48 -07002070 }
2071
2072 boolean hasRingingCall() {
Santos Cordon23baed32014-06-27 14:45:39 -07002073 return getFirstCallWithState(CallState.RINGING) != null;
Santos Cordonc7e85d42014-05-22 02:51:48 -07002074 }
2075
Santos Cordondeb8c892014-05-30 01:38:03 -07002076 boolean onMediaButton(int type) {
2077 if (hasAnyCalls()) {
yifan.baic73ca8d2016-04-21 16:19:12 +08002078 Call ringingCall = getFirstCallWithState(CallState.RINGING);
Santos Cordondeb8c892014-05-30 01:38:03 -07002079 if (HeadsetMediaButton.SHORT_PRESS == type) {
Santos Cordondeb8c892014-05-30 01:38:03 -07002080 if (ringingCall == null) {
yifan.baic73ca8d2016-04-21 16:19:12 +08002081 Call callToHangup = getFirstCallWithState(CallState.RINGING, CallState.DIALING,
2082 CallState.PULLING, CallState.ACTIVE, CallState.ON_HOLD);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002083 Log.addEvent(callToHangup, LogUtils.Events.INFO,
2084 "media btn short press - end call.");
yifan.baic73ca8d2016-04-21 16:19:12 +08002085 if (callToHangup != null) {
Srikanth Chintala29644dc2017-07-19 10:27:11 +05302086 disconnectCall(callToHangup);
yifan.baic73ca8d2016-04-21 16:19:12 +08002087 return true;
2088 }
Santos Cordondeb8c892014-05-30 01:38:03 -07002089 } else {
Hall Liu3777a7e2016-07-25 14:46:48 -07002090 ringingCall.answer(VideoProfile.STATE_AUDIO_ONLY);
Santos Cordondeb8c892014-05-30 01:38:03 -07002091 return true;
2092 }
2093 } else if (HeadsetMediaButton.LONG_PRESS == type) {
yifan.baic73ca8d2016-04-21 16:19:12 +08002094 if (ringingCall != null) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002095 Log.addEvent(getForegroundCall(),
2096 LogUtils.Events.INFO, "media btn long press - reject");
yifan.baic73ca8d2016-04-21 16:19:12 +08002097 ringingCall.reject(false, null);
2098 } else {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002099 Log.addEvent(getForegroundCall(), LogUtils.Events.INFO,
2100 "media btn long press - mute");
yifan.baic73ca8d2016-04-21 16:19:12 +08002101 mCallAudioManager.toggleMute();
Santos Cordondeb8c892014-05-30 01:38:03 -07002102 }
yifan.baic73ca8d2016-04-21 16:19:12 +08002103 return true;
Santos Cordondeb8c892014-05-30 01:38:03 -07002104 }
2105 }
2106 return false;
2107 }
2108
2109 /**
Santos Cordon91bd74c2014-11-07 16:10:22 -08002110 * Returns true if telecom supports adding another top-level call.
Santos Cordon10838c22014-06-11 17:36:04 -07002111 */
Hall Liu28b82f02016-07-26 17:38:56 -07002112 @VisibleForTesting
2113 public boolean canAddCall() {
Hall Liuf1422e72016-01-27 11:07:07 -08002114 boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
2115 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
2116 if (!isDeviceProvisioned) {
2117 Log.d(TAG, "Device not provisioned, canAddCall is false.");
2118 return false;
2119 }
2120
Andrew Lee113ad622014-12-01 18:33:10 -08002121 if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
2122 return false;
2123 }
2124
Santos Cordon91bd74c2014-11-07 16:10:22 -08002125 int count = 0;
2126 for (Call call : mCalls) {
2127 if (call.isEmergencyCall()) {
2128 // We never support add call if one of the calls is an emergency call.
2129 return false;
Tyler Gunn1e37be52016-07-11 08:54:23 -07002130 } else if (call.isExternalCall()) {
2131 // External calls don't count.
2132 continue;
Santos Cordon91bd74c2014-11-07 16:10:22 -08002133 } else if (call.getParentCall() == null) {
2134 count++;
2135 }
Hall Liufc130b22016-06-15 17:54:48 -07002136 Bundle extras = call.getExtras();
2137 if (extras != null) {
Hall Liu140c8402016-07-06 16:30:46 -07002138 if (extras.getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) {
2139 return false;
2140 }
Hall Liufc130b22016-06-15 17:54:48 -07002141 }
Santos Cordon10838c22014-06-11 17:36:04 -07002142
Santos Cordon91bd74c2014-11-07 16:10:22 -08002143 // We do not check states for canAddCall. We treat disconnected calls the same
2144 // and wait until they are removed instead. If we didn't count disconnected calls,
2145 // we could put InCallServices into a state where they are showing two calls but
2146 // also support add-call. Technically it's right, but overall looks better (UI-wise)
2147 // and acts better if we wait until the call is removed.
2148 if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
Santos Cordon10838c22014-06-11 17:36:04 -07002149 return false;
2150 }
2151 }
Hall Liufc130b22016-06-15 17:54:48 -07002152
Santos Cordon10838c22014-06-11 17:36:04 -07002153 return true;
2154 }
2155
Ihab Awada3653902015-01-23 13:44:46 -08002156 @VisibleForTesting
P.Y. Laligandd35be752015-03-06 14:12:43 -08002157 public Call getRingingCall() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -07002158 return getFirstCallWithState(CallState.RINGING);
2159 }
2160
Brad Ebinger53855132015-10-30 10:58:19 -07002161 public Call getActiveCall() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -07002162 return getFirstCallWithState(CallState.ACTIVE);
2163 }
2164
Nancy Chen05a9e402014-09-26 14:14:32 -07002165 Call getDialingCall() {
2166 return getFirstCallWithState(CallState.DIALING);
Santos Cordon68d1a6b2014-09-19 12:25:58 -07002167 }
2168
Brad Ebinger53855132015-10-30 10:58:19 -07002169 @VisibleForTesting
2170 public Call getHeldCall() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -07002171 return getFirstCallWithState(CallState.ON_HOLD);
2172 }
2173
Pengquan Meng2fc55272018-03-19 17:36:32 -07002174 public Call getHeldCallByConnectionService(ConnectionServiceWrapper connSvr) {
2175 Optional<Call> heldCall = mCalls.stream()
2176 .filter(call -> call.getConnectionService() == connSvr
2177 && call.getState() == CallState.ON_HOLD)
2178 .findFirst();
2179 return heldCall.isPresent() ? heldCall.get() : null;
2180 }
2181
Brad Ebinger53855132015-10-30 10:58:19 -07002182 @VisibleForTesting
2183 public int getNumHeldCalls() {
Santos Cordonc0ca76e2014-10-21 15:54:19 -07002184 int count = 0;
2185 for (Call call : mCalls) {
2186 if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
2187 count++;
2188 }
2189 }
2190 return count;
2191 }
2192
Brad Ebinger53855132015-10-30 10:58:19 -07002193 @VisibleForTesting
2194 public Call getOutgoingCall() {
Roshan Pius7d7cf272015-08-28 11:10:56 -07002195 return getFirstCallWithState(OUTGOING_CALL_STATES);
2196 }
2197
Hall Liu0a6dd302015-12-16 15:06:49 -08002198 @VisibleForTesting
2199 public Call getFirstCallWithState(int... states) {
Santos Cordonf193ba42014-09-12 06:37:39 -07002200 return getFirstCallWithState(null, states);
2201 }
2202
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07002203 @VisibleForTesting
2204 public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() {
2205 return mPhoneNumberUtilsAdapter;
2206 }
2207
Santos Cordon10838c22014-06-11 17:36:04 -07002208 /**
Santos Cordondeb8c892014-05-30 01:38:03 -07002209 * Returns the first call that it finds with the given states. The states are treated as having
2210 * priority order so that any call with the first state will be returned before any call with
2211 * states listed later in the parameter list.
Santos Cordonf193ba42014-09-12 06:37:39 -07002212 *
2213 * @param callToSkip Call that this method should skip while searching
Santos Cordondeb8c892014-05-30 01:38:03 -07002214 */
Santos Cordonf193ba42014-09-12 06:37:39 -07002215 Call getFirstCallWithState(Call callToSkip, int... states) {
Ihab Awad6fb37c82014-08-07 19:48:57 -07002216 for (int currentState : states) {
Santos Cordon23baed32014-06-27 14:45:39 -07002217 // check the foreground first
Hall Liue091ab92015-12-18 17:05:30 -08002218 Call foregroundCall = getForegroundCall();
2219 if (foregroundCall != null && foregroundCall.getState() == currentState) {
2220 return foregroundCall;
Santos Cordon23baed32014-06-27 14:45:39 -07002221 }
2222
Santos Cordondeb8c892014-05-30 01:38:03 -07002223 for (Call call : mCalls) {
Santos Cordonf193ba42014-09-12 06:37:39 -07002224 if (Objects.equals(callToSkip, call)) {
2225 continue;
2226 }
2227
2228 // Only operate on top-level calls
2229 if (call.getParentCall() != null) {
2230 continue;
2231 }
2232
Tyler Gunnf15dc332016-06-07 16:01:41 -07002233 if (call.isExternalCall()) {
2234 continue;
2235 }
2236
Santos Cordondeb8c892014-05-30 01:38:03 -07002237 if (currentState == call.getState()) {
2238 return call;
2239 }
2240 }
2241 }
2242 return null;
2243 }
2244
Santos Cordon0fbe6322014-08-14 04:04:25 -07002245 Call createConferenceCall(
Tyler Gunn8452be02015-09-17 09:57:02 -07002246 String callId,
Santos Cordon0fbe6322014-08-14 04:04:25 -07002247 PhoneAccountHandle phoneAccount,
2248 ParcelableConference parcelableConference) {
Tyler Gunnd92e7372015-01-09 15:59:45 -08002249
2250 // If the parceled conference specifies a connect time, use it; otherwise default to 0,
2251 // which is the default value for new Calls.
2252 long connectTime =
2253 parcelableConference.getConnectTimeMillis() ==
2254 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
2255 parcelableConference.getConnectTimeMillis();
Tyler Gunn8bb2b012017-08-04 09:28:59 -07002256 long connectElapsedTime =
2257 parcelableConference.getConnectElapsedTimeMillis() ==
2258 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
2259 parcelableConference.getConnectElapsedTimeMillis();
Tyler Gunnd92e7372015-01-09 15:59:45 -08002260
Santos Cordon0fbe6322014-08-14 04:04:25 -07002261 Call call = new Call(
Tyler Gunn8452be02015-09-17 09:57:02 -07002262 callId,
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002263 mContext,
Ihab Awad78a5e6b2015-02-06 10:13:05 -08002264 this,
Ihab Awade6dbc9d2015-03-26 10:33:44 -07002265 mLock,
Santos Cordon0fbe6322014-08-14 04:04:25 -07002266 mConnectionServiceRepository,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -07002267 mContactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -07002268 mCallerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07002269 mPhoneNumberUtilsAdapter,
Santos Cordon0fbe6322014-08-14 04:04:25 -07002270 null /* handle */,
2271 null /* gatewayInfo */,
2272 null /* connectionManagerPhoneAccount */,
2273 phoneAccount,
Hall Liu32587202015-11-18 11:10:08 -08002274 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
2275 false /* forceAttachToExistingConnection */,
Tyler Gunnd92e7372015-01-09 15:59:45 -08002276 true /* isConference */,
Tyler Gunn8bb2b012017-08-04 09:28:59 -07002277 connectTime,
2278 connectElapsedTime,
2279 mClockProxy);
Santos Cordon0fbe6322014-08-14 04:04:25 -07002280
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07002281 setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
2282 "new conference call");
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002283 call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
Tyler Gunn571d5e62016-03-15 15:55:18 -07002284 call.setConnectionProperties(parcelableConference.getConnectionProperties());
Tyler Gunn13e8a692016-08-03 18:38:00 -07002285 call.setVideoState(parcelableConference.getVideoState());
Rekha Kumard240fe82015-04-01 21:45:57 -07002286 call.setVideoProvider(parcelableConference.getVideoProvider());
Andrew Lee57412d32015-04-14 13:38:44 -07002287 call.setStatusHints(parcelableConference.getStatusHints());
Tyler Gunn961694a2016-03-21 16:01:40 -07002288 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras());
Tyler Gunn9b618b82016-10-17 15:54:35 -07002289 // In case this Conference was added via a ConnectionManager, keep track of the original
2290 // Connection ID as created by the originating ConnectionService.
2291 Bundle extras = parcelableConference.getExtras();
2292 if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
2293 call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
2294 }
Santos Cordon0fbe6322014-08-14 04:04:25 -07002295
2296 // TODO: Move this to be a part of addCall()
2297 call.addListener(this);
2298 addCall(call);
2299 return call;
2300 }
2301
Yorke Leef86db2e2014-09-12 17:56:48 -07002302 /**
2303 * @return the call state currently tracked by {@link PhoneStateBroadcaster}
2304 */
2305 int getCallState() {
2306 return mPhoneStateBroadcaster.getCallState();
2307 }
Nancy Chenbc9ef172014-08-15 17:11:57 -07002308
Santos Cordon4b2c1192014-03-19 18:15:38 -07002309 /**
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002310 * Retrieves the {@link PhoneAccountRegistrar}.
2311 *
2312 * @return The {@link PhoneAccountRegistrar}.
2313 */
2314 PhoneAccountRegistrar getPhoneAccountRegistrar() {
2315 return mPhoneAccountRegistrar;
2316 }
2317
2318 /**
2319 * Retrieves the {@link MissedCallNotifier}
2320 * @return The {@link MissedCallNotifier}.
2321 */
2322 MissedCallNotifier getMissedCallNotifier() {
2323 return mMissedCallNotifier;
2324 }
2325
2326 /**
Tyler Gunn2b17f232017-03-08 08:51:00 -08002327 * Retrieves the {@link IncomingCallNotifier}.
2328 * @return The {@link IncomingCallNotifier}.
2329 */
2330 IncomingCallNotifier getIncomingCallNotifier() {
2331 return mIncomingCallNotifier;
2332 }
2333
2334 /**
Brad Ebinger3165d502015-12-15 17:22:29 -08002335 * Reject an incoming call and manually add it to the Call Log.
2336 * @param incomingCall Incoming call that has been rejected
2337 */
2338 private void rejectCallAndLog(Call incomingCall) {
Tyler Gunn165ee752016-09-06 13:40:10 -07002339 if (incomingCall.getConnectionService() != null) {
2340 // Only reject the call if it has not already been destroyed. If a call ends while
2341 // incoming call filtering is taking place, it is possible that the call has already
2342 // been destroyed, and as such it will be impossible to send the reject to the
2343 // associated ConnectionService.
2344 incomingCall.reject(false, null);
2345 } else {
2346 Log.i(this, "rejectCallAndLog - call already destroyed.");
2347 }
2348
Brad Ebinger3165d502015-12-15 17:22:29 -08002349 // Since the call was not added to the list of calls, we have to call the missed
2350 // call notifier and the call logger manually.
2351 // Do we need missed call notification for direct to Voicemail calls?
Ta-wei Yen982c0bd2016-04-14 13:59:54 -07002352 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
2353 true /*showNotificationForMissedCall*/);
Brad Ebinger3165d502015-12-15 17:22:29 -08002354 }
2355
2356 /**
Ben Gilada0d9f752014-02-26 11:49:03 -08002357 * Adds the specified call to the main list of live calls.
2358 *
2359 * @param call The call to add.
2360 */
Tyler Gunnc74b3e22017-11-07 15:03:24 -08002361 @VisibleForTesting
2362 public void addCall(Call call) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002363 Trace.beginSection("addCall");
Yorke Leeb701f6d2014-08-12 14:04:19 -07002364 Log.v(this, "addCall(%s)", call);
Yorke Leeb701f6d2014-08-12 14:04:19 -07002365 call.addListener(this);
Sailesh Nepale59bb192014-04-01 18:33:59 -07002366 mCalls.add(call);
Santos Cordon40f78c22014-04-07 02:11:42 -07002367
Sailesh Nepal703a1af2016-04-14 20:10:12 -07002368 // Specifies the time telecom finished routing the call. This is used by the dialer for
2369 // analytics.
2370 Bundle extras = call.getIntentExtras();
2371 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
2372 SystemClock.elapsedRealtime());
2373
Hall Liufc130b22016-06-15 17:54:48 -07002374 updateCanAddCall();
Santos Cordon40f78c22014-04-07 02:11:42 -07002375 // onCallAdded for calls which immediately take the foreground (like the first call).
Santos Cordona56f2762014-03-24 15:55:53 -07002376 for (CallsManagerListener listener : mListeners) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002377 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002378 Trace.beginSection(listener.getClass().toString() + " addCall");
2379 }
Santos Cordona56f2762014-03-24 15:55:53 -07002380 listener.onCallAdded(call);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002381 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002382 Trace.endSection();
2383 }
Santos Cordona56f2762014-03-24 15:55:53 -07002384 }
Yorke Leee4a9c412014-11-14 16:59:42 -08002385 Trace.endSection();
Ben Gilada0d9f752014-02-26 11:49:03 -08002386 }
2387
Sailesh Nepal810735e2014-03-18 18:15:46 -07002388 private void removeCall(Call call) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002389 Trace.beginSection("removeCall");
Santos Cordonc499c1c2014-04-14 17:13:14 -07002390 Log.v(this, "removeCall(%s)", call);
Sailesh Nepal4857f472014-04-07 22:26:27 -07002391
Tyler Gunn76581712017-05-09 14:39:42 -07002392 call.setParentAndChildCall(null); // clean up parent relationship before destroying.
Santos Cordon766d04f2014-05-06 10:28:25 -07002393 call.removeListener(this);
Sailesh Nepalc92c4362014-07-04 18:33:21 -07002394 call.clearConnectionService();
Hall Liudd68bc32017-01-25 17:14:23 -08002395 // TODO: clean up RTT pipes
Sailesh Nepale59bb192014-04-01 18:33:59 -07002396
2397 boolean shouldNotify = false;
2398 if (mCalls.contains(call)) {
2399 mCalls.remove(call);
2400 shouldNotify = true;
Santos Cordona56f2762014-03-24 15:55:53 -07002401 }
Ben Gilada0d9f752014-02-26 11:49:03 -08002402
Santos Cordon2685dab2015-04-17 17:12:09 -07002403 call.destroy();
2404
Sailesh Nepale59bb192014-04-01 18:33:59 -07002405 // Only broadcast changes for calls that are being tracked.
2406 if (shouldNotify) {
Hall Liufc130b22016-06-15 17:54:48 -07002407 updateCanAddCall();
Sailesh Nepale59bb192014-04-01 18:33:59 -07002408 for (CallsManagerListener listener : mListeners) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002409 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002410 Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
2411 }
Sailesh Nepale59bb192014-04-01 18:33:59 -07002412 listener.onCallRemoved(call);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002413 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002414 Trace.endSection();
2415 }
Sailesh Nepale59bb192014-04-01 18:33:59 -07002416 }
Yorke Lee604a90f2014-10-20 12:13:32 -07002417 }
Yorke Leee4a9c412014-11-14 16:59:42 -08002418 Trace.endSection();
Sailesh Nepal810735e2014-03-18 18:15:46 -07002419 }
Evan Charlton5c670a92014-03-06 14:58:20 -08002420
Sailesh Nepal810735e2014-03-18 18:15:46 -07002421 /**
Santos Cordon1ae2b852014-03-19 03:03:10 -07002422 * Sets the specified state on the specified call.
Sailesh Nepal810735e2014-03-18 18:15:46 -07002423 *
2424 * @param call The call.
2425 * @param newState The new state of the call.
2426 */
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07002427 private void setCallState(Call call, int newState, String tag) {
Jay Shrauneracb91eb2014-08-08 16:04:53 -07002428 if (call == null) {
2429 return;
2430 }
Ihab Awad6fb37c82014-08-07 19:48:57 -07002431 int oldState = call.getState();
Nancy Chen308ab8b2014-09-02 16:18:30 -07002432 Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
2433 CallState.toString(newState), call);
Sailesh Nepal810735e2014-03-18 18:15:46 -07002434 if (newState != oldState) {
Tyler Gunnad972422017-11-13 13:36:35 -08002435 // If the call switches to held state while a DTMF tone is playing, stop the tone to
2436 // ensure that the tone generator stops playing the tone.
2437 if (newState == CallState.ON_HOLD && call.isDtmfTonePlaying()) {
2438 stopDtmfTone(call);
2439 }
2440
Sailesh Nepal810735e2014-03-18 18:15:46 -07002441 // Unfortunately, in the telephony world the radio is king. So if the call notifies
2442 // us that the call is in a particular state, we allow it even if it doesn't make
Ihab Awad6fb37c82014-08-07 19:48:57 -07002443 // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
Santos Cordondf399862014-08-06 04:39:15 -07002444 // TODO: Consider putting a stop to the above and turning CallState
Sailesh Nepal810735e2014-03-18 18:15:46 -07002445 // into a well-defined state machine.
Santos Cordondf399862014-08-06 04:39:15 -07002446 // TODO: Define expected state transitions here, and log when an
Sailesh Nepal810735e2014-03-18 18:15:46 -07002447 // unexpected transition occurs.
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07002448 call.setState(newState, tag);
Xueren Zhangc0e8e9d2015-03-06 14:16:55 +01002449 maybeShowErrorDialogOnDisconnect(call);
Sailesh Nepal810735e2014-03-18 18:15:46 -07002450
Yorke Leee4a9c412014-11-14 16:59:42 -08002451 Trace.beginSection("onCallStateChanged");
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002452
Tyler Gunn141ef582017-05-26 13:38:13 -07002453 maybeHandleHandover(call, newState);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002454
Sailesh Nepal810735e2014-03-18 18:15:46 -07002455 // Only broadcast state change for calls that are being tracked.
Sailesh Nepale59bb192014-04-01 18:33:59 -07002456 if (mCalls.contains(call)) {
Hall Liufc130b22016-06-15 17:54:48 -07002457 updateCanAddCall();
Santos Cordona56f2762014-03-24 15:55:53 -07002458 for (CallsManagerListener listener : mListeners) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002459 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002460 Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
2461 }
Santos Cordona56f2762014-03-24 15:55:53 -07002462 listener.onCallStateChanged(call, oldState, newState);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002463 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002464 Trace.endSection();
2465 }
Santos Cordona56f2762014-03-24 15:55:53 -07002466 }
Santos Cordon4e9fffe2014-03-04 18:13:41 -08002467 }
Yorke Leee4a9c412014-11-14 16:59:42 -08002468 Trace.endSection();
Santos Cordon4e9fffe2014-03-04 18:13:41 -08002469 }
2470 }
2471
Tyler Gunn141ef582017-05-26 13:38:13 -07002472 /**
2473 * Identifies call state transitions for a call which trigger handover events.
2474 * - If this call has a handover to it which just started and this call goes active, treat
2475 * this as if the user accepted the handover.
2476 * - If this call has a handover to it which just started and this call is disconnected, treat
2477 * this as if the user rejected the handover.
2478 * - If this call has a handover from it which just started and this call is disconnected, do
2479 * nothing as the call prematurely disconnected before the user accepted the handover.
2480 * - If this call has a handover from it which was already accepted by the user and this call is
2481 * disconnected, mark the handover as complete.
2482 *
2483 * @param call A call whose state is changing.
2484 * @param newState The new state of the call.
2485 */
2486 private void maybeHandleHandover(Call call, int newState) {
2487 if (call.getHandoverSourceCall() != null) {
2488 // We are handing over another call to this one.
2489 if (call.getHandoverState() == HandoverState.HANDOVER_TO_STARTED) {
2490 // A handover to this call has just been initiated.
2491 if (newState == CallState.ACTIVE) {
2492 // This call went active, so the user has accepted the handover.
2493 Log.i(this, "setCallState: handover to accepted");
2494 acceptHandoverTo(call);
2495 } else if (newState == CallState.DISCONNECTED) {
2496 // The call was disconnected, so the user has rejected the handover.
2497 Log.i(this, "setCallState: handover to rejected");
2498 rejectHandoverTo(call);
2499 }
2500 }
2501 // If this call was disconnected because it was handed over TO another call, report the
2502 // handover as complete.
2503 } else if (call.getHandoverDestinationCall() != null
2504 && newState == CallState.DISCONNECTED) {
2505 int handoverState = call.getHandoverState();
2506 if (handoverState == HandoverState.HANDOVER_FROM_STARTED) {
2507 // Disconnect before handover was accepted.
2508 Log.i(this, "setCallState: disconnect before handover accepted");
Tyler Gunn845a6772017-06-23 08:30:10 -07002509 // Let the handover destination know that the source has disconnected prior to
2510 // completion of the handover.
2511 call.getHandoverDestinationCall().sendCallEvent(
2512 android.telecom.Call.EVENT_HANDOVER_SOURCE_DISCONNECTED, null);
Tyler Gunn141ef582017-05-26 13:38:13 -07002513 } else if (handoverState == HandoverState.HANDOVER_ACCEPTED) {
2514 Log.i(this, "setCallState: handover from complete");
2515 completeHandoverFrom(call);
2516 }
2517 }
2518 }
2519
2520 private void completeHandoverFrom(Call call) {
2521 Call handoverTo = call.getHandoverDestinationCall();
2522 Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
2523 call.getId(), handoverTo.getId());
2524 Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s",
2525 call.getId(), handoverTo.getId());
2526
2527 // Inform the "from" Call (ie the source call) that the handover from it has
2528 // completed; this allows the InCallService to be notified that a handover it
2529 // initiated completed.
2530 call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null);
Tyler Gunn7c031f22018-01-18 15:00:41 -08002531 call.onHandoverComplete();
2532
Tyler Gunn141ef582017-05-26 13:38:13 -07002533 // Inform the "to" ConnectionService that handover to it has completed.
2534 handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null);
Tyler Gunn7c031f22018-01-18 15:00:41 -08002535 handoverTo.onHandoverComplete();
Tyler Gunn141ef582017-05-26 13:38:13 -07002536 answerCall(handoverTo, handoverTo.getVideoState());
2537 call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE);
Tyler Gunn632547e2017-08-22 15:40:38 -07002538
2539 // If the call we handed over to is self-managed, we need to disconnect the calls for other
2540 // ConnectionServices.
2541 if (handoverTo.isSelfManaged()) {
2542 disconnectOtherCalls(handoverTo.getTargetPhoneAccount());
2543 }
Tyler Gunn141ef582017-05-26 13:38:13 -07002544 }
2545
2546 private void rejectHandoverTo(Call handoverTo) {
2547 Call handoverFrom = handoverTo.getHandoverSourceCall();
2548 Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId());
Tyler Gunn97f30dc2018-03-07 21:09:58 +00002549 Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected",
Tyler Gunn141ef582017-05-26 13:38:13 -07002550 handoverTo.getId(), handoverFrom.getId());
Tyler Gunn97f30dc2018-03-07 21:09:58 +00002551 Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected",
Tyler Gunn141ef582017-05-26 13:38:13 -07002552 handoverTo.getId(), handoverFrom.getId());
2553
2554 // Inform the "from" Call (ie the source call) that the handover from it has
2555 // failed; this allows the InCallService to be notified that a handover it
2556 // initiated failed.
2557 handoverFrom.onConnectionEvent(Connection.EVENT_HANDOVER_FAILED, null);
Tyler Gunn97f30dc2018-03-07 21:09:58 +00002558 handoverFrom.onHandoverFailed(android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED);
2559
Tyler Gunn141ef582017-05-26 13:38:13 -07002560 // Inform the "to" ConnectionService that handover to it has failed. This
2561 // allows the ConnectionService the call was being handed over
2562 if (handoverTo.getConnectionService() != null) {
2563 // Only attempt if the call has a bound ConnectionService if handover failed
2564 // early on in the handover process, the CS will be unbound and we won't be
2565 // able to send the call event.
2566 handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null);
Tyler Gunn97f30dc2018-03-07 21:09:58 +00002567 handoverTo.getConnectionService().handoverFailed(handoverTo,
2568 android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED);
Tyler Gunn141ef582017-05-26 13:38:13 -07002569 }
2570 handoverTo.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_FAILED);
2571 }
2572
2573 private void acceptHandoverTo(Call handoverTo) {
2574 Call handoverFrom = handoverTo.getHandoverSourceCall();
2575 Log.i(this, "acceptHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId());
2576 handoverTo.setHandoverState(HandoverState.HANDOVER_ACCEPTED);
Tyler Gunn97f30dc2018-03-07 21:09:58 +00002577 handoverTo.onHandoverComplete();
Tyler Gunn141ef582017-05-26 13:38:13 -07002578 handoverFrom.setHandoverState(HandoverState.HANDOVER_ACCEPTED);
Tyler Gunn97f30dc2018-03-07 21:09:58 +00002579 handoverFrom.onHandoverComplete();
Tyler Gunn141ef582017-05-26 13:38:13 -07002580
2581 Log.addEvent(handoverTo, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s",
2582 handoverFrom.getId(), handoverTo.getId());
2583 Log.addEvent(handoverFrom, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s",
2584 handoverFrom.getId(), handoverTo.getId());
2585
2586 // Disconnect the call we handed over from.
2587 disconnectCall(handoverFrom);
Tyler Gunn632547e2017-08-22 15:40:38 -07002588 // If we handed over to a self-managed ConnectionService, we need to disconnect calls for
2589 // other ConnectionServices.
2590 if (handoverTo.isSelfManaged()) {
2591 disconnectOtherCalls(handoverTo.getTargetPhoneAccount());
2592 }
Tyler Gunn141ef582017-05-26 13:38:13 -07002593 }
2594
Santos Cordon91bd74c2014-11-07 16:10:22 -08002595 private void updateCanAddCall() {
2596 boolean newCanAddCall = canAddCall();
2597 if (newCanAddCall != mCanAddCall) {
2598 mCanAddCall = newCanAddCall;
2599 for (CallsManagerListener listener : mListeners) {
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002600 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002601 Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
2602 }
Santos Cordon91bd74c2014-11-07 16:10:22 -08002603 listener.onCanAddCallChanged(mCanAddCall);
Brad Ebingera3eccfe2016-10-05 15:45:22 -07002604 if (LogUtils.SYSTRACE_DEBUG) {
Yorke Leee4a9c412014-11-14 16:59:42 -08002605 Trace.endSection();
2606 }
Santos Cordon91bd74c2014-11-07 16:10:22 -08002607 }
2608 }
2609 }
2610
Yorke Leeb701f6d2014-08-12 14:04:19 -07002611 private boolean isPotentialMMICode(Uri handle) {
2612 return (handle != null && handle.getSchemeSpecificPart() != null
2613 && handle.getSchemeSpecificPart().contains("#"));
2614 }
Santos Cordonf193ba42014-09-12 06:37:39 -07002615
Tyler Gunn5b452df2014-10-01 15:02:58 -07002616 /**
2617 * Determines if a dialed number is potentially an In-Call MMI code. In-Call MMI codes are
2618 * MMI codes which can be dialed when one or more calls are in progress.
2619 * <P>
2620 * Checks for numbers formatted similar to the MMI codes defined in:
Amit Mahajan8c4e22a2015-12-14 13:24:15 -08002621 * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)}
Tyler Gunn5b452df2014-10-01 15:02:58 -07002622 *
2623 * @param handle The URI to call.
2624 * @return {@code True} if the URI represents a number which could be an in-call MMI code.
2625 */
2626 private boolean isPotentialInCallMMICode(Uri handle) {
2627 if (handle != null && handle.getSchemeSpecificPart() != null &&
Xueren Zhangc0e8e9d2015-03-06 14:16:55 +01002628 handle.getScheme() != null &&
Tyler Gunn5b452df2014-10-01 15:02:58 -07002629 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
2630
2631 String dialedNumber = handle.getSchemeSpecificPart();
2632 return (dialedNumber.equals("0") ||
2633 (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
2634 (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
2635 dialedNumber.equals("3") ||
2636 dialedNumber.equals("4") ||
2637 dialedNumber.equals("5"));
2638 }
2639 return false;
2640 }
2641
Tyler Gunna90ba732017-01-26 07:24:08 -08002642 @VisibleForTesting
2643 public int getNumCallsWithState(final boolean isSelfManaged, Call excludeCall,
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002644 PhoneAccountHandle phoneAccountHandle, int... states) {
2645 return getNumCallsWithState(isSelfManaged ? CALL_FILTER_SELF_MANAGED : CALL_FILTER_MANAGED,
2646 excludeCall, phoneAccountHandle, states);
2647 }
2648
2649 /**
2650 * Determines the number of calls matching the specified criteria.
2651 * @param callFilter indicates whether to include just managed calls
2652 * ({@link #CALL_FILTER_MANAGED}), self-managed calls
2653 * ({@link #CALL_FILTER_SELF_MANAGED}), or all calls
2654 * ({@link #CALL_FILTER_ALL}).
2655 * @param excludeCall Where {@code non-null}, this call is excluded from the count.
2656 * @param phoneAccountHandle Where {@code non-null}, calls for this {@link PhoneAccountHandle}
2657 * are excluded from the count.
2658 * @param states The list of {@link CallState}s to include in the count.
2659 * @return Count of calls matching criteria.
2660 */
2661 @VisibleForTesting
2662 public int getNumCallsWithState(final int callFilter, Call excludeCall,
2663 PhoneAccountHandle phoneAccountHandle, int... states) {
Tyler Gunnf15dc332016-06-07 16:01:41 -07002664
Tyler Gunna90ba732017-01-26 07:24:08 -08002665 Set<Integer> desiredStates = IntStream.of(states).boxed().collect(Collectors.toSet());
2666
2667 Stream<Call> callsStream = mCalls.stream()
2668 .filter(call -> desiredStates.contains(call.getState()) &&
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002669 call.getParentCall() == null && !call.isExternalCall());
Tyler Gunna90ba732017-01-26 07:24:08 -08002670
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002671 if (callFilter == CALL_FILTER_MANAGED) {
2672 callsStream = callsStream.filter(call -> !call.isSelfManaged());
2673 } else if (callFilter == CALL_FILTER_SELF_MANAGED) {
2674 callsStream = callsStream.filter(call -> call.isSelfManaged());
2675 }
2676
2677 // If a call to exclude was specified, filter it out.
Tyler Gunna90ba732017-01-26 07:24:08 -08002678 if (excludeCall != null) {
2679 callsStream = callsStream.filter(call -> call != excludeCall);
Santos Cordonf193ba42014-09-12 06:37:39 -07002680 }
Tyler Gunna90ba732017-01-26 07:24:08 -08002681
2682 // If a phone account handle was specified, only consider calls for that phone account.
2683 if (phoneAccountHandle != null) {
2684 callsStream = callsStream.filter(
2685 call -> phoneAccountHandle.equals(call.getTargetPhoneAccount()));
2686 }
2687
2688 return (int) callsStream.count();
Santos Cordonf193ba42014-09-12 06:37:39 -07002689 }
2690
Pengquan Meng4832f202017-12-20 16:13:04 -08002691 private boolean hasMaximumLiveCalls(Call exceptCall) {
2692 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
2693 exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES);
2694 }
2695
Tyler Gunna90ba732017-01-26 07:24:08 -08002696 private boolean hasMaximumManagedLiveCalls(Call exceptCall) {
2697 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(false /* isSelfManaged */,
2698 exceptCall, null /* phoneAccountHandle */, LIVE_CALL_STATES);
Santos Cordonf193ba42014-09-12 06:37:39 -07002699 }
2700
Tyler Gunna90ba732017-01-26 07:24:08 -08002701 private boolean hasMaximumSelfManagedCalls(Call exceptCall,
2702 PhoneAccountHandle phoneAccountHandle) {
2703 return MAXIMUM_SELF_MANAGED_CALLS <= getNumCallsWithState(true /* isSelfManaged */,
2704 exceptCall, phoneAccountHandle, ANY_CALL_STATE);
Santos Cordonf193ba42014-09-12 06:37:39 -07002705 }
2706
Tyler Gunna90ba732017-01-26 07:24:08 -08002707 private boolean hasMaximumManagedHoldingCalls(Call exceptCall) {
2708 return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall,
2709 null /* phoneAccountHandle */, CallState.ON_HOLD);
Santos Cordonf193ba42014-09-12 06:37:39 -07002710 }
2711
Tyler Gunna90ba732017-01-26 07:24:08 -08002712 private boolean hasMaximumManagedRingingCalls(Call exceptCall) {
2713 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall,
2714 null /* phoneAccountHandle */, CallState.RINGING);
Santos Cordonf193ba42014-09-12 06:37:39 -07002715 }
2716
Tyler Gunna90ba732017-01-26 07:24:08 -08002717 private boolean hasMaximumSelfManagedRingingCalls(Call exceptCall,
2718 PhoneAccountHandle phoneAccountHandle) {
2719 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(true /* isSelfManaged */, exceptCall,
2720 phoneAccountHandle, CallState.RINGING);
2721 }
2722
Pengquan Meng4832f202017-12-20 16:13:04 -08002723 private boolean hasMaximumOutgoingCalls(Call exceptCall) {
2724 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
2725 exceptCall, null /* phoneAccountHandle */, OUTGOING_CALL_STATES);
2726 }
2727
Tyler Gunna90ba732017-01-26 07:24:08 -08002728 private boolean hasMaximumManagedOutgoingCalls(Call exceptCall) {
2729 return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall,
2730 null /* phoneAccountHandle */, OUTGOING_CALL_STATES);
2731 }
2732
2733 private boolean hasMaximumManagedDialingCalls(Call exceptCall) {
2734 return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall,
2735 null /* phoneAccountHandle */, CallState.DIALING, CallState.PULLING);
2736 }
2737
2738 /**
2739 * Given a {@link PhoneAccountHandle} determines if there are calls owned by any other
2740 * {@link PhoneAccountHandle}.
2741 * @param phoneAccountHandle The {@link PhoneAccountHandle} to check.
2742 * @return {@code true} if there are other calls, {@code false} otherwise.
2743 */
2744 public boolean hasCallsForOtherPhoneAccount(PhoneAccountHandle phoneAccountHandle) {
Tyler Gunn2b17f232017-03-08 08:51:00 -08002745 return getNumCallsForOtherPhoneAccount(phoneAccountHandle) > 0;
2746 }
2747
2748 /**
2749 * Determines the number of calls present for PhoneAccounts other than the one specified.
2750 * @param phoneAccountHandle The handle of the PhoneAccount.
2751 * @return Number of calls owned by other PhoneAccounts.
2752 */
2753 public int getNumCallsForOtherPhoneAccount(PhoneAccountHandle phoneAccountHandle) {
2754 return (int) mCalls.stream().filter(call ->
Tyler Gunna90ba732017-01-26 07:24:08 -08002755 !phoneAccountHandle.equals(call.getTargetPhoneAccount()) &&
Tyler Gunn2b17f232017-03-08 08:51:00 -08002756 call.getParentCall() == null &&
2757 !call.isExternalCall()).count();
Tyler Gunna90ba732017-01-26 07:24:08 -08002758 }
2759
2760 /**
Tyler Gunn37e782b2017-02-10 09:42:03 -08002761 * Determines if there are any managed calls.
Tyler Gunna90ba732017-01-26 07:24:08 -08002762 * @return {@code true} if there are managed calls, {@code false} otherwise.
2763 */
2764 public boolean hasManagedCalls() {
2765 return mCalls.stream().filter(call -> !call.isSelfManaged() &&
2766 !call.isExternalCall()).count() > 0;
Roshan Pius4de4a892015-08-24 11:06:58 -07002767 }
2768
Tyler Gunn37e782b2017-02-10 09:42:03 -08002769 /**
Tyler Gunn2b17f232017-03-08 08:51:00 -08002770 * Determines if there are any self-managed calls.
2771 * @return {@code true} if there are self-managed calls, {@code false} otherwise.
2772 */
2773 public boolean hasSelfManagedCalls() {
2774 return mCalls.stream().filter(call -> call.isSelfManaged()).count() > 0;
2775 }
2776
2777 /**
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002778 * Determines if there are any ongoing managed or self-managed calls.
2779 * Note: The {@link #ONGOING_CALL_STATES} are
2780 * @return {@code true} if there are ongoing managed or self-managed calls, {@code false}
2781 * otherwise.
2782 */
2783 public boolean hasOngoingCalls() {
2784 return getNumCallsWithState(
2785 CALL_FILTER_ALL, null /* excludeCall */,
2786 null /* phoneAccountHandle */,
2787 ONGOING_CALL_STATES) > 0;
2788 }
2789
2790 /**
Tyler Gunn37e782b2017-02-10 09:42:03 -08002791 * Determines if there are any ongoing managed calls.
2792 * @return {@code true} if there are ongoing managed calls, {@code false} otherwise.
2793 */
2794 public boolean hasOngoingManagedCalls() {
2795 return getNumCallsWithState(
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002796 CALL_FILTER_MANAGED, null /* excludeCall */,
Tyler Gunn37e782b2017-02-10 09:42:03 -08002797 null /* phoneAccountHandle */,
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002798 ONGOING_CALL_STATES) > 0;
Tyler Gunn37e782b2017-02-10 09:42:03 -08002799 }
2800
Tyler Gunn2b17f232017-03-08 08:51:00 -08002801 /**
Tyler Gunnbaf105b2017-04-11 15:21:03 -07002802 * Determines if the system incoming call UI should be shown.
Tyler Gunn2b17f232017-03-08 08:51:00 -08002803 * The system incoming call UI will be shown if the new incoming call is self-managed, and there
2804 * are ongoing calls for another PhoneAccount.
2805 * @param incomingCall The incoming call.
2806 * @return {@code true} if the system incoming call UI should be shown, {@code false} otherwise.
2807 */
2808 public boolean shouldShowSystemIncomingCallUi(Call incomingCall) {
2809 return incomingCall.isIncoming() && incomingCall.isSelfManaged() &&
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07002810 hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) &&
Tyler Gunn141ef582017-05-26 13:38:13 -07002811 incomingCall.getHandoverSourceCall() == null;
Tyler Gunn2b17f232017-03-08 08:51:00 -08002812 }
2813
Santos Cordonf193ba42014-09-12 06:37:39 -07002814 private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
Pengquan Meng4832f202017-12-20 16:13:04 -08002815 if (hasMaximumLiveCalls(call)) {
Santos Cordonf193ba42014-09-12 06:37:39 -07002816 // NOTE: If the amount of live calls changes beyond 1, this logic will probably
2817 // have to change.
Tony Makf5a32e42016-02-02 12:10:26 +00002818 Call liveCall = getFirstCallWithState(LIVE_CALL_STATES);
Anju Mathapatic88574c2014-12-16 14:46:19 +05302819 Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
2820 liveCall);
Santos Cordonf193ba42014-09-12 06:37:39 -07002821
Santos Cordond1766502014-09-26 15:45:39 -07002822 if (call == liveCall) {
2823 // If the call is already the foreground call, then we are golden.
Santos Cordon92694512015-04-23 14:47:28 -07002824 // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT
Santos Cordond1766502014-09-26 15:45:39 -07002825 // state since the call was already populated into the list.
2826 return true;
2827 }
2828
Pengquan Meng4832f202017-12-20 16:13:04 -08002829 if (hasMaximumOutgoingCalls(call)) {
Yorke Lee867907d2015-06-03 12:24:43 -07002830 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
2831 if (isEmergency && !outgoingCall.isEmergencyCall()) {
2832 // Disconnect the current outgoing call if it's not an emergency call. If the
2833 // user tries to make two outgoing calls to different emergency call numbers,
2834 // we will try to connect the first outgoing call.
Hall Liu32587202015-11-18 11:10:08 -08002835 call.getAnalytics().setCallIsAdditional(true);
2836 outgoingCall.getAnalytics().setCallIsInterrupted(true);
Yorke Lee867907d2015-06-03 12:24:43 -07002837 outgoingCall.disconnect();
2838 return true;
2839 }
2840 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
2841 // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT}
2842 // state, just disconnect it since the user has explicitly started a new call.
Hall Liu32587202015-11-18 11:10:08 -08002843 call.getAnalytics().setCallIsAdditional(true);
2844 outgoingCall.getAnalytics().setCallIsInterrupted(true);
Yorke Lee867907d2015-06-03 12:24:43 -07002845 outgoingCall.disconnect();
2846 return true;
Andrew Leee6595182014-09-23 18:43:05 -07002847 }
2848 return false;
2849 }
2850
Pengquan Meng4832f202017-12-20 16:13:04 -08002851 // Disconnected the live call if the outgoing call is an emergency call.
2852 if (isEmergency && !canHold(liveCall)) {
2853 call.getAnalytics().setCallIsAdditional(true);
2854 liveCall.getAnalytics().setCallIsInterrupted(true);
Tyler Gunnf4f05392018-03-26 18:57:59 +00002855 liveCall.disconnect("emergency, can't hold");
Pengquan Meng4832f202017-12-20 16:13:04 -08002856 return true;
Santos Cordonf193ba42014-09-12 06:37:39 -07002857 }
2858
Tyler Gunne4cd9162015-08-11 15:36:04 -07002859 // TODO: Remove once b/23035408 has been corrected.
2860 // If the live call is a conference, it will not have a target phone account set. This
2861 // means the check to see if the live call has the same target phone account as the new
2862 // call will not cause us to bail early. As a result, we'll end up holding the
2863 // ongoing conference call. However, the ConnectionService is already doing that. This
2864 // has caused problems with some carriers. As a workaround until b/23035408 is
2865 // corrected, we will try and get the target phone account for one of the conference's
2866 // children and use that instead.
2867 PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount();
2868 if (liveCallPhoneAccount == null && liveCall.isConference() &&
2869 !liveCall.getChildCalls().isEmpty()) {
2870 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall);
2871 Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " +
2872 liveCallPhoneAccount);
2873 }
2874
Santos Cordonf193ba42014-09-12 06:37:39 -07002875 // First thing, if we are trying to make a call with the same phone account as the live
2876 // call, then allow it so that the connection service can make its own decision about
2877 // how to handle the new call relative to the current one.
Tyler Gunne4cd9162015-08-11 15:36:04 -07002878 if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) {
2879 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches.");
Hall Liu32587202015-11-18 11:10:08 -08002880 call.getAnalytics().setCallIsAdditional(true);
2881 liveCall.getAnalytics().setCallIsInterrupted(true);
Santos Cordonf193ba42014-09-12 06:37:39 -07002882 return true;
2883 } else if (call.getTargetPhoneAccount() == null) {
2884 // Without a phone account, we can't say reliably that the call will fail.
2885 // If the user chooses the same phone account as the live call, then it's
2886 // still possible that the call can be made (like with CDMA calls not supporting
2887 // hold but they still support adding a call by going immediately into conference
2888 // mode). Return true here and we'll run this code again after user chooses an
2889 // account.
2890 return true;
2891 }
2892
2893 // Try to hold the live call before attempting the new outgoing call.
Pengquan Meng4832f202017-12-20 16:13:04 -08002894 if (canHold(liveCall)) {
Tyler Gunne4cd9162015-08-11 15:36:04 -07002895 Log.i(this, "makeRoomForOutgoingCall: holding live call.");
Hall Liu32587202015-11-18 11:10:08 -08002896 call.getAnalytics().setCallIsAdditional(true);
2897 liveCall.getAnalytics().setCallIsInterrupted(true);
Tyler Gunnf4f05392018-03-26 18:57:59 +00002898 liveCall.hold("calling " + call.getId());
Santos Cordonf193ba42014-09-12 06:37:39 -07002899 return true;
2900 }
2901
2902 // The live call cannot be held so we're out of luck here. There's no room.
2903 return false;
2904 }
2905 return true;
2906 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -07002907
2908 /**
Tyler Gunne4cd9162015-08-11 15:36:04 -07002909 * Given a call, find the first non-null phone account handle of its children.
2910 *
2911 * @param parentCall The parent call.
2912 * @return The first non-null phone account handle of the children, or {@code null} if none.
2913 */
2914 private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) {
2915 for (Call childCall : parentCall.getChildCalls()) {
2916 PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount();
2917 if (childPhoneAccount != null) {
2918 return childPhoneAccount;
2919 }
2920 }
2921 return null;
2922 }
2923
2924 /**
Santos Cordond6782cd2015-04-16 09:57:50 -07002925 * Checks to see if the call should be on speakerphone and if so, set it.
2926 */
2927 private void maybeMoveToSpeakerPhone(Call call) {
Tyler Gunnb821f922017-06-21 14:40:19 -07002928 if (call.isHandoverInProgress() && call.getState() == CallState.DIALING) {
2929 // When a new outgoing call is initiated for the purpose of handing over, do not engage
2930 // speaker automatically until the call goes active.
2931 return;
2932 }
Santos Cordond6782cd2015-04-16 09:57:50 -07002933 if (call.getStartWithSpeakerphoneOn()) {
Hall Liu9086fb12017-11-07 18:01:53 -08002934 setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
Santos Cordond6782cd2015-04-16 09:57:50 -07002935 call.setStartWithSpeakerphoneOn(false);
2936 }
2937 }
2938
2939 /**
Omata Shou6bc865c2017-07-26 13:39:27 +09002940 * Checks to see if the call is an emergency call and if so, turn off mute.
2941 */
2942 private void maybeTurnOffMute(Call call) {
2943 if (call.isEmergencyCall()) {
2944 mute(false);
2945 }
2946 }
2947
2948 /**
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002949 * Creates a new call for an existing connection.
2950 *
2951 * @param callId The id of the new call.
2952 * @param connection The connection information.
2953 * @return The new call.
2954 */
2955 Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
Hall Liu4b9e5562016-07-14 15:34:56 -07002956 boolean isDowngradedConference = (connection.getConnectionProperties()
2957 & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002958 Call call = new Call(
Tyler Gunn234d5422015-12-08 14:27:49 -08002959 callId,
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002960 mContext,
Ihab Awad78a5e6b2015-02-06 10:13:05 -08002961 this,
Ihab Awade6dbc9d2015-03-26 10:33:44 -07002962 mLock,
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002963 mConnectionServiceRepository,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -07002964 mContactsAsyncHelper,
Ihab Awadabcbce42015-04-07 14:04:01 -07002965 mCallerInfoAsyncQueryFactory,
Brad Ebinger6e8f3d72016-06-20 11:35:42 -07002966 mPhoneNumberUtilsAdapter,
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002967 connection.getHandle() /* handle */,
2968 null /* gatewayInfo */,
2969 null /* connectionManagerPhoneAccount */,
2970 connection.getPhoneAccount(), /* targetPhoneAccountHandle */
Hall Liu32587202015-11-18 11:10:08 -08002971 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
2972 false /* forceAttachToExistingConnection */,
Hall Liu4b9e5562016-07-14 15:34:56 -07002973 isDowngradedConference /* isConference */,
Tyler Gunn8bb2b012017-08-04 09:28:59 -07002974 connection.getConnectTimeMillis() /* connectTimeMillis */,
2975 connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */
2976 mClockProxy);
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002977
Hall Liu32587202015-11-18 11:10:08 -08002978 call.initAnalytics();
2979 call.getAnalytics().setCreatedFromExistingConnection(true);
2980
Santos Cordon5fa4e4f2015-06-10 14:56:01 -07002981 setCallState(call, Call.getStateFromConnectionState(connection.getState()),
2982 "existing connection");
Ihab Awad07bc5ee2014-11-12 13:42:52 -08002983 call.setConnectionCapabilities(connection.getConnectionCapabilities());
Tyler Gunn571d5e62016-03-15 15:55:18 -07002984 call.setConnectionProperties(connection.getConnectionProperties());
Tyler Gunn26d478f2017-06-20 11:16:57 -07002985 call.setHandle(connection.getHandle(), connection.getHandlePresentation());
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002986 call.setCallerDisplayName(connection.getCallerDisplayName(),
2987 connection.getCallerDisplayNamePresentation());
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07002988 call.addListener(this);
Tyler Gunn9b618b82016-10-17 15:54:35 -07002989
2990 // In case this connection was added via a ConnectionManager, keep track of the original
2991 // Connection ID as created by the originating ConnectionService.
2992 Bundle extras = connection.getExtras();
2993 if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
2994 call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
2995 }
Tyler Gunn76581712017-05-09 14:39:42 -07002996 Log.i(this, "createCallForExistingConnection: %s", connection);
2997 Call parentCall = null;
2998 if (!TextUtils.isEmpty(connection.getParentCallId())) {
2999 String parentId = connection.getParentCallId();
3000 parentCall = mCalls
3001 .stream()
3002 .filter(c -> c.getId().equals(parentId))
3003 .findFirst()
3004 .orElse(null);
3005 if (parentCall != null) {
3006 Log.i(this, "createCallForExistingConnection: %s added as child of %s.",
3007 call.getId(),
3008 parentCall.getId());
3009 // Set JUST the parent property, which won't send an update to the Incall UI.
3010 call.setParentCall(parentCall);
3011 }
3012 }
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07003013 addCall(call);
Tyler Gunn76581712017-05-09 14:39:42 -07003014 if (parentCall != null) {
3015 // Now, set the call as a child of the parent since it has been added to Telecom. This
3016 // is where we will inform InCall.
3017 call.setChildOf(parentCall);
Tyler Gunn988ccc82017-06-01 11:22:10 -07003018 call.notifyParentChanged(parentCall);
Tyler Gunn76581712017-05-09 14:39:42 -07003019 }
Tyler Gunn6e2b94e2014-10-30 11:05:22 -07003020
3021 return call;
3022 }
3023
3024 /**
Tyler Gunn9b618b82016-10-17 15:54:35 -07003025 * Determines whether Telecom already knows about a Connection added via the
3026 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
3027 * Connection)} API via a ConnectionManager.
3028 *
3029 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
3030 * @param originalConnectionId The new connection ID to check.
3031 * @return {@code true} if this connection is already known by Telecom.
3032 */
3033 Call getAlreadyAddedConnection(String originalConnectionId) {
3034 Optional<Call> existingCall = mCalls.stream()
3035 .filter(call -> originalConnectionId.equals(call.getOriginalConnectionId()) ||
3036 originalConnectionId.equals(call.getId()))
3037 .findFirst();
3038
3039 if (existingCall.isPresent()) {
3040 Log.i(this, "isExistingConnectionAlreadyAdded - call %s already added with id %s",
3041 originalConnectionId, existingCall.get().getId());
3042 return existingCall.get();
3043 }
3044
3045 return null;
3046 }
3047
3048 /**
Tyler Gunn8452be02015-09-17 09:57:02 -07003049 * @return A new unique telecom call Id.
3050 */
3051 private String getNextCallId() {
3052 synchronized(mLock) {
3053 return TELECOM_CALL_ID_PREFIX + (++mCallId);
3054 }
3055 }
3056
Hall Liuaeece4e2017-02-14 16:42:12 -08003057 public int getNextRttRequestId() {
3058 synchronized (mLock) {
3059 return (++mRttRequestId);
3060 }
3061 }
3062
Tyler Gunn8452be02015-09-17 09:57:02 -07003063 /**
Tony Maka9930942016-01-15 10:57:14 +00003064 * Callback when foreground user is switched. We will reload missed call in all profiles
3065 * including the user itself. There may be chances that profiles are not started yet.
3066 */
Tyler Gunn1bf0e6b2016-11-30 15:19:13 -08003067 @VisibleForTesting
3068 public void onUserSwitch(UserHandle userHandle) {
Santos Cordonf0f99f32016-02-18 16:13:57 -08003069 mCurrentUserHandle = userHandle;
Tony Maka9930942016-01-15 10:57:14 +00003070 mMissedCallNotifier.setCurrentUserHandle(userHandle);
3071 final UserManager userManager = UserManager.get(mContext);
3072 List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier());
3073 for (UserInfo profile : profiles) {
3074 reloadMissedCallsOfUser(profile.getUserHandle());
3075 }
3076 }
3077
3078 /**
3079 * Because there may be chances that profiles are not started yet though its parent user is
3080 * switched, we reload missed calls of profile that are just started here.
3081 */
3082 void onUserStarting(UserHandle userHandle) {
3083 if (UserUtil.isProfile(mContext, userHandle)) {
3084 reloadMissedCallsOfUser(userHandle);
3085 }
3086 }
3087
Hall Liuc9cf5442016-06-29 10:08:10 -07003088 public TelecomSystem.SyncRoot getLock() {
3089 return mLock;
3090 }
3091
Tony Maka9930942016-01-15 10:57:14 +00003092 private void reloadMissedCallsOfUser(UserHandle userHandle) {
Hall Liu3037ac62016-09-22 14:40:03 -07003093 mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper,
3094 new MissedCallNotifier.CallInfoFactory(), userHandle);
3095 }
3096
3097 public void onBootCompleted() {
3098 mMissedCallNotifier.reloadAfterBootComplete(mCallerInfoLookupHelper,
3099 new MissedCallNotifier.CallInfoFactory());
Tony Maka9930942016-01-15 10:57:14 +00003100 }
3101
Tyler Gunna90ba732017-01-26 07:24:08 -08003102 public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
3103 return isIncomingCallPermitted(null /* excludeCall */, phoneAccountHandle);
3104 }
3105
3106 public boolean isIncomingCallPermitted(Call excludeCall,
3107 PhoneAccountHandle phoneAccountHandle) {
3108 if (phoneAccountHandle == null) {
3109 return false;
3110 }
3111 PhoneAccount phoneAccount =
3112 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle);
3113 if (phoneAccount == null) {
3114 return false;
3115 }
3116
3117 if (!phoneAccount.isSelfManaged()) {
3118 return !hasMaximumManagedRingingCalls(excludeCall) &&
3119 !hasMaximumManagedHoldingCalls(excludeCall);
3120 } else {
3121 return !hasEmergencyCall() &&
3122 !hasMaximumSelfManagedRingingCalls(excludeCall, phoneAccountHandle) &&
Tyler Gunn2b17f232017-03-08 08:51:00 -08003123 !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle);
Tyler Gunna90ba732017-01-26 07:24:08 -08003124 }
3125 }
3126
3127 public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
3128 return isOutgoingCallPermitted(null /* excludeCall */, phoneAccountHandle);
3129 }
3130
3131 public boolean isOutgoingCallPermitted(Call excludeCall,
3132 PhoneAccountHandle phoneAccountHandle) {
3133 if (phoneAccountHandle == null) {
3134 return false;
3135 }
3136 PhoneAccount phoneAccount =
3137 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle);
3138 if (phoneAccount == null) {
3139 return false;
3140 }
3141
3142 if (!phoneAccount.isSelfManaged()) {
3143 return !hasMaximumManagedOutgoingCalls(excludeCall) &&
3144 !hasMaximumManagedDialingCalls(excludeCall) &&
3145 !hasMaximumManagedLiveCalls(excludeCall) &&
3146 !hasMaximumManagedHoldingCalls(excludeCall);
3147 } else {
Pengquan Meng4832f202017-12-20 16:13:04 -08003148 // Only permit self-managed outgoing calls if
3149 // 1. there is no emergency ongoing call
3150 // 2. The outgoing call is an handover call or it not hit the self-managed call limit
3151 // and the current active call can be held.
3152 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
3153 return !hasEmergencyCall() &&
3154 ((excludeCall != null && excludeCall.getHandoverSourceCall() != null) ||
3155 (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) &&
3156 (activeCall == null || canHold(activeCall))));
Tyler Gunna90ba732017-01-26 07:24:08 -08003157 }
3158 }
3159
Hall Liu18c94492018-04-09 17:27:01 -07003160 public boolean isReplyWithSmsAllowed(int uid) {
3161 UserHandle callingUser = UserHandle.of(UserHandle.getUserId(uid));
3162 UserManager userManager = mContext.getSystemService(UserManager.class);
3163 KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
3164
3165 boolean isUserRestricted = userManager != null
3166 && userManager.hasUserRestriction(UserManager.DISALLOW_SMS, callingUser);
3167 boolean isLockscreenRestricted = keyguardManager != null
3168 && keyguardManager.isDeviceLocked();
3169 Log.d(this, "isReplyWithSmsAllowed: isUserRestricted: %s, isLockscreenRestricted: %s",
3170 isUserRestricted, isLockscreenRestricted);
3171
3172 // TODO(hallliu): actually check the lockscreen once b/77731473 is fixed
3173 return !isUserRestricted;
3174 }
Tony Maka9930942016-01-15 10:57:14 +00003175 /**
Tyler Gunn9e806ee2017-02-06 20:49:24 -08003176 * Blocks execution until all Telecom handlers have completed their current work.
3177 */
3178 public void waitOnHandlers() {
3179 CountDownLatch mainHandlerLatch = new CountDownLatch(3);
3180 mHandler.post(() -> {
3181 mainHandlerLatch.countDown();
3182 });
3183 mCallAudioManager.getCallAudioModeStateMachine().getHandler().post(() -> {
3184 mainHandlerLatch.countDown();
3185 });
3186 mCallAudioManager.getCallAudioRouteStateMachine().getHandler().post(() -> {
3187 mainHandlerLatch.countDown();
3188 });
3189
3190 try {
3191 mainHandlerLatch.await(HANDLER_WAIT_TIMEOUT, TimeUnit.MILLISECONDS);
3192 } catch (InterruptedException e) {
3193 Log.w(this, "waitOnHandlers: interrupted %s", e);
3194 }
3195 }
3196
3197 /**
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003198 * Used to confirm creation of an outgoing call which was marked as pending confirmation in
3199 * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)}.
3200 * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via
3201 * {@link ConfirmCallDialogActivity}.
3202 * @param callId The call ID of the call to confirm.
3203 */
3204 public void confirmPendingCall(String callId) {
3205 Log.i(this, "confirmPendingCall: callId=%s", callId);
3206 if (mPendingCall != null && mPendingCall.getId().equals(callId)) {
3207 Log.addEvent(mPendingCall, LogUtils.Events.USER_CONFIRMED);
3208 addCall(mPendingCall);
3209
3210 // We are going to place the new outgoing call, so disconnect any ongoing self-managed
3211 // calls which are ongoing at this time.
Tyler Gunnf4f05392018-03-26 18:57:59 +00003212 disconnectSelfManagedCalls("outgoing call " + callId);
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003213
3214 // Kick of the new outgoing call intent from where it left off prior to confirming the
3215 // call.
3216 CallIntentProcessor.sendNewOutgoingCallIntent(mContext, mPendingCall, this,
3217 mPendingCall.getOriginalCallIntent());
3218 mPendingCall = null;
3219 }
3220 }
3221
3222 /**
3223 * Used to cancel an outgoing call which was marked as pending confirmation in
3224 * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)}.
3225 * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via
3226 * {@link ConfirmCallDialogActivity}.
3227 * @param callId The call ID of the call to cancel.
3228 */
3229 public void cancelPendingCall(String callId) {
3230 Log.i(this, "cancelPendingCall: callId=%s", callId);
3231 if (mPendingCall != null && mPendingCall.getId().equals(callId)) {
3232 Log.addEvent(mPendingCall, LogUtils.Events.USER_CANCELLED);
3233 markCallAsDisconnected(mPendingCall, new DisconnectCause(DisconnectCause.CANCELED));
3234 markCallAsRemoved(mPendingCall);
3235 mPendingCall = null;
3236 }
3237 }
3238
3239 /**
3240 * Called from {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)} when
3241 * a managed call is added while there are ongoing self-managed calls. Starts
3242 * {@link ConfirmCallDialogActivity} to prompt the user to see if they wish to place the
3243 * outgoing call or not.
3244 * @param call The call to confirm.
3245 */
3246 private void startCallConfirmation(Call call) {
3247 if (mPendingCall != null) {
3248 Log.i(this, "startCallConfirmation: call %s is already pending; disconnecting %s",
3249 mPendingCall.getId(), call.getId());
3250 markCallDisconnectedDueToSelfManagedCall(call);
3251 return;
3252 }
3253 Log.addEvent(call, LogUtils.Events.USER_CONFIRMATION);
3254 mPendingCall = call;
3255
3256 // Figure out the name of the app in charge of the self-managed call(s).
Pengquan Meng4832f202017-12-20 16:13:04 -08003257 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
3258 if (activeCall != null) {
3259 CharSequence ongoingAppName = activeCall.getTargetPhoneAccountLabel();
3260 Log.i(this, "startCallConfirmation: callId=%s, ongoingApp=%s", call.getId(),
3261 ongoingAppName);
Pengquan Mengfc9c8ff2018-01-17 05:50:49 +00003262
Pengquan Meng4832f202017-12-20 16:13:04 -08003263 Intent confirmIntent = new Intent(mContext, ConfirmCallDialogActivity.class);
3264 confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID, call.getId());
3265 confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_ONGOING_APP_NAME, ongoingAppName);
3266 confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3267 mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
3268 }
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003269 }
3270
3271 /**
3272 * Disconnects all self-managed calls.
3273 */
Tyler Gunnf4f05392018-03-26 18:57:59 +00003274 private void disconnectSelfManagedCalls(String reason) {
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003275 // Disconnect all self-managed calls to make priority for emergency call.
3276 // Use Call.disconnect() to command the ConnectionService to disconnect the calls.
3277 // CallsManager.markCallAsDisconnected doesn't actually tell the ConnectionService to
3278 // disconnect.
3279 mCalls.stream()
3280 .filter(c -> c.isSelfManaged())
Tyler Gunnf4f05392018-03-26 18:57:59 +00003281 .forEach(c -> c.disconnect(reason));
Tyler Gunn911d4de2017-12-19 08:11:35 -08003282
3283 // When disconnecting all self-managed calls, switch audio routing back to the baseline
3284 // route. This ensures if, for example, the self-managed ConnectionService was routed to
3285 // speakerphone that we'll switch back to earpiece for the managed call which necessitated
3286 // disconnecting the self-managed calls.
3287 mCallAudioManager.switchBaseline();
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003288 }
3289
3290 /**
Tyler Gunn91d43cf2014-09-17 12:19:39 -07003291 * Dumps the state of the {@link CallsManager}.
3292 *
3293 * @param pw The {@code IndentingPrintWriter} to write the state to.
3294 */
3295 public void dump(IndentingPrintWriter pw) {
3296 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
Tyler Gunn91d43cf2014-09-17 12:19:39 -07003297 if (mCalls != null) {
3298 pw.println("mCalls: ");
3299 pw.increaseIndent();
3300 for (Call call : mCalls) {
3301 pw.println(call);
3302 }
3303 pw.decreaseIndent();
3304 }
Tyler Gunn9787e0e2014-10-14 14:36:12 -07003305
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003306 if (mPendingCall != null) {
3307 pw.print("mPendingCall:");
3308 pw.println(mPendingCall.getId());
3309 }
3310
Tyler Gunn9787e0e2014-10-14 14:36:12 -07003311 if (mCallAudioManager != null) {
3312 pw.println("mCallAudioManager:");
3313 pw.increaseIndent();
3314 mCallAudioManager.dump(pw);
3315 pw.decreaseIndent();
3316 }
3317
3318 if (mTtyManager != null) {
3319 pw.println("mTtyManager:");
3320 pw.increaseIndent();
3321 mTtyManager.dump(pw);
3322 pw.decreaseIndent();
3323 }
3324
3325 if (mInCallController != null) {
3326 pw.println("mInCallController:");
3327 pw.increaseIndent();
3328 mInCallController.dump(pw);
3329 pw.decreaseIndent();
3330 }
3331
Hall Liu7c928322016-12-06 18:15:39 -08003332 if (mDefaultDialerCache != null) {
3333 pw.println("mDefaultDialerCache:");
3334 pw.increaseIndent();
3335 mDefaultDialerCache.dumpCache(pw);
3336 pw.decreaseIndent();
3337 }
3338
Tyler Gunn9787e0e2014-10-14 14:36:12 -07003339 if (mConnectionServiceRepository != null) {
3340 pw.println("mConnectionServiceRepository:");
3341 pw.increaseIndent();
3342 mConnectionServiceRepository.dump(pw);
3343 pw.decreaseIndent();
3344 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -07003345 }
Xueren Zhangc0e8e9d2015-03-06 14:16:55 +01003346
3347 /**
3348 * For some disconnected causes, we show a dialog when it's a mmi code or potential mmi code.
3349 *
3350 * @param call The call.
3351 */
3352 private void maybeShowErrorDialogOnDisconnect(Call call) {
3353 if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle())
Srikanth Chintalaba018452017-07-03 11:39:43 +05303354 || isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(call)) {
Xueren Zhangc0e8e9d2015-03-06 14:16:55 +01003355 DisconnectCause disconnectCause = call.getDisconnectCause();
3356 if (!TextUtils.isEmpty(disconnectCause.getDescription()) && (disconnectCause.getCode()
3357 == DisconnectCause.ERROR)) {
3358 Intent errorIntent = new Intent(mContext, ErrorDialogActivity.class);
3359 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_STRING_EXTRA,
3360 disconnectCause.getDescription());
3361 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3362 mContext.startActivityAsUser(errorIntent, UserHandle.CURRENT);
3363 }
3364 }
3365 }
Sailesh Nepal703a1af2016-04-14 20:10:12 -07003366
3367 private void setIntentExtrasAndStartTime(Call call, Bundle extras) {
Tyler Gunn7c031f22018-01-18 15:00:41 -08003368 if (extras != null) {
3369 // Create our own instance to modify (since extras may be Bundle.EMPTY)
3370 extras = new Bundle(extras);
3371 } else {
3372 extras = new Bundle();
3373 }
Sailesh Nepal703a1af2016-04-14 20:10:12 -07003374
Tyler Gunn7c031f22018-01-18 15:00:41 -08003375 // Specifies the time telecom began routing the call. This is used by the dialer for
3376 // analytics.
3377 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS,
Sailesh Nepal703a1af2016-04-14 20:10:12 -07003378 SystemClock.elapsedRealtime());
3379
Tyler Gunn7c031f22018-01-18 15:00:41 -08003380 call.setIntentExtras(extras);
Sailesh Nepal703a1af2016-04-14 20:10:12 -07003381 }
Tyler Gunna90ba732017-01-26 07:24:08 -08003382
3383 /**
3384 * Notifies the {@link android.telecom.ConnectionService} associated with a
3385 * {@link PhoneAccountHandle} that the attempt to create a new connection has failed.
3386 *
3387 * @param phoneAccountHandle The {@link PhoneAccountHandle}.
3388 * @param call The {@link Call} which could not be added.
3389 */
3390 private void notifyCreateConnectionFailed(PhoneAccountHandle phoneAccountHandle, Call call) {
3391 if (phoneAccountHandle == null) {
3392 return;
3393 }
3394 ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
3395 phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle());
3396 if (service == null) {
3397 Log.i(this, "Found no connection service.");
3398 return;
3399 } else {
3400 call.setConnectionService(service);
3401 service.createConnectionFailed(call);
3402 }
3403 }
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003404
3405 /**
Sanket Padawe29411c92017-11-30 16:51:08 -08003406 * Notifies the {@link android.telecom.ConnectionService} associated with a
3407 * {@link PhoneAccountHandle} that the attempt to handover a call has failed.
3408 *
3409 * @param call The handover call
3410 * @param reason The error reason code for handover failure
3411 */
3412 private void notifyHandoverFailed(Call call, int reason) {
3413 ConnectionServiceWrapper service = call.getConnectionService();
3414 service.handoverFailed(call, reason);
3415 call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
Tyler Gunnf4f05392018-03-26 18:57:59 +00003416 call.disconnect("handover failed");
Sanket Padawe29411c92017-11-30 16:51:08 -08003417 }
3418
3419 /**
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003420 * Called in response to a {@link Call} receiving a {@link Call#sendCallEvent(String, Bundle)}
3421 * of type {@link android.telecom.Call#EVENT_REQUEST_HANDOVER} indicating the
3422 * {@link android.telecom.InCallService} has requested a handover to another
3423 * {@link android.telecom.ConnectionService}.
3424 *
3425 * We will explicitly disallow a handover when there is an emergency call present.
3426 *
3427 * @param handoverFromCall The {@link Call} to be handed over.
3428 * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to.
3429 * @param videoState The desired video state of {@link Call} after handover.
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003430 * @param initiatingExtras Extras associated with the handover, to be passed to the handover
3431 * {@link android.telecom.ConnectionService}.
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003432 */
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003433 private void requestHandoverViaEvents(Call handoverFromCall,
3434 PhoneAccountHandle handoverToHandle,
3435 int videoState, Bundle initiatingExtras) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003436
3437 boolean isHandoverFromSupported = isHandoverFromPhoneAccountSupported(
3438 handoverFromCall.getTargetPhoneAccount());
3439 boolean isHandoverToSupported = isHandoverToPhoneAccountSupported(handoverToHandle);
3440
3441 if (!isHandoverFromSupported || !isHandoverToSupported || hasEmergencyCall()) {
3442 handoverFromCall.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null);
3443 return;
3444 }
3445
3446 Log.addEvent(handoverFromCall, LogUtils.Events.HANDOVER_REQUEST, handoverToHandle);
3447
3448 Bundle extras = new Bundle();
3449 extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
Tyler Gunn141ef582017-05-26 13:38:13 -07003450 extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
3451 handoverFromCall.getTargetPhoneAccount());
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003452 extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003453 if (initiatingExtras != null) {
3454 extras.putAll(initiatingExtras);
3455 }
3456 extras.putParcelable(TelecomManager.EXTRA_CALL_AUDIO_STATE,
3457 mCallAudioManager.getCallAudioState());
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003458 Call handoverToCall = startOutgoingCall(handoverFromCall.getHandle(), handoverToHandle,
Tyler Gunnbbd78a72017-04-30 14:16:07 -07003459 extras, getCurrentUserHandle(), null /* originalIntent */);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003460 Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER,
3461 "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId());
Tyler Gunn141ef582017-05-26 13:38:13 -07003462 handoverFromCall.setHandoverDestinationCall(handoverToCall);
3463 handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
3464 handoverToCall.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
3465 handoverToCall.setHandoverSourceCall(handoverFromCall);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003466 handoverToCall.setNewOutgoingCallIntentBroadcastIsDone();
3467 placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */,
3468 false /* startwithSpeaker */,
3469 videoState);
3470 }
3471
3472 /**
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003473 * Called in response to a {@link Call} receiving a {@link Call#handoverTo(PhoneAccountHandle,
3474 * int, Bundle)} indicating the {@link android.telecom.InCallService} has requested a
3475 * handover to another {@link android.telecom.ConnectionService}.
3476 *
3477 * We will explicitly disallow a handover when there is an emergency call present.
3478 *
3479 * @param handoverFromCall The {@link Call} to be handed over.
3480 * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to.
3481 * @param videoState The desired video state of {@link Call} after handover.
Tyler Gunn7c031f22018-01-18 15:00:41 -08003482 * @param extras Extras associated with the handover, to be passed to the handover
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003483 * {@link android.telecom.ConnectionService}.
3484 */
3485 private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle,
3486 int videoState, Bundle extras) {
3487
3488 // Send an error back if there are any ongoing emergency calls.
3489 if (hasEmergencyCall()) {
3490 handoverFromCall.onHandoverFailed(
Tyler Gunn8c4e6212018-03-19 20:07:57 +00003491 android.telecom.Call.Callback.HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL);
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003492 return;
3493 }
3494
3495 // If source and destination phone accounts don't support handover, send an error back.
3496 boolean isHandoverFromSupported = isHandoverFromPhoneAccountSupported(
3497 handoverFromCall.getTargetPhoneAccount());
3498 boolean isHandoverToSupported = isHandoverToPhoneAccountSupported(handoverToHandle);
3499 if (!isHandoverFromSupported || !isHandoverToSupported) {
3500 handoverFromCall.onHandoverFailed(
Tyler Gunn97f30dc2018-03-07 21:09:58 +00003501 android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED);
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003502 return;
3503 }
3504
3505 Log.addEvent(handoverFromCall, LogUtils.Events.HANDOVER_REQUEST, handoverToHandle);
3506
3507 // Create a new instance of Call
3508 PhoneAccount account =
3509 mPhoneAccountRegistrar.getPhoneAccount(handoverToHandle, getCurrentUserHandle());
3510 boolean isSelfManaged = account != null && account.isSelfManaged();
3511
3512 Call call = new Call(getNextCallId(), mContext,
3513 this, mLock, mConnectionServiceRepository,
3514 mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, mPhoneNumberUtilsAdapter,
3515 handoverFromCall.getHandle(), null,
3516 null, null,
3517 Call.CALL_DIRECTION_OUTGOING, false,
3518 false, mClockProxy);
3519 call.initAnalytics();
3520
3521 // Set self-managed and voipAudioMode if destination is self-managed CS
3522 call.setIsSelfManaged(isSelfManaged);
3523 if (isSelfManaged) {
3524 call.setIsVoipAudioMode(true);
3525 }
3526 call.setInitiatingUser(getCurrentUserHandle());
3527
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003528 // Ensure we don't try to place an outgoing call with video if video is not
3529 // supported.
3530 if (VideoProfile.isVideo(videoState) && account != null &&
3531 !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
3532 call.setVideoState(VideoProfile.STATE_AUDIO_ONLY);
3533 } else {
3534 call.setVideoState(videoState);
3535 }
3536
3537 // Set target phone account to destAcct.
3538 call.setTargetPhoneAccount(handoverToHandle);
3539
3540 if (account != null && account.getExtras() != null && account.getExtras()
3541 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
3542 Log.d(this, "requestHandover: defaulting to voip mode for call %s",
3543 call.getId());
3544 call.setIsVoipAudioMode(true);
3545 }
3546
3547 // Set call state to connecting
3548 call.setState(
3549 CallState.CONNECTING,
3550 handoverToHandle == null ? "no-handle" : handoverToHandle.toString());
Tyler Gunn7c031f22018-01-18 15:00:41 -08003551
3552 // Mark as handover so that the ConnectionService knows this is a handover request.
3553 if (extras == null) {
3554 extras = new Bundle();
3555 }
3556 extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true);
3557 extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
3558 handoverFromCall.getTargetPhoneAccount());
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003559 setIntentExtrasAndStartTime(call, extras);
3560
3561 // Add call to call tracker
3562 if (!mCalls.contains(call)) {
3563 addCall(call);
3564 }
3565
3566 Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER,
3567 "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), call.getId());
3568
3569 handoverFromCall.setHandoverDestinationCall(call);
3570 handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
3571 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
3572 call.setHandoverSourceCall(handoverFromCall);
3573 call.setNewOutgoingCallIntentBroadcastIsDone();
3574
3575 // Auto-enable speakerphone if the originating intent specified to do so, if the call
3576 // is a video call, of if using speaker when docked
3577 final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
3578 R.bool.use_speaker_when_docked);
3579 final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
3580 final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);
3581 call.setStartWithSpeakerphoneOn(false || useSpeakerForVideoCall
3582 || (useSpeakerWhenDocked && useSpeakerForDock));
3583 call.setVideoState(videoState);
3584
3585 final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
3586 call.getTargetPhoneAccount());
3587
3588 // If the account has been set, proceed to place the outgoing call.
3589 if (call.isSelfManaged() && !isOutgoingCallPermitted) {
3590 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
3591 } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) {
3592 markCallDisconnectedDueToSelfManagedCall(call);
3593 } else {
3594 if (call.isEmergencyCall()) {
3595 // Disconnect all self-managed calls to make priority for emergency call.
Tyler Gunnf4f05392018-03-26 18:57:59 +00003596 disconnectSelfManagedCalls("emergency call");
Sanket Padawe4ebc3ca2017-11-21 12:49:43 -08003597 }
3598
3599 call.startCreateConnection(mPhoneAccountRegistrar);
3600 }
3601
3602 }
3603
3604 /**
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003605 * Determines if handover from the specified {@link PhoneAccountHandle} is supported.
3606 *
3607 * @param from The {@link PhoneAccountHandle} the handover originates from.
3608 * @return {@code true} if handover is currently allowed, {@code false} otherwise.
3609 */
3610 private boolean isHandoverFromPhoneAccountSupported(PhoneAccountHandle from) {
Tyler Gunn6f6f1c52017-04-17 18:22:04 -07003611 return getBooleanPhoneAccountExtra(from, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM);
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003612 }
3613
3614 /**
3615 * Determines if handover to the specified {@link PhoneAccountHandle} is supported.
3616 *
3617 * @param to The {@link PhoneAccountHandle} the handover it to.
3618 * @return {@code true} if handover is currently allowed, {@code false} otherwise.
3619 */
3620 private boolean isHandoverToPhoneAccountSupported(PhoneAccountHandle to) {
3621 return getBooleanPhoneAccountExtra(to, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO);
3622 }
3623
3624 /**
3625 * Retrieves a boolean phone account extra.
3626 * @param handle the {@link PhoneAccountHandle} to retrieve the extra for.
3627 * @param key The extras key.
3628 * @return {@code true} if the extra {@link PhoneAccount} extra is true, {@code false}
3629 * otherwise.
3630 */
3631 private boolean getBooleanPhoneAccountExtra(PhoneAccountHandle handle, String key) {
3632 PhoneAccount phoneAccount = getPhoneAccountRegistrar().getPhoneAccountUnchecked(handle);
3633 if (phoneAccount == null) {
3634 return false;
3635 }
3636
3637 Bundle fromExtras = phoneAccount.getExtras();
3638 if (fromExtras == null) {
3639 return false;
3640 }
3641 return fromExtras.getBoolean(key);
3642 }
3643
3644 /**
3645 * Determines if there is an existing handover in process.
3646 * @return {@code true} if a call in the process of handover exists, {@code false} otherwise.
3647 */
3648 private boolean isHandoverInProgress() {
Tyler Gunn141ef582017-05-26 13:38:13 -07003649 return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null ||
3650 c.getHandoverDestinationCall() != null).count() > 0;
Tyler Gunn6f9ceb22017-04-06 08:47:01 -07003651 }
Tyler Gunnea4c6fb2017-04-14 13:46:21 -07003652
3653 private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) {
3654 Intent intent =
3655 new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED);
3656 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
3657 intent.putExtra(
3658 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
3659 Log.i(this, "Sending phone-account %s unregistered intent as user", accountHandle);
3660 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
3661 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
3662
3663 String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication(
3664 getCurrentUserHandle().getIdentifier());
3665 if (!TextUtils.isEmpty(dialerPackage)) {
3666 Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED)
3667 .setPackage(dialerPackage);
3668 directedIntent.putExtra(
3669 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
3670 Log.i(this, "Sending phone-account unregistered intent to default dialer");
3671 mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null);
3672 }
3673 return ;
3674 }
3675
3676 private void broadcastRegisterIntent(PhoneAccountHandle accountHandle) {
3677 Intent intent = new Intent(
3678 TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED);
3679 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
3680 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
3681 accountHandle);
3682 Log.i(this, "Sending phone-account %s registered intent as user", accountHandle);
3683 mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
3684 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
3685
3686 String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication(
3687 getCurrentUserHandle().getIdentifier());
3688 if (!TextUtils.isEmpty(dialerPackage)) {
3689 Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED)
3690 .setPackage(dialerPackage);
3691 directedIntent.putExtra(
3692 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
3693 Log.i(this, "Sending phone-account registered intent to default dialer");
3694 mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null);
3695 }
3696 return ;
3697 }
Sanket Padaweccdf3642017-11-10 14:49:05 -08003698
3699 public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) {
Sanket Padawe29411c92017-11-30 16:51:08 -08003700
3701 final String handleScheme = srcAddr.getSchemeSpecificPart();
3702 Call fromCall = mCalls.stream()
3703 .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber(
3704 c.getHandle().getSchemeSpecificPart(), handleScheme))
3705 .findFirst()
3706 .orElse(null);
3707
3708 Call call = new Call(
3709 getNextCallId(),
3710 mContext,
3711 this,
3712 mLock,
3713 mConnectionServiceRepository,
3714 mContactsAsyncHelper,
3715 mCallerInfoAsyncQueryFactory,
3716 mPhoneNumberUtilsAdapter,
3717 srcAddr,
3718 null /* gatewayInfo */,
3719 null /* connectionManagerPhoneAccount */,
3720 destAcct,
3721 Call.CALL_DIRECTION_INCOMING /* callDirection */,
3722 false /* forceAttachToExistingConnection */,
3723 false, /* isConference */
3724 mClockProxy);
3725
3726 if (fromCall == null || isHandoverInProgress() ||
3727 !isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount()) ||
3728 !isHandoverToPhoneAccountSupported(destAcct) ||
3729 hasEmergencyCall()) {
3730 Log.w(this, "acceptHandover: Handover not supported");
3731 notifyHandoverFailed(call,
Tyler Gunn97f30dc2018-03-07 21:09:58 +00003732 android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED);
Sanket Padawe29411c92017-11-30 16:51:08 -08003733 return;
3734 }
3735
3736 PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(destAcct);
3737 if (phoneAccount == null) {
3738 Log.w(this, "acceptHandover: Handover not supported. phoneAccount = null");
3739 notifyHandoverFailed(call,
Tyler Gunn97f30dc2018-03-07 21:09:58 +00003740 android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED);
Sanket Padawe29411c92017-11-30 16:51:08 -08003741 return;
3742 }
3743 call.setIsSelfManaged(phoneAccount.isSelfManaged());
3744 if (call.isSelfManaged() || (phoneAccount.getExtras() != null &&
3745 phoneAccount.getExtras().getBoolean(
3746 PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE))) {
3747 call.setIsVoipAudioMode(true);
3748 }
3749 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
3750 call.setVideoState(VideoProfile.STATE_AUDIO_ONLY);
3751 } else {
3752 call.setVideoState(videoState);
3753 }
3754
3755 call.initAnalytics();
3756 call.addListener(this);
3757
3758 fromCall.setHandoverDestinationCall(call);
3759 call.setHandoverSourceCall(fromCall);
3760 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
3761 fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
3762
3763 if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) {
3764 // Ensure when the call goes active that it will go to speakerphone if the
3765 // handover to call is a video call.
3766 call.setStartWithSpeakerphoneOn(true);
3767 }
3768
Tyler Gunn7c031f22018-01-18 15:00:41 -08003769 Bundle extras = call.getIntentExtras();
3770 if (extras == null) {
3771 extras = new Bundle();
3772 }
3773 extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true);
3774 extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
3775 fromCall.getTargetPhoneAccount());
3776
Sanket Padawe29411c92017-11-30 16:51:08 -08003777 call.startCreateConnection(mPhoneAccountRegistrar);
Sanket Padaweccdf3642017-11-10 14:49:05 -08003778 }
Pengquan Meng4832f202017-12-20 16:13:04 -08003779
3780 ConnectionServiceFocusManager getConnectionServiceFocusManager() {
3781 return mConnectionSvrFocusMgr;
3782 }
3783
3784 private boolean canHold(Call call) {
3785 return call.can(Connection.CAPABILITY_HOLD);
3786 }
3787
Tyler Gunn60f230e2018-03-16 15:11:27 -07003788 private boolean supportsHold(Call call) {
3789 return call.can(Connection.CAPABILITY_SUPPORT_HOLD);
3790 }
3791
Pengquan Meng4832f202017-12-20 16:13:04 -08003792 private final class ActionSetCallState implements PendingAction {
3793
3794 private final Call mCall;
3795 private final int mState;
3796 private final String mTag;
3797
3798 ActionSetCallState(Call call, int state, String tag) {
3799 mCall = call;
3800 mState = state;
3801 mTag = tag;
3802 }
3803
3804 @Override
3805 public void performAction() {
3806 Log.d(this, "perform set call state for %s, state = %s", mCall, mState);
3807 setCallState(mCall, mState, mTag);
3808 }
3809 }
3810
3811 private final class ActionUnHoldCall implements PendingAction {
3812 private final Call mCall;
Tyler Gunnf4f05392018-03-26 18:57:59 +00003813 private final String mPreviouslyHeldCallId;
Pengquan Meng4832f202017-12-20 16:13:04 -08003814
Tyler Gunnf4f05392018-03-26 18:57:59 +00003815 ActionUnHoldCall(Call call, String previouslyHeldCallId) {
Pengquan Meng4832f202017-12-20 16:13:04 -08003816 mCall = call;
Tyler Gunnf4f05392018-03-26 18:57:59 +00003817 mPreviouslyHeldCallId = previouslyHeldCallId;
Pengquan Meng4832f202017-12-20 16:13:04 -08003818 }
3819
3820 @Override
3821 public void performAction() {
3822 Log.d(this, "perform unhold call for %s", mCall);
Tyler Gunnf4f05392018-03-26 18:57:59 +00003823 mCall.unhold("held " + mPreviouslyHeldCallId);
Pengquan Meng4832f202017-12-20 16:13:04 -08003824 }
3825 }
3826
3827 private final class ActionAnswerCall implements PendingAction {
3828 private final Call mCall;
3829 private final int mVideoState;
3830
3831 ActionAnswerCall(Call call, int videoState) {
3832 mCall = call;
3833 mVideoState = videoState;
3834 }
3835
3836 @Override
3837 public void performAction() {
3838 Log.d(this, "perform answer call for %s, videoState = %d", mCall, mVideoState);
3839 for (CallsManagerListener listener : mListeners) {
3840 listener.onIncomingCallAnswered(mCall);
3841 }
3842
3843 // We do not update the UI until we get confirmation of the answer() through
3844 // {@link #markCallAsActive}.
3845 mCall.answer(mVideoState);
3846 if (isSpeakerphoneAutoEnabledForVideoCalls(mVideoState)) {
3847 mCall.setStartWithSpeakerphoneOn(true);
3848 }
3849 }
3850 }
3851
3852 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
3853 public static final class RequestCallback implements
3854 ConnectionServiceFocusManager.RequestFocusCallback {
3855 private PendingAction mPendingAction;
3856
3857 RequestCallback(PendingAction pendingAction) {
3858 mPendingAction = pendingAction;
3859 }
3860
3861 @Override
3862 public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) {
3863 if (mPendingAction != null) {
3864 mPendingAction.performAction();
3865 }
3866 }
3867 }
Ben Gilad9f2bed32013-12-12 17:43:26 -08003868}