Santos Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 1 | /* |
| 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 Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 17 | package com.android.server.telecom; |
Ben Gilad | 9f2bed3 | 2013-12-12 17:43:26 -0800 | [diff] [blame] | 18 | |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 19 | import android.app.ActivityManager; |
Hall Liu | 18c9449 | 2018-04-09 17:27:01 -0700 | [diff] [blame^] | 20 | import android.app.KeyguardManager; |
Tyler Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 21 | import android.content.BroadcastReceiver; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 22 | import android.content.Context; |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 23 | import android.content.pm.UserInfo; |
Xueren Zhang | c0e8e9d | 2015-03-06 14:16:55 +0100 | [diff] [blame] | 24 | import android.content.Intent; |
Tyler Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 25 | import android.content.IntentFilter; |
Hall Liu | fbed016 | 2016-01-14 18:06:29 -0800 | [diff] [blame] | 26 | import android.media.AudioManager; |
Sailesh Nepal | ce704b9 | 2014-03-17 18:31:43 -0700 | [diff] [blame] | 27 | import android.net.Uri; |
Evan Charlton | a05805b | 2014-03-05 08:21:46 -0800 | [diff] [blame] | 28 | import android.os.Bundle; |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 29 | import android.os.Handler; |
Ihab Awad | e6dbc9d | 2015-03-26 10:33:44 -0700 | [diff] [blame] | 30 | import android.os.Looper; |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 31 | import android.os.Process; |
Sailesh Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 32 | import android.os.SystemClock; |
Rekha Kumar | d240fe8 | 2015-04-01 21:45:57 -0700 | [diff] [blame] | 33 | import android.os.SystemProperties; |
Brad Ebinger | d931a01 | 2015-10-21 12:54:08 -0700 | [diff] [blame] | 34 | import android.os.SystemVibrator; |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 35 | import android.os.Trace; |
Tony Mak | 578a4e6 | 2015-11-23 11:18:51 +0000 | [diff] [blame] | 36 | import android.os.UserHandle; |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 37 | import android.os.UserManager; |
Tyler Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 38 | import android.provider.BlockedNumberContract.SystemContract; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 39 | import android.provider.CallLog.Calls; |
Hall Liu | f1422e7 | 2016-01-27 11:07:07 -0800 | [diff] [blame] | 40 | import android.provider.Settings; |
Yorke Lee | 2a66f7b | 2015-05-13 14:21:19 -0700 | [diff] [blame] | 41 | import android.telecom.CallAudioState; |
Tyler Gunn | d92e737 | 2015-01-09 15:59:45 -0800 | [diff] [blame] | 42 | import android.telecom.Conference; |
Ihab Awad | 07bc5ee | 2014-11-12 13:42:52 -0800 | [diff] [blame] | 43 | import android.telecom.Connection; |
Andrew Lee | 701dc00 | 2014-09-11 21:29:12 -0700 | [diff] [blame] | 44 | import android.telecom.DisconnectCause; |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 45 | import android.telecom.GatewayInfo; |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 46 | import android.telecom.Log; |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 47 | import android.telecom.ParcelableConference; |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 48 | import android.telecom.ParcelableConnection; |
Tyler Gunn | 5b452df | 2014-10-01 15:02:58 -0700 | [diff] [blame] | 49 | import android.telecom.PhoneAccount; |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 50 | import android.telecom.PhoneAccountHandle; |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 51 | import android.telecom.Logging.Runnable; |
Tyler Gunn | 5b452df | 2014-10-01 15:02:58 -0700 | [diff] [blame] | 52 | import android.telecom.TelecomManager; |
Andrew Lee | 4550623 | 2014-10-22 17:54:33 -0700 | [diff] [blame] | 53 | import android.telecom.VideoProfile; |
Tyler Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 54 | import android.telephony.CarrierConfigManager; |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 55 | import android.telephony.PhoneNumberUtils; |
Sailesh Nepal | 8aa6a00 | 2014-09-12 10:52:49 -0700 | [diff] [blame] | 56 | import android.telephony.TelephonyManager; |
Yorke Lee | 8d3f269 | 2015-05-08 11:44:39 -0700 | [diff] [blame] | 57 | import android.text.TextUtils; |
Santos Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 58 | |
Ihab Awad | a365390 | 2015-01-23 13:44:46 -0800 | [diff] [blame] | 59 | import com.android.internal.annotations.VisibleForTesting; |
Abhijith Shastry | cf27f68 | 2016-03-01 18:28:44 -0800 | [diff] [blame] | 60 | import com.android.internal.telephony.AsyncEmergencyContactNotifier; |
Rekha Kumar | d240fe8 | 2015-04-01 21:45:57 -0700 | [diff] [blame] | 61 | import com.android.internal.telephony.PhoneConstants; |
| 62 | import com.android.internal.telephony.TelephonyProperties; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 63 | import com.android.internal.util.IndentingPrintWriter; |
Hall Liu | 486cb19 | 2016-10-21 18:23:33 -0700 | [diff] [blame] | 64 | import com.android.server.telecom.bluetooth.BluetoothRouteManager; |
Hall Liu | 784a496 | 2018-03-06 11:03:17 -0800 | [diff] [blame] | 65 | import com.android.server.telecom.bluetooth.BluetoothStateReceiver; |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 66 | import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter; |
| 67 | import com.android.server.telecom.callfiltering.BlockCheckerAdapter; |
| 68 | import com.android.server.telecom.callfiltering.CallFilterResultCallback; |
| 69 | import com.android.server.telecom.callfiltering.CallFilteringResult; |
| 70 | import com.android.server.telecom.callfiltering.CallScreeningServiceFilter; |
| 71 | import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter; |
| 72 | import com.android.server.telecom.callfiltering.IncomingCallFilter; |
Xueren Zhang | c0e8e9d | 2015-03-06 14:16:55 +0100 | [diff] [blame] | 73 | import com.android.server.telecom.components.ErrorDialogActivity; |
Tyler Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 74 | import com.android.server.telecom.settings.BlockedNumbersUtil; |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 75 | import com.android.server.telecom.ui.ConfirmCallDialogActivity; |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 76 | import com.android.server.telecom.ui.IncomingCallNotifier; |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 77 | |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 78 | import java.util.ArrayList; |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 79 | import java.util.Arrays; |
Santos Cordon | 4bc0245 | 2014-11-19 15:51:12 -0800 | [diff] [blame] | 80 | import java.util.Collection; |
Jay Shrauner | a82c8f7 | 2014-08-14 15:49:16 -0700 | [diff] [blame] | 81 | import java.util.Collections; |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 82 | import java.util.HashMap; |
Ihab Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 83 | import java.util.HashSet; |
Brad Ebinger | e680e66 | 2016-08-26 11:45:43 -0700 | [diff] [blame] | 84 | import java.util.Iterator; |
Santos Cordon | 7cdb909 | 2014-07-24 21:33:33 -0700 | [diff] [blame] | 85 | import java.util.List; |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 86 | import java.util.Map; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 87 | import java.util.Objects; |
Tyler Gunn | 9b618b8 | 2016-10-17 15:54:35 -0700 | [diff] [blame] | 88 | import java.util.Optional; |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 89 | import java.util.Set; |
Jay Shrauner | a82c8f7 | 2014-08-14 15:49:16 -0700 | [diff] [blame] | 90 | import java.util.concurrent.ConcurrentHashMap; |
Tyler Gunn | 9e806ee | 2017-02-06 20:49:24 -0800 | [diff] [blame] | 91 | import java.util.concurrent.CountDownLatch; |
| 92 | import java.util.concurrent.TimeUnit; |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 93 | import java.util.stream.Collectors; |
| 94 | import java.util.stream.IntStream; |
| 95 | import java.util.stream.Stream; |
Ben Gilad | 9f2bed3 | 2013-12-12 17:43:26 -0800 | [diff] [blame] | 96 | |
Ben Gilad | dd8c608 | 2013-12-30 14:44:08 -0800 | [diff] [blame] | 97 | /** |
| 98 | * Singleton. |
| 99 | * |
Jay Shrauner | acb91eb | 2014-08-08 16:04:53 -0700 | [diff] [blame] | 100 | * NOTE: by design most APIs are package private, use the relevant adapter/s to allow |
Ben Gilad | dd8c608 | 2013-12-30 14:44:08 -0800 | [diff] [blame] | 101 | * access from other packages specifically refraining from passing the CallsManager instance |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 102 | * beyond the com.android.server.telecom package boundary. |
Ben Gilad | dd8c608 | 2013-12-30 14:44:08 -0800 | [diff] [blame] | 103 | */ |
Ihab Awad | aa383cc | 2015-03-22 22:12:28 -0700 | [diff] [blame] | 104 | @VisibleForTesting |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 105 | public class CallsManager extends Call.ListenerBase |
Tyler Gunn | 1bf0e6b | 2016-11-30 15:19:13 -0800 | [diff] [blame] | 106 | implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy { |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 107 | |
Santos Cordon | df39986 | 2014-08-06 04:39:15 -0700 | [diff] [blame] | 108 | // TODO: Consider renaming this CallsManagerPlugin. |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 109 | @VisibleForTesting |
| 110 | public interface CallsManagerListener { |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 111 | void onCallAdded(Call call); |
| 112 | void onCallRemoved(Call call); |
Ihab Awad | 6fb37c8 | 2014-08-07 19:48:57 -0700 | [diff] [blame] | 113 | void onCallStateChanged(Call call, int oldState, int newState); |
Sailesh Nepal | c92c436 | 2014-07-04 18:33:21 -0700 | [diff] [blame] | 114 | void onConnectionServiceChanged( |
Sailesh Nepal | 8c85dee | 2014-04-07 22:21:40 -0700 | [diff] [blame] | 115 | Call call, |
Sailesh Nepal | c92c436 | 2014-07-04 18:33:21 -0700 | [diff] [blame] | 116 | ConnectionServiceWrapper oldService, |
| 117 | ConnectionServiceWrapper newService); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 118 | void onIncomingCallAnswered(Call call); |
Ihab Awad | ff7493a | 2014-06-10 13:47:44 -0700 | [diff] [blame] | 119 | void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage); |
Yorke Lee | 2a66f7b | 2015-05-13 14:21:19 -0700 | [diff] [blame] | 120 | void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState); |
Andrew Lee | 5be64bc | 2014-09-08 18:35:15 -0700 | [diff] [blame] | 121 | void onRingbackRequested(Call call, boolean ringback); |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 122 | void onIsConferencedChanged(Call call); |
Andrew Lee | 5be64bc | 2014-09-08 18:35:15 -0700 | [diff] [blame] | 123 | void onIsVoipAudioModeChanged(Call call); |
Hall Liu | 9696c21 | 2016-06-24 16:09:02 -0700 | [diff] [blame] | 124 | void onVideoStateChanged(Call call, int previousVideoState, int newVideoState); |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 125 | void onCanAddCallChanged(boolean canAddCall); |
Tyler Gunn | 86014fc | 2015-06-12 14:31:25 -0700 | [diff] [blame] | 126 | void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile); |
Tyler Gunn | db82191 | 2016-02-16 14:35:25 -0800 | [diff] [blame] | 127 | void onHoldToneRequested(Call call); |
Tyler Gunn | 1a40c4f | 2016-04-14 14:29:45 -0700 | [diff] [blame] | 128 | void onExternalCallChanged(Call call, boolean isExternalCall); |
Honggang | b3ccbb3 | 2016-05-31 14:46:22 +0800 | [diff] [blame] | 129 | void onDisconnectedTonePlaying(boolean isTonePlaying); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 130 | } |
| 131 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 132 | /** Interface used to define the action which is executed delay under some condition. */ |
| 133 | interface PendingAction { |
| 134 | void performAction(); |
| 135 | } |
| 136 | |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 137 | private static final String TAG = "CallsManager"; |
Ben Gilad | 9f2bed3 | 2013-12-12 17:43:26 -0800 | [diff] [blame] | 138 | |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 139 | /** |
| 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 Gunn | ea4c6fb | 2017-04-14 13:46:21 -0700 | [diff] [blame] | 160 | private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION = |
| 161 | "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION"; |
| 162 | |
Tyler Gunn | 9e806ee | 2017-02-06 20:49:24 -0800 | [diff] [blame] | 163 | private static final int HANDLER_WAIT_TIMEOUT = 10000; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 164 | 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 Pius | 4de4a89 | 2015-08-24 11:06:58 -0700 | [diff] [blame] | 167 | private static final int MAXIMUM_DIALING_CALLS = 1; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 168 | private static final int MAXIMUM_OUTGOING_CALLS = 1; |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 169 | private static final int MAXIMUM_TOP_LEVEL_CALLS = 2; |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 170 | private static final int MAXIMUM_SELF_MANAGED_CALLS = 10; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 171 | |
| 172 | private static final int[] OUTGOING_CALL_STATES = |
Tyler Gunn | 1e37be5 | 2016-07-11 08:54:23 -0700 | [diff] [blame] | 173 | {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, |
| 174 | CallState.PULLING}; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 175 | |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 176 | /** |
| 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 Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 180 | private static final int[] LIVE_CALL_STATES = |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 181 | {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, |
Tyler Gunn | 1e37be5 | 2016-07-11 08:54:23 -0700 | [diff] [blame] | 182 | CallState.PULLING, CallState.ACTIVE}; |
| 183 | |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 184 | /** |
| 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 Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 195 | 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 Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 200 | public static final String TELECOM_CALL_ID_PREFIX = "TC@"; |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 201 | |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 202 | // 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 Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 214 | /** |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 215 | * 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 Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 217 | * |
Jay Shrauner | a82c8f7 | 2014-08-14 15:49:16 -0700 | [diff] [blame] | 218 | * 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 Cordon | 681663d | 2014-01-30 04:32:15 -0800 | [diff] [blame] | 221 | */ |
Jay Shrauner | a82c8f7 | 2014-08-14 15:49:16 -0700 | [diff] [blame] | 222 | private final Set<Call> mCalls = Collections.newSetFromMap( |
| 223 | new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); |
Santos Cordon | 681663d | 2014-01-30 04:32:15 -0800 | [diff] [blame] | 224 | |
Tyler Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 225 | /** |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 226 | * 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 Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 232 | * 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 Liu | aeece4e | 2017-02-14 16:42:12 -0800 | [diff] [blame] | 238 | private int mRttRequestId = 0; |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 239 | /** |
| 240 | * Stores the current foreground user. |
| 241 | */ |
| 242 | private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser()); |
| 243 | |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 244 | private final ConnectionServiceRepository mConnectionServiceRepository; |
| 245 | private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; |
| 246 | private final InCallController mInCallController; |
Santos Cordon | f3671a6 | 2014-05-29 21:51:53 -0700 | [diff] [blame] | 247 | private final CallAudioManager mCallAudioManager; |
Tyler Gunn | 5d66e1d | 2018-01-25 20:22:49 -0800 | [diff] [blame] | 248 | private final CallRecordingTonePlayer mCallRecordingTonePlayer; |
Ihab Awad | 8de7691 | 2015-02-17 12:25:52 -0800 | [diff] [blame] | 249 | private RespondViaSmsManager mRespondViaSmsManager; |
Santos Cordon | f3671a6 | 2014-05-29 21:51:53 -0700 | [diff] [blame] | 250 | private final Ringer mRinger; |
Santos Cordon | 61e6f35 | 2014-12-03 02:53:34 -0800 | [diff] [blame] | 251 | private final InCallWakeLockController mInCallWakeLockController; |
Jay Shrauner | a82c8f7 | 2014-08-14 15:49:16 -0700 | [diff] [blame] | 252 | // 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 Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 256 | private final HeadsetMediaButton mHeadsetMediaButton; |
Sailesh Nepal | b88795a | 2014-07-15 14:53:27 -0700 | [diff] [blame] | 257 | private final WiredHeadsetManager mWiredHeadsetManager; |
Hall Liu | 486cb19 | 2016-10-21 18:23:33 -0700 | [diff] [blame] | 258 | private final BluetoothRouteManager mBluetoothRouteManager; |
Santos Cordon | a04bdca | 2015-03-04 16:57:55 -0800 | [diff] [blame] | 259 | private final DockManager mDockManager; |
Sailesh Nepal | b88795a | 2014-07-15 14:53:27 -0700 | [diff] [blame] | 260 | private final TtyManager mTtyManager; |
Yorke Lee | d134687 | 2014-07-28 14:38:45 -0700 | [diff] [blame] | 261 | private final ProximitySensorManager mProximitySensorManager; |
Yorke Lee | f86db2e | 2014-09-12 17:56:48 -0700 | [diff] [blame] | 262 | private final PhoneStateBroadcaster mPhoneStateBroadcaster; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 263 | private final CallLogManager mCallLogManager; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 264 | private final Context mContext; |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 265 | private final TelecomSystem.SyncRoot mLock; |
| 266 | private final ContactsAsyncHelper mContactsAsyncHelper; |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 267 | private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 268 | private final PhoneAccountRegistrar mPhoneAccountRegistrar; |
| 269 | private final MissedCallNotifier mMissedCallNotifier; |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 270 | private IncomingCallNotifier mIncomingCallNotifier; |
Hall Liu | 5b70c1c | 2016-03-03 18:42:57 -0800 | [diff] [blame] | 271 | private final CallerInfoLookupHelper mCallerInfoLookupHelper; |
Hall Liu | 7c92832 | 2016-12-06 18:15:39 -0800 | [diff] [blame] | 272 | private final DefaultDialerCache mDefaultDialerCache; |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 273 | private final Timeouts.Adapter mTimeoutsAdapter; |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 274 | private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter; |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 275 | private final ClockProxy mClockProxy; |
Ihab Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 276 | private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>(); |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 277 | private final Set<Call> mPendingCallsToDisconnect = new HashSet<>(); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 278 | private final ConnectionServiceFocusManager mConnectionSvrFocusMgr; |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 279 | /* Handler tied to thread in which CallManager was initialized. */ |
Ihab Awad | e6dbc9d | 2015-03-26 10:33:44 -0700 | [diff] [blame] | 280 | private final Handler mHandler = new Handler(Looper.getMainLooper()); |
mike dooley | 66f26d1 | 2016-12-19 13:25:47 -0800 | [diff] [blame] | 281 | private final EmergencyCallHelper mEmergencyCallHelper; |
Sailesh Nepal | 84fa5f8 | 2014-04-02 11:01:11 -0700 | [diff] [blame] | 282 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 283 | 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 290 | .forEach(c -> c.disconnect("release " + |
| 291 | connectionService.getComponentName().getPackageName())); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | @Override |
| 295 | public void setCallsManagerListener(CallsManagerListener listener) { |
| 296 | mListeners.add(listener); |
| 297 | } |
| 298 | }; |
| 299 | |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 300 | private boolean mCanAddCall = true; |
| 301 | |
Brad Ebinger | 3da5764 | 2016-04-04 17:32:19 -0700 | [diff] [blame] | 302 | private TelephonyManager.MultiSimVariants mRadioSimVariants = null; |
| 303 | |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 304 | private Runnable mStopTone; |
| 305 | |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 306 | /** |
Tyler Gunn | ea4c6fb | 2017-04-14 13:46:21 -0700 | [diff] [blame] | 307 | * 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 Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 322 | * 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 Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 340 | * Initializes the required Telecom components. |
Santos Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 341 | */ |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 342 | @VisibleForTesting |
| 343 | public CallsManager( |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 344 | Context context, |
| 345 | TelecomSystem.SyncRoot lock, |
| 346 | ContactsAsyncHelper contactsAsyncHelper, |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 347 | CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 348 | MissedCallNotifier missedCallNotifier, |
| 349 | PhoneAccountRegistrar phoneAccountRegistrar, |
| 350 | HeadsetMediaButtonFactory headsetMediaButtonFactory, |
| 351 | ProximitySensorManagerFactory proximitySensorManagerFactory, |
Hall Liu | 8fb1fb7 | 2015-10-22 15:24:40 -0700 | [diff] [blame] | 352 | InCallWakeLockControllerFactory inCallWakeLockControllerFactory, |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 353 | ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory |
| 354 | connectionServiceFocusManagerFactory, |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 355 | CallAudioManager.AudioServiceFactory audioServiceFactory, |
Hall Liu | 486cb19 | 2016-10-21 18:23:33 -0700 | [diff] [blame] | 356 | BluetoothRouteManager bluetoothManager, |
Santos Cordon | f78a72f | 2016-01-21 22:10:32 +0000 | [diff] [blame] | 357 | WiredHeadsetManager wiredHeadsetManager, |
Santos Cordon | 501b9b3 | 2016-03-07 14:40:07 -0800 | [diff] [blame] | 358 | SystemStateProvider systemStateProvider, |
Hall Liu | 7c92832 | 2016-12-06 18:15:39 -0800 | [diff] [blame] | 359 | DefaultDialerCache defaultDialerCache, |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 360 | Timeouts.Adapter timeoutsAdapter, |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 361 | AsyncRingtonePlayer asyncRingtonePlayer, |
Tyler Gunn | eaaf074 | 2016-09-15 15:36:38 -0700 | [diff] [blame] | 362 | PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, |
Brad Ebinger | 7bba111 | 2017-06-08 13:57:28 -0700 | [diff] [blame] | 363 | EmergencyCallHelper emergencyCallHelper, |
Tyler Gunn | 02e00dd | 2017-08-24 15:44:02 -0700 | [diff] [blame] | 364 | InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory, |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 365 | ClockProxy clockProxy, |
Hall Liu | 784a496 | 2018-03-06 11:03:17 -0800 | [diff] [blame] | 366 | BluetoothStateReceiver bluetoothStateReceiver, |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 367 | InCallControllerFactory inCallControllerFactory) { |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 368 | mContext = context; |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 369 | mLock = lock; |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 370 | mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter; |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 371 | mContactsAsyncHelper = contactsAsyncHelper; |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 372 | mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory; |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 373 | mPhoneAccountRegistrar = phoneAccountRegistrar; |
Tyler Gunn | ea4c6fb | 2017-04-14 13:46:21 -0700 | [diff] [blame] | 374 | mPhoneAccountRegistrar.addListener(mPhoneAccountListener); |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 375 | mMissedCallNotifier = missedCallNotifier; |
| 376 | StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 377 | mWiredHeadsetManager = wiredHeadsetManager; |
Hall Liu | 7c92832 | 2016-12-06 18:15:39 -0800 | [diff] [blame] | 378 | mDefaultDialerCache = defaultDialerCache; |
Hall Liu | 486cb19 | 2016-10-21 18:23:33 -0700 | [diff] [blame] | 379 | mBluetoothRouteManager = bluetoothManager; |
Santos Cordon | a04bdca | 2015-03-04 16:57:55 -0800 | [diff] [blame] | 380 | mDockManager = new DockManager(context); |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 381 | mTimeoutsAdapter = timeoutsAdapter; |
mike dooley | 66f26d1 | 2016-12-19 13:25:47 -0800 | [diff] [blame] | 382 | mEmergencyCallHelper = emergencyCallHelper; |
Hall Liu | 5b70c1c | 2016-03-03 18:42:57 -0800 | [diff] [blame] | 383 | mCallerInfoLookupHelper = new CallerInfoLookupHelper(context, mCallerInfoAsyncQueryFactory, |
Hall Liu | 4659827 | 2016-04-05 12:58:51 -0700 | [diff] [blame] | 384 | mContactsAsyncHelper, mLock); |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 385 | |
Hall Liu | 5507671 | 2017-02-28 19:59:30 -0800 | [diff] [blame] | 386 | mDtmfLocalTonePlayer = |
| 387 | new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy()); |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 388 | CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine( |
| 389 | context, |
| 390 | this, |
| 391 | bluetoothManager, |
| 392 | wiredHeadsetManager, |
| 393 | statusBarNotifier, |
Hall Liu | a3e9dda | 2015-12-15 17:52:50 -0800 | [diff] [blame] | 394 | audioServiceFactory, |
Scott Randolph | 1b97fdd | 2017-11-01 18:34:19 -0700 | [diff] [blame] | 395 | CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 396 | ); |
Hall Liu | 2985570 | 2015-12-11 17:42:03 -0800 | [diff] [blame] | 397 | callAudioRouteStateMachine.initialize(); |
| 398 | |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 399 | CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter = |
| 400 | new CallAudioRoutePeripheralAdapter( |
| 401 | callAudioRouteStateMachine, |
| 402 | bluetoothManager, |
| 403 | wiredHeadsetManager, |
| 404 | mDockManager); |
| 405 | |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 406 | InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory( |
Brad Ebinger | 7bba111 | 2017-06-08 13:57:28 -0700 | [diff] [blame] | 407 | callAudioRoutePeripheralAdapter, lock, toneGeneratorFactory); |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 408 | |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 409 | SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil(); |
Brad Ebinger | c9286f4 | 2016-03-10 16:02:54 -0800 | [diff] [blame] | 410 | RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context); |
Brad Ebinger | d931a01 | 2015-10-21 12:54:08 -0700 | [diff] [blame] | 411 | SystemVibrator systemVibrator = new SystemVibrator(context); |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 412 | mInCallController = inCallControllerFactory.create(context, mLock, this, |
| 413 | systemStateProvider, defaultDialerCache, mTimeoutsAdapter, |
mike dooley | 66f26d1 | 2016-12-19 13:25:47 -0800 | [diff] [blame] | 414 | emergencyCallHelper); |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 415 | mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer, |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 416 | ringtoneFactory, systemVibrator, mInCallController); |
Tyler Gunn | 5d66e1d | 2018-01-25 20:22:49 -0800 | [diff] [blame] | 417 | mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, |
| 418 | (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), mLock); |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 419 | mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine, |
Hall Liu | fbed016 | 2016-01-14 18:06:29 -0800 | [diff] [blame] | 420 | this,new CallAudioModeStateMachine((AudioManager) |
| 421 | mContext.getSystemService(Context.AUDIO_SERVICE)), |
Hall Liu | 784a496 | 2018-03-06 11:03:17 -0800 | [diff] [blame] | 422 | playerFactory, mRinger, new RingbackPlayer(playerFactory), |
| 423 | bluetoothStateReceiver, mDtmfLocalTonePlayer); |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 424 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 425 | mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create( |
| 426 | mRequester, Looper.getMainLooper()); |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 427 | mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock); |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 428 | mTtyManager = new TtyManager(context, mWiredHeadsetManager); |
Ihab Awad | 8de7691 | 2015-02-17 12:25:52 -0800 | [diff] [blame] | 429 | mProximitySensorManager = proximitySensorManagerFactory.create(context, this); |
| 430 | mPhoneStateBroadcaster = new PhoneStateBroadcaster(this); |
Ta-wei Yen | 982c0bd | 2016-04-14 13:59:54 -0700 | [diff] [blame] | 431 | mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier); |
Ihab Awad | 8de7691 | 2015-02-17 12:25:52 -0800 | [diff] [blame] | 432 | mConnectionServiceRepository = |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 433 | new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this); |
Ihab Awad | 8de7691 | 2015-02-17 12:25:52 -0800 | [diff] [blame] | 434 | mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this); |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 435 | mClockProxy = clockProxy; |
Santos Cordon | ae19306 | 2014-05-21 21:21:49 -0700 | [diff] [blame] | 436 | |
Tyler Gunn | ae694b1 | 2015-11-11 17:46:03 -0800 | [diff] [blame] | 437 | mListeners.add(mInCallWakeLockController); |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 438 | mListeners.add(statusBarNotifier); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 439 | mListeners.add(mCallLogManager); |
Yorke Lee | f86db2e | 2014-09-12 17:56:48 -0700 | [diff] [blame] | 440 | mListeners.add(mPhoneStateBroadcaster); |
Santos Cordon | f3671a6 | 2014-05-29 21:51:53 -0700 | [diff] [blame] | 441 | mListeners.add(mInCallController); |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 442 | mListeners.add(mCallAudioManager); |
Tyler Gunn | 5d66e1d | 2018-01-25 20:22:49 -0800 | [diff] [blame] | 443 | mListeners.add(mCallRecordingTonePlayer); |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 444 | mListeners.add(missedCallNotifier); |
Santos Cordon | 8128998 | 2014-06-03 16:03:08 -0700 | [diff] [blame] | 445 | mListeners.add(mHeadsetMediaButton); |
Yorke Lee | d134687 | 2014-07-28 14:38:45 -0700 | [diff] [blame] | 446 | mListeners.add(mProximitySensorManager); |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 447 | |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 448 | // 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 Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 454 | // 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 Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 459 | } |
| 460 | |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 461 | public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) { |
| 462 | if (mIncomingCallNotifier != null) { |
| 463 | mListeners.remove(mIncomingCallNotifier); |
| 464 | } |
| 465 | mIncomingCallNotifier = incomingCallNotifier; |
| 466 | mListeners.add(mIncomingCallNotifier); |
| 467 | } |
| 468 | |
Ihab Awad | 8de7691 | 2015-02-17 12:25:52 -0800 | [diff] [blame] | 469 | 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 Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 479 | } |
| 480 | |
Hall Liu | 5b70c1c | 2016-03-03 18:42:57 -0800 | [diff] [blame] | 481 | public CallerInfoLookupHelper getCallerInfoLookupHelper() { |
| 482 | return mCallerInfoLookupHelper; |
| 483 | } |
| 484 | |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 485 | @Override |
Ihab Awad | 6fb37c8 | 2014-08-07 19:48:57 -0700 | [diff] [blame] | 486 | public void onSuccessfulOutgoingCall(Call call, int callState) { |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 487 | Log.v(this, "onSuccessfulOutgoingCall, %s", call); |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 488 | |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 489 | setCallState(call, callState, "successful outgoing call"); |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 490 | 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 Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 499 | } |
Santos Cordon | 6cb7ba9 | 2014-05-23 14:09:32 -0700 | [diff] [blame] | 500 | |
| 501 | markCallAsDialing(call); |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 502 | } |
| 503 | |
| 504 | @Override |
Andrew Lee | 701dc00 | 2014-09-11 21:29:12 -0700 | [diff] [blame] | 505 | public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) { |
Sailesh Nepal | 5a73b03 | 2014-06-25 15:53:21 -0700 | [diff] [blame] | 506 | Log.v(this, "onFailedOutgoingCall, call: %s", call); |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 507 | |
Andrew Lee | e6288a7 | 2014-10-01 13:50:02 -0700 | [diff] [blame] | 508 | markCallAsRemoved(call); |
Sailesh Nepal | 5a73b03 | 2014-06-25 15:53:21 -0700 | [diff] [blame] | 509 | } |
| 510 | |
| 511 | @Override |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 512 | public void onSuccessfulIncomingCall(Call incomingCall) { |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 513 | Log.d(this, "onSuccessfulIncomingCall"); |
Hall Liu | ddf3f9c | 2016-08-30 13:38:52 -0700 | [diff] [blame] | 514 | 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 Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 520 | List<IncomingCallFilter.CallFilter> filters = new ArrayList<>(); |
| 521 | filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper)); |
Tyler Gunn | aee51f2 | 2018-03-09 02:33:54 +0000 | [diff] [blame] | 522 | filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter(), |
| 523 | mCallerInfoLookupHelper)); |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 524 | filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar, |
Hall Liu | 7c92832 | 2016-12-06 18:15:39 -0800 | [diff] [blame] | 525 | mDefaultDialerCache, new ParcelableCallUtils.Converter(), mLock)); |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 526 | new IncomingCallFilter(mContext, this, incomingCall, mLock, |
| 527 | mTimeoutsAdapter, filters).performFiltering(); |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 528 | } |
| 529 | |
| 530 | @Override |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 531 | public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) { |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 532 | // Only set the incoming call as ringing if it isn't already disconnected. It is possible |
Tyler Gunn | e6276e1 | 2015-10-22 20:48:50 -0700 | [diff] [blame] | 533 | // 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 Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 538 | result.shouldAllowCall ? "successful incoming call" : "blocking call"); |
Tyler Gunn | e6276e1 | 2015-10-22 20:48:50 -0700 | [diff] [blame] | 539 | } else { |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 540 | Log.i(this, "onCallFilteringCompleted: call already disconnected."); |
Tyler Gunn | 2ecfd88 | 2016-09-16 15:32:11 -0700 | [diff] [blame] | 541 | return; |
Tyler Gunn | e6276e1 | 2015-10-22 20:48:50 -0700 | [diff] [blame] | 542 | } |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 543 | |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 544 | if (result.shouldAllowCall) { |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 545 | if (hasMaximumManagedRingingCalls(incomingCall)) { |
Wei Liu | 13791b9 | 2016-10-04 17:46:44 -0700 | [diff] [blame] | 546 | 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 Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 553 | } else if (hasMaximumManagedDialingCalls(incomingCall)) { |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 554 | Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " + |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 555 | "dialing calls."); |
| 556 | rejectCallAndLog(incomingCall); |
| 557 | } else { |
| 558 | addCall(incomingCall); |
| 559 | } |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 560 | } else { |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 561 | if (result.shouldReject) { |
| 562 | Log.i(this, "onCallFilteringCompleted: blocked call, rejecting."); |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 563 | incomingCall.reject(false, null); |
| 564 | } |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 565 | if (result.shouldAddToCallLog) { |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 566 | Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log."); |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 567 | if (result.shouldShowNotification) { |
Ta-wei Yen | 982c0bd | 2016-04-14 13:59:54 -0700 | [diff] [blame] | 568 | Log.w(this, "onCallScreeningCompleted: blocked call, showing notification."); |
| 569 | } |
Hall Liu | 6d4b66d | 2016-04-01 16:31:13 -0700 | [diff] [blame] | 570 | mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE, |
| 571 | result.shouldShowNotification); |
| 572 | } else if (result.shouldShowNotification) { |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 573 | Log.i(this, "onCallScreeningCompleted: blocked call, showing notification."); |
Hall Liu | 3037ac6 | 2016-09-22 14:40:03 -0700 | [diff] [blame] | 574 | mMissedCallNotifier.showMissedCallNotification( |
| 575 | new MissedCallNotifier.CallInfo(incomingCall)); |
Sailesh Nepal | ae92595 | 2016-01-24 18:56:58 -0800 | [diff] [blame] | 576 | } |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 577 | } |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 578 | } |
| 579 | |
Wei Liu | 13791b9 | 2016-10-04 17:46:44 -0700 | [diff] [blame] | 580 | /** |
| 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 Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 611 | @Override |
| 612 | public void onFailedIncomingCall(Call call) { |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 613 | setCallState(call, CallState.DISCONNECTED, "failed incoming call"); |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 614 | call.removeListener(this); |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 615 | } |
| 616 | |
Ihab Awad | cb387ac | 2014-05-28 16:49:38 -0700 | [diff] [blame] | 617 | @Override |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 618 | public void onSuccessfulUnknownCall(Call call, int callState) { |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 619 | setCallState(call, callState, "successful unknown call"); |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 620 | 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 Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 627 | setCallState(call, CallState.DISCONNECTED, "failed unknown call"); |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 628 | call.removeListener(this); |
| 629 | } |
| 630 | |
| 631 | @Override |
Andrew Lee | 5be64bc | 2014-09-08 18:35:15 -0700 | [diff] [blame] | 632 | public void onRingbackRequested(Call call, boolean ringback) { |
Ihab Awad | cb387ac | 2014-05-28 16:49:38 -0700 | [diff] [blame] | 633 | for (CallsManagerListener listener : mListeners) { |
Andrew Lee | 5be64bc | 2014-09-08 18:35:15 -0700 | [diff] [blame] | 634 | listener.onRingbackRequested(call, ringback); |
Ihab Awad | cb387ac | 2014-05-28 16:49:38 -0700 | [diff] [blame] | 635 | } |
| 636 | } |
| 637 | |
Evan Charlton | 352105c | 2014-06-03 14:10:54 -0700 | [diff] [blame] | 638 | @Override |
| 639 | public void onPostDialWait(Call call, String remaining) { |
| 640 | mInCallController.onPostDialWait(call, remaining); |
| 641 | } |
| 642 | |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 643 | @Override |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 644 | 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 Ebinger | e62e9e8 | 2016-02-01 18:26:40 -0800 | [diff] [blame] | 649 | mHandler.removeCallbacks(mStopTone.getRunnableToCancel()); |
| 650 | mStopTone.cancel(); |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 651 | } |
| 652 | |
| 653 | mDtmfLocalTonePlayer.playTone(call, nextChar); |
| 654 | |
Brad Ebinger | f5e0666 | 2016-08-25 16:16:27 -0700 | [diff] [blame] | 655 | mStopTone = new Runnable("CM.oPDC", mLock) { |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 656 | @Override |
Brad Ebinger | e62e9e8 | 2016-02-01 18:26:40 -0800 | [diff] [blame] | 657 | public void loggedRun() { |
Brad Ebinger | f5e0666 | 2016-08-25 16:16:27 -0700 | [diff] [blame] | 658 | // Set a timeout to stop the tone in case there isn't another tone to |
| 659 | // follow. |
| 660 | mDtmfLocalTonePlayer.stopTone(call); |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 661 | } |
| 662 | }; |
Brad Ebinger | e62e9e8 | 2016-02-01 18:26:40 -0800 | [diff] [blame] | 663 | mHandler.postDelayed(mStopTone.prepare(), |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 664 | 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 Ebinger | e62e9e8 | 2016-02-01 18:26:40 -0800 | [diff] [blame] | 670 | mHandler.removeCallbacks(mStopTone.getRunnableToCancel()); |
| 671 | mStopTone.cancel(); |
Nancy Chen | a469f76 | 2014-12-09 18:29:20 -0800 | [diff] [blame] | 672 | } |
| 673 | mDtmfLocalTonePlayer.stopTone(call); |
| 674 | } else { |
| 675 | Log.w(this, "onPostDialChar: invalid value %d", nextChar); |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | @Override |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 680 | public void onParentChanged(Call call) { |
Santos Cordon | 66fe882 | 2014-10-10 16:10:58 -0700 | [diff] [blame] | 681 | // parent-child relationship affects which call should be foreground, so do an update. |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 682 | updateCanAddCall(); |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 683 | for (CallsManagerListener listener : mListeners) { |
| 684 | listener.onIsConferencedChanged(call); |
| 685 | } |
| 686 | } |
| 687 | |
| 688 | @Override |
| 689 | public void onChildrenChanged(Call call) { |
Santos Cordon | 66fe882 | 2014-10-10 16:10:58 -0700 | [diff] [blame] | 690 | // parent-child relationship affects which call should be foreground, so do an update. |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 691 | updateCanAddCall(); |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 692 | for (CallsManagerListener listener : mListeners) { |
| 693 | listener.onIsConferencedChanged(call); |
| 694 | } |
| 695 | } |
| 696 | |
Ihab Awad | ff7493a | 2014-06-10 13:47:44 -0700 | [diff] [blame] | 697 | @Override |
Andrew Lee | 5be64bc | 2014-09-08 18:35:15 -0700 | [diff] [blame] | 698 | public void onIsVoipAudioModeChanged(Call call) { |
Sailesh Nepal | 7e66957 | 2014-07-08 21:29:12 -0700 | [diff] [blame] | 699 | for (CallsManagerListener listener : mListeners) { |
Andrew Lee | 5be64bc | 2014-09-08 18:35:15 -0700 | [diff] [blame] | 700 | listener.onIsVoipAudioModeChanged(call); |
Sailesh Nepal | 7e66957 | 2014-07-08 21:29:12 -0700 | [diff] [blame] | 701 | } |
| 702 | } |
| 703 | |
Andrew Lee | 4a79660 | 2014-07-11 17:23:03 -0700 | [diff] [blame] | 704 | @Override |
Hall Liu | 9696c21 | 2016-06-24 16:09:02 -0700 | [diff] [blame] | 705 | public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) { |
Andrew Lee | 4a79660 | 2014-07-11 17:23:03 -0700 | [diff] [blame] | 706 | for (CallsManagerListener listener : mListeners) { |
Hall Liu | 9696c21 | 2016-06-24 16:09:02 -0700 | [diff] [blame] | 707 | listener.onVideoStateChanged(call, previousVideoState, newVideoState); |
Andrew Lee | 4a79660 | 2014-07-11 17:23:03 -0700 | [diff] [blame] | 708 | } |
| 709 | } |
| 710 | |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 711 | @Override |
Hall Liu | 63e690c | 2017-02-14 18:05:03 -0800 | [diff] [blame] | 712 | public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call, |
| 713 | long disconnectionTimeout) { |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 714 | mPendingCallsToDisconnect.add(call); |
Brad Ebinger | f5e0666 | 2016-08-25 16:16:27 -0700 | [diff] [blame] | 715 | mHandler.postDelayed(new Runnable("CM.oCVNOCB", mLock) { |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 716 | @Override |
Brad Ebinger | e62e9e8 | 2016-02-01 18:26:40 -0800 | [diff] [blame] | 717 | public void loggedRun() { |
Brad Ebinger | f5e0666 | 2016-08-25 16:16:27 -0700 | [diff] [blame] | 718 | if (mPendingCallsToDisconnect.remove(call)) { |
| 719 | Log.i(this, "Delayed disconnection of call: %s", call); |
| 720 | call.disconnect(); |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 721 | } |
| 722 | } |
Hall Liu | 63e690c | 2017-02-14 18:05:03 -0800 | [diff] [blame] | 723 | }.prepare(), disconnectionTimeout); |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 724 | |
| 725 | return true; |
| 726 | } |
| 727 | |
Tyler Gunn | 86014fc | 2015-06-12 14:31:25 -0700 | [diff] [blame] | 728 | /** |
| 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 Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 767 | public Collection<Call> getCalls() { |
Santos Cordon | 4bc0245 | 2014-11-19 15:51:12 -0800 | [diff] [blame] | 768 | return Collections.unmodifiableCollection(mCalls); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 769 | } |
| 770 | |
Tyler Gunn | db82191 | 2016-02-16 14:35:25 -0800 | [diff] [blame] | 771 | /** |
| 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 Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 786 | /** |
| 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 Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 792 | * @param extras |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 793 | */ |
| 794 | @Override |
Tyler Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 795 | public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 796 | Bundle extras, boolean isLegacy) { |
| 797 | if (isLegacy) { |
| 798 | requestHandoverViaEvents(call, handoverTo, videoState, extras); |
| 799 | } else { |
| 800 | requestHandover(call, handoverTo, videoState, extras); |
| 801 | } |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 802 | } |
| 803 | |
Brad Ebinger | d931a01 | 2015-10-21 12:54:08 -0700 | [diff] [blame] | 804 | @VisibleForTesting |
| 805 | public Call getForegroundCall() { |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 806 | if (mCallAudioManager == null) { |
| 807 | // Happens when getForegroundCall is called before full initialization. |
| 808 | return null; |
| 809 | } |
| 810 | return mCallAudioManager.getForegroundCall(); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 811 | } |
| 812 | |
Tyler Gunn | 1bf0e6b | 2016-11-30 15:19:13 -0800 | [diff] [blame] | 813 | @Override |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 814 | public UserHandle getCurrentUserHandle() { |
| 815 | return mCurrentUserHandle; |
| 816 | } |
| 817 | |
Hall Liu | 9ecbb1c | 2016-04-14 14:35:48 -0700 | [diff] [blame] | 818 | public CallAudioManager getCallAudioManager() { |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 819 | return mCallAudioManager; |
Santos Cordon | ae19306 | 2014-05-21 21:21:49 -0700 | [diff] [blame] | 820 | } |
| 821 | |
Santos Cordon | f3671a6 | 2014-05-29 21:51:53 -0700 | [diff] [blame] | 822 | InCallController getInCallController() { |
| 823 | return mInCallController; |
| 824 | } |
| 825 | |
mike dooley | 66f26d1 | 2016-12-19 13:25:47 -0800 | [diff] [blame] | 826 | EmergencyCallHelper getEmergencyCallHelper() { |
| 827 | return mEmergencyCallHelper; |
| 828 | } |
| 829 | |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 830 | @VisibleForTesting |
| 831 | public boolean hasEmergencyCall() { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 832 | for (Call call : mCalls) { |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 833 | if (call.isEmergencyCall()) { |
| 834 | return true; |
| 835 | } |
| 836 | } |
| 837 | return false; |
| 838 | } |
| 839 | |
Hall Liu | ccd793f | 2018-03-16 14:48:59 -0700 | [diff] [blame] | 840 | public boolean hasEmergencyRttCall() { |
| 841 | for (Call call : mCalls) { |
| 842 | if (call.isEmergencyCall() && call.isRttCall()) { |
| 843 | return true; |
| 844 | } |
| 845 | } |
| 846 | return false; |
| 847 | } |
| 848 | |
Honggang | b3ccbb3 | 2016-05-31 14:46:22 +0800 | [diff] [blame] | 849 | @VisibleForTesting |
| 850 | public boolean hasOnlyDisconnectedCalls() { |
| 851 | if (mCalls.size() == 0) { |
| 852 | return false; |
| 853 | } |
Roshan Pius | 669f90b | 2015-09-02 16:21:39 -0700 | [diff] [blame] | 854 | for (Call call : mCalls) { |
| 855 | if (!call.isDisconnected()) { |
| 856 | return false; |
| 857 | } |
| 858 | } |
| 859 | return true; |
| 860 | } |
| 861 | |
Hall Liu | 5b8551e | 2017-03-10 17:05:23 -0800 | [diff] [blame] | 862 | public boolean hasVideoCall() { |
Andrew Lee | 4550623 | 2014-10-22 17:54:33 -0700 | [diff] [blame] | 863 | for (Call call : mCalls) { |
Tyler Gunn | 467b64f | 2015-06-09 13:48:47 -0700 | [diff] [blame] | 864 | if (VideoProfile.isVideo(call.getVideoState())) { |
Andrew Lee | 4550623 | 2014-10-22 17:54:33 -0700 | [diff] [blame] | 865 | return true; |
| 866 | } |
| 867 | } |
| 868 | return false; |
| 869 | } |
| 870 | |
Hall Liu | 28b82f0 | 2016-07-26 17:38:56 -0700 | [diff] [blame] | 871 | @VisibleForTesting |
| 872 | public CallAudioState getAudioState() { |
Yorke Lee | 2a66f7b | 2015-05-13 14:21:19 -0700 | [diff] [blame] | 873 | return mCallAudioManager.getCallAudioState(); |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 874 | } |
| 875 | |
Sailesh Nepal | b88795a | 2014-07-15 14:53:27 -0700 | [diff] [blame] | 876 | boolean isTtySupported() { |
| 877 | return mTtyManager.isTtySupported(); |
| 878 | } |
| 879 | |
| 880 | int getCurrentTtyMode() { |
| 881 | return mTtyManager.getCurrentTtyMode(); |
| 882 | } |
| 883 | |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 884 | @VisibleForTesting |
| 885 | public void addListener(CallsManagerListener listener) { |
Santos Cordon | 68d1a6b | 2014-09-19 12:25:58 -0700 | [diff] [blame] | 886 | mListeners.add(listener); |
| 887 | } |
| 888 | |
| 889 | void removeListener(CallsManagerListener listener) { |
| 890 | mListeners.remove(listener); |
| 891 | } |
| 892 | |
Santos Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 893 | /** |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 894 | * Starts the process to attach the call to a connection service. |
Santos Cordon | 80d9bdc | 2014-02-13 18:28:46 -0800 | [diff] [blame] | 895 | * |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 896 | * @param phoneAccountHandle The phone account which contains the component name of the |
| 897 | * connection service to use for this call. |
Evan Charlton | a05805b | 2014-03-05 08:21:46 -0800 | [diff] [blame] | 898 | * @param extras The optional extras Bundle passed with the intent used for the incoming call. |
Santos Cordon | 80d9bdc | 2014-02-13 18:28:46 -0800 | [diff] [blame] | 899 | */ |
Evan Charlton | 8917637 | 2014-07-19 18:23:09 -0700 | [diff] [blame] | 900 | void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) { |
Sailesh Nepal | f1c191d | 2014-03-07 18:17:39 -0800 | [diff] [blame] | 901 | Log.d(this, "processIncomingCallIntent"); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 902 | boolean isHandover = extras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER); |
Yorke Lee | 69fa897 | 2015-06-08 11:25:32 -0700 | [diff] [blame] | 903 | 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 Nepal | 905dfba | 2014-07-14 08:20:41 -0700 | [diff] [blame] | 908 | Call call = new Call( |
Tyler Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 909 | getNextCallId(), |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 910 | mContext, |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 911 | this, |
Ihab Awad | e6dbc9d | 2015-03-26 10:33:44 -0700 | [diff] [blame] | 912 | mLock, |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 913 | mConnectionServiceRepository, |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 914 | mContactsAsyncHelper, |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 915 | mCallerInfoAsyncQueryFactory, |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 916 | mPhoneNumberUtilsAdapter, |
Sailesh Nepal | 8aa6a00 | 2014-09-12 10:52:49 -0700 | [diff] [blame] | 917 | handle, |
Sailesh Nepal | 664837f | 2014-07-14 16:31:51 -0700 | [diff] [blame] | 918 | null /* gatewayInfo */, |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 919 | null /* connectionManagerPhoneAccount */, |
Evan Charlton | 8917637 | 2014-07-19 18:23:09 -0700 | [diff] [blame] | 920 | phoneAccountHandle, |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 921 | Call.CALL_DIRECTION_INCOMING /* callDirection */, |
| 922 | false /* forceAttachToExistingConnection */, |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 923 | false, /* isConference */ |
| 924 | mClockProxy); |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 925 | |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 926 | // 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 Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 934 | if (call.isSelfManaged()) { |
| 935 | // Self managed calls will always be voip audio mode. |
| 936 | call.setIsVoipAudioMode(true); |
Pengquan Meng | fc9c8ff | 2018-01-17 05:50:49 +0000 | [diff] [blame] | 937 | } else { |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 938 | // 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 Meng | fc9c8ff | 2018-01-17 05:50:49 +0000 | [diff] [blame] | 943 | Bundle dropCallExtras = new Bundle(); |
| 944 | dropCallExtras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true); |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 945 | |
Pengquan Meng | fc9c8ff | 2018-01-17 05:50:49 +0000 | [diff] [blame] | 946 | // Include the name of the app which will drop the call. |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 947 | 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 Meng | fc9c8ff | 2018-01-17 05:50:49 +0000 | [diff] [blame] | 951 | call.putExtras(Call.SOURCE_CONNECTION_SERVICE, dropCallExtras); |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 952 | } |
| 953 | } |
Srikanth Chintala | 5b8facb | 2015-02-17 11:39:21 +0530 | [diff] [blame] | 954 | |
Tony Mak | 59a314c | 2017-06-20 16:31:44 +0100 | [diff] [blame] | 955 | if (extras.getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { |
Srikanth Chintala | 5b8facb | 2015-02-17 11:39:21 +0530 | [diff] [blame] | 956 | Log.d(this, "processIncomingCallIntent: defaulting to voip mode for call %s", |
| 957 | call.getId()); |
| 958 | call.setIsVoipAudioMode(true); |
| 959 | } |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 960 | } |
Hall Liu | db81e6a | 2018-03-12 18:28:37 -0700 | [diff] [blame] | 961 | if (isRttSettingOn() || |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 962 | extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { |
Hall Liu | f71cca9 | 2018-02-27 13:05:25 -0800 | [diff] [blame] | 963 | Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn()); |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 964 | if (phoneAccount != null && |
| 965 | phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { |
Hall Liu | 996ecdb | 2018-02-09 16:48:57 -0800 | [diff] [blame] | 966 | call.createRttStreams(); |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 967 | } |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 968 | // 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 Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 971 | } |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 972 | // If the extras specifies a video state, set it on the call if the PhoneAccount supports |
| 973 | // video. |
Tyler Gunn | b821f92 | 2017-06-21 14:40:19 -0700 | [diff] [blame] | 974 | int videoState = VideoProfile.STATE_AUDIO_ONLY; |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 975 | if (extras.containsKey(TelecomManager.EXTRA_INCOMING_VIDEO_STATE) && |
| 976 | phoneAccount != null && phoneAccount.hasCapabilities( |
| 977 | PhoneAccount.CAPABILITY_VIDEO_CALLING)) { |
Tyler Gunn | b821f92 | 2017-06-21 14:40:19 -0700 | [diff] [blame] | 978 | videoState = extras.getInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE); |
| 979 | call.setVideoState(videoState); |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 980 | } |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 981 | |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 982 | call.initAnalytics(); |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 983 | if (getForegroundCall() != null) { |
| 984 | getForegroundCall().getAnalytics().setCallIsInterrupted(true); |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 985 | call.getAnalytics().setCallIsAdditional(true); |
| 986 | } |
Sailesh Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 987 | setIntentExtrasAndStartTime(call, extras); |
Santos Cordon | df39986 | 2014-08-06 04:39:15 -0700 | [diff] [blame] | 988 | // TODO: Move this to be a part of addCall() |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 989 | call.addListener(this); |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 990 | |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 991 | boolean isHandoverAllowed = true; |
| 992 | if (isHandover) { |
| 993 | if (!isHandoverInProgress() && |
| 994 | isHandoverToPhoneAccountSupported(phoneAccountHandle)) { |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 995 | 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 1004 | "handover."); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 1005 | 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 1014 | fromCall.setHandoverDestinationCall(call); |
| 1015 | call.setHandoverSourceCall(fromCall); |
| 1016 | call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); |
| 1017 | fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); |
Tyler Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 1018 | 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 Gunn | b821f92 | 2017-06-21 14:40:19 -0700 | [diff] [blame] | 1022 | 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 Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 1027 | } |
Tyler Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 1028 | } else { |
| 1029 | Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 1030 | } |
| 1031 | } |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 1032 | |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 1033 | if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, |
| 1034 | call.getTargetPhoneAccount()))) { |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 1035 | notifyCreateConnectionFailed(phoneAccountHandle, call); |
| 1036 | } else { |
| 1037 | call.startCreateConnection(mPhoneAccountRegistrar); |
| 1038 | } |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 1039 | } |
| 1040 | |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 1041 | 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 Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 1045 | getNextCallId(), |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 1046 | mContext, |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 1047 | this, |
Ihab Awad | e6dbc9d | 2015-03-26 10:33:44 -0700 | [diff] [blame] | 1048 | mLock, |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 1049 | mConnectionServiceRepository, |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 1050 | mContactsAsyncHelper, |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 1051 | mCallerInfoAsyncQueryFactory, |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 1052 | mPhoneNumberUtilsAdapter, |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 1053 | handle, |
| 1054 | null /* gatewayInfo */, |
| 1055 | null /* connectionManagerPhoneAccount */, |
| 1056 | phoneAccountHandle, |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1057 | Call.CALL_DIRECTION_UNKNOWN /* callDirection */, |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 1058 | // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach |
| 1059 | // to the existing connection instead of trying to create a new one. |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1060 | true /* forceAttachToExistingConnection */, |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 1061 | false, /* isConference */ |
| 1062 | mClockProxy); |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1063 | call.initAnalytics(); |
| 1064 | |
Sailesh Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 1065 | setIntentExtrasAndStartTime(call, extras); |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 1066 | call.addListener(this); |
| 1067 | call.startCreateConnection(mPhoneAccountRegistrar); |
| 1068 | } |
| 1069 | |
Yorke Lee | 8d3f269 | 2015-05-08 11:44:39 -0700 | [diff] [blame] | 1070 | 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 Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1084 | private Call reuseOutgoingCall(Uri handle) { |
| 1085 | // Check to see if we can reuse any of the calls that are waiting to disconnect. |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 1086 | // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information. |
Yorke Lee | 5997951 | 2014-12-02 01:03:40 -0800 | [diff] [blame] | 1087 | Call reusedCall = null; |
Brad Ebinger | e680e66 | 2016-08-26 11:45:43 -0700 | [diff] [blame] | 1088 | for (Iterator<Call> callIter = mPendingCallsToDisconnect.iterator(); callIter.hasNext();) { |
| 1089 | Call pendingCall = callIter.next(); |
Yorke Lee | 8d3f269 | 2015-05-08 11:44:39 -0700 | [diff] [blame] | 1090 | if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) { |
Brad Ebinger | e680e66 | 2016-08-26 11:45:43 -0700 | [diff] [blame] | 1091 | callIter.remove(); |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 1092 | Log.i(this, "Reusing disconnected call %s", pendingCall); |
Yorke Lee | 5997951 | 2014-12-02 01:03:40 -0800 | [diff] [blame] | 1093 | reusedCall = pendingCall; |
| 1094 | } else { |
Yorke Lee | 8d3f269 | 2015-05-08 11:44:39 -0700 | [diff] [blame] | 1095 | Log.i(this, "Not reusing disconnected call %s", pendingCall); |
Hall Liu | 10139b2 | 2017-03-06 19:28:04 -0800 | [diff] [blame] | 1096 | callIter.remove(); |
Yorke Lee | 5997951 | 2014-12-02 01:03:40 -0800 | [diff] [blame] | 1097 | pendingCall.disconnect(); |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 1098 | } |
| 1099 | } |
| 1100 | |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1101 | return reusedCall; |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 1102 | } |
| 1103 | |
Nancy Chen | 0d3076c | 2014-07-30 14:45:44 -0700 | [diff] [blame] | 1104 | /** |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 1105 | * 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 Chen | a9d91da | 2014-08-12 14:31:06 -0700 | [diff] [blame] | 1110 | * @param handle Handle to connect the call with. |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1111 | * @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 Mak | 578a4e6 | 2015-11-23 11:18:51 +0000 | [diff] [blame] | 1114 | * @param initiatingUser {@link UserHandle} of user that place the outgoing call. |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 1115 | * @param originalIntent |
Nancy Chen | 0d3076c | 2014-07-30 14:45:44 -0700 | [diff] [blame] | 1116 | */ |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1117 | @VisibleForTesting |
| 1118 | public Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras, |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 1119 | UserHandle initiatingUser, Intent originalIntent) { |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1120 | boolean isReusedCall = true; |
| 1121 | Call call = reuseOutgoingCall(handle); |
| 1122 | |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 1123 | PhoneAccount account = |
| 1124 | mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser); |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1125 | boolean isSelfManaged = account != null && account.isSelfManaged(); |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 1126 | |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1127 | // 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 Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 1136 | mPhoneNumberUtilsAdapter, |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1137 | handle, |
| 1138 | null /* gatewayInfo */, |
| 1139 | null /* connectionManagerPhoneAccount */, |
| 1140 | null /* phoneAccountHandle */, |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1141 | Call.CALL_DIRECTION_OUTGOING /* callDirection */, |
| 1142 | false /* forceAttachToExistingConnection */, |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 1143 | false, /* isConference */ |
| 1144 | mClockProxy); |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1145 | call.initAnalytics(); |
| 1146 | |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 1147 | // 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 Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1151 | call.setIsSelfManaged(isSelfManaged); |
| 1152 | if (isSelfManaged) { |
| 1153 | // Self-managed calls will ALWAYS use voip audio mode. |
| 1154 | call.setIsVoipAudioMode(true); |
Tyler Gunn | acb3bc8 | 2017-01-09 09:43:56 -0800 | [diff] [blame] | 1155 | } |
Hall Liu | 874c0f8 | 2016-04-29 18:13:18 -0700 | [diff] [blame] | 1156 | call.setInitiatingUser(initiatingUser); |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1157 | isReusedCall = false; |
| 1158 | } |
Nancy Chen | 1c5926f | 2014-09-17 14:44:14 -0700 | [diff] [blame] | 1159 | |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1160 | int videoState = VideoProfile.STATE_AUDIO_ONLY; |
Tyler Gunn | 228f95e | 2016-03-03 16:24:45 -0800 | [diff] [blame] | 1161 | if (extras != null) { |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1162 | // 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 Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1164 | videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, |
Tyler Gunn | f815638 | 2016-03-30 08:35:29 -0700 | [diff] [blame] | 1165 | 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 Gunn | 10274f7 | 2016-07-26 15:38:16 -0700 | [diff] [blame] | 1169 | // 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 Gunn | 10274f7 | 2016-07-26 15:38:16 -0700 | [diff] [blame] | 1172 | if (call.isEmergencyCall() && account != null && |
Tyler Gunn | f815638 | 2016-03-30 08:35:29 -0700 | [diff] [blame] | 1173 | !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 Gunn | 10274f7 | 2016-07-26 15:38:16 -0700 | [diff] [blame] | 1180 | } 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 Gunn | f815638 | 2016-03-30 08:35:29 -0700 | [diff] [blame] | 1186 | } |
| 1187 | } |
| 1188 | |
| 1189 | call.setVideoState(videoState); |
Omkar Kolangade | 005612a | 2015-08-25 11:08:18 -0700 | [diff] [blame] | 1190 | } |
| 1191 | |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1192 | List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount( |
| 1193 | phoneAccountHandle, handle, VideoProfile.isVideo(videoState), initiatingUser); |
| 1194 | if (potentialPhoneAccounts.size() == 1) { |
| 1195 | phoneAccountHandle = potentialPhoneAccounts.get(0); |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 1196 | } else { |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1197 | phoneAccountHandle = null; |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1198 | } |
Nancy Chen | 1c5926f | 2014-09-17 14:44:14 -0700 | [diff] [blame] | 1199 | call.setTargetPhoneAccount(phoneAccountHandle); |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1200 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 1201 | boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1202 | |
| 1203 | // Do not support any more live calls. Our options are to move a call to hold, disconnect |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1204 | // 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1207 | 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 Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 1220 | } |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1221 | return null; |
| 1222 | } |
| 1223 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1224 | // The outgoing call can be placed, go forward. |
| 1225 | |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1226 | boolean needsAccountSelection = |
| 1227 | phoneAccountHandle == null && potentialPhoneAccounts.size() > 1 |
| 1228 | && !call.isEmergencyCall() && !isSelfManaged; |
Nancy Chen | 8d92f45 | 2014-10-20 21:43:44 -0700 | [diff] [blame] | 1229 | if (needsAccountSelection) { |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1230 | // This is the state where the user is expected to select an account |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 1231 | call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection"); |
Pawit Pornkitprasan | 1b2fd28 | 2015-03-15 08:33:32 +0700 | [diff] [blame] | 1232 | // Create our own instance to modify (since extras may be Bundle.EMPTY) |
| 1233 | extras = new Bundle(extras); |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1234 | extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, |
| 1235 | potentialPhoneAccounts); |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1236 | } else { |
Srikanth Chintala | 5b8facb | 2015-02-17 11:39:21 +0530 | [diff] [blame] | 1237 | 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 Pius | 781d025 | 2015-06-25 14:13:07 -0700 | [diff] [blame] | 1248 | call.setState( |
| 1249 | CallState.CONNECTING, |
| 1250 | phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString()); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1251 | |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1252 | 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 Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1255 | if (accountToUse != null |
| 1256 | && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { |
Hall Liu | 996ecdb | 2018-02-09 16:48:57 -0800 | [diff] [blame] | 1257 | call.createRttStreams(); |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1258 | } |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1259 | // 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 Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1262 | } |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1263 | } |
Sailesh Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 1264 | setIntentExtrasAndStartTime(call, extras); |
Nancy Chen | 1c5926f | 2014-09-17 14:44:14 -0700 | [diff] [blame] | 1265 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1266 | if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) { |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 1267 | // Do not add the call if it is a potential MMI code. |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 1268 | call.addListener(this); |
Santos Cordon | cf5b291 | 2014-11-05 21:57:54 -0800 | [diff] [blame] | 1269 | } else if (!mCalls.contains(call)) { |
| 1270 | // We check if mCalls already contains the call because we could potentially be reusing |
Brad Ebinger | b1e3d75 | 2015-11-09 17:54:17 -0800 | [diff] [blame] | 1271 | // a call which was previously added (See {@link #reuseOutgoingCall}). |
Tyler Gunn | 5b452df | 2014-10-01 15:02:58 -0700 | [diff] [blame] | 1272 | addCall(call); |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 1273 | } |
Nancy Chen | 0d3076c | 2014-07-30 14:45:44 -0700 | [diff] [blame] | 1274 | |
| 1275 | return call; |
| 1276 | } |
| 1277 | |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 1278 | /** |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1279 | * 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 Gunn | b8ce2aa | 2017-11-15 13:10:13 -0800 | [diff] [blame] | 1323 | } else { |
| 1324 | // The target phone account is valid and was found. |
| 1325 | return Arrays.asList(targetPhoneAccountHandle); |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1326 | } |
| 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 Gunn | cf21032 | 2018-01-23 09:18:24 -0800 | [diff] [blame] | 1350 | /** |
| 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 Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1357 | UserHandle initiatingUser) { |
| 1358 | PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount( |
| 1359 | targetPhoneAccountHandle, initiatingUser); |
| 1360 | return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged(); |
| 1361 | } |
| 1362 | |
| 1363 | /** |
Yorke Lee | 3350163 | 2014-03-17 19:24:12 -0700 | [diff] [blame] | 1364 | * Attempts to issue/connect the specified call. |
Santos Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 1365 | * |
Yorke Lee | 3350163 | 2014-03-17 19:24:12 -0700 | [diff] [blame] | 1366 | * @param handle Handle to connect the call with. |
Yorke Lee | 3350163 | 2014-03-17 19:24:12 -0700 | [diff] [blame] | 1367 | * @param gatewayInfo Optional gateway information that can be used to route the call to the |
Nancy Chen | 53ceedc | 2014-07-08 18:56:51 -0700 | [diff] [blame] | 1368 | * actual dialed handle via a gateway provider. May be null. |
Sai Cheemalapati | b7157e9 | 2014-06-11 17:51:55 -0700 | [diff] [blame] | 1369 | * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects. |
Tyler Gunn | c4abd91 | 2014-07-08 14:22:10 -0700 | [diff] [blame] | 1370 | * @param videoState The desired video state for the outgoing call. |
Santos Cordon | 8e8b8d2 | 2013-12-19 14:14:05 -0800 | [diff] [blame] | 1371 | */ |
Hall Liu | 220b419 | 2015-12-11 11:33:08 -0800 | [diff] [blame] | 1372 | @VisibleForTesting |
| 1373 | public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, |
| 1374 | boolean speakerphoneOn, int videoState) { |
Nancy Chen | 0d3076c | 2014-07-30 14:45:44 -0700 | [diff] [blame] | 1375 | if (call == null) { |
| 1376 | // don't do anything if the call no longer exists |
| 1377 | Log.i(this, "Canceling unknown call."); |
Santos Cordon | b7af09e | 2014-08-06 04:30:01 -0700 | [diff] [blame] | 1378 | return; |
| 1379 | } |
| 1380 | |
Nancy Chen | 201b437 | 2014-09-08 14:18:24 -0700 | [diff] [blame] | 1381 | final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); |
Yorke Lee | 3350163 | 2014-03-17 19:24:12 -0700 | [diff] [blame] | 1382 | |
| 1383 | if (gatewayInfo == null) { |
Santos Cordon | b58f453 | 2014-05-29 11:59:06 -0700 | [diff] [blame] | 1384 | Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); |
Yorke Lee | 3350163 | 2014-03-17 19:24:12 -0700 | [diff] [blame] | 1385 | } 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 Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 1389 | |
Nancy Chen | 0d3076c | 2014-07-30 14:45:44 -0700 | [diff] [blame] | 1390 | call.setHandle(uriHandle); |
| 1391 | call.setGatewayInfo(gatewayInfo); |
Tyler Gunn | b492f4c | 2015-12-15 08:15:43 -0800 | [diff] [blame] | 1392 | |
| 1393 | final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean( |
| 1394 | R.bool.use_speaker_when_docked); |
John Eckerdal | 6348600 | 2015-11-20 13:17:29 +0100 | [diff] [blame] | 1395 | final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock(); |
Hall Liu | 9696c21 | 2016-06-24 16:09:02 -0700 | [diff] [blame] | 1396 | final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState); |
Tyler Gunn | b492f4c | 2015-12-15 08:15:43 -0800 | [diff] [blame] | 1397 | |
| 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 Eckerdal | 6348600 | 2015-11-20 13:17:29 +0100 | [diff] [blame] | 1401 | || (useSpeakerWhenDocked && useSpeakerForDock)); |
Tyler Gunn | c4abd91 | 2014-07-08 14:22:10 -0700 | [diff] [blame] | 1402 | call.setVideoState(videoState); |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 1403 | |
Santos Cordon | a04bdca | 2015-03-04 16:57:55 -0800 | [diff] [blame] | 1404 | if (speakerphoneOn) { |
| 1405 | Log.i(this, "%s Starting with speakerphone as requested", call); |
John Eckerdal | 6348600 | 2015-11-20 13:17:29 +0100 | [diff] [blame] | 1406 | } else if (useSpeakerWhenDocked && useSpeakerForDock) { |
Santos Cordon | a04bdca | 2015-03-04 16:57:55 -0800 | [diff] [blame] | 1407 | Log.i(this, "%s Starting with speakerphone because car is docked.", call); |
Tyler Gunn | b492f4c | 2015-12-15 08:15:43 -0800 | [diff] [blame] | 1408 | } else if (useSpeakerForVideoCall) { |
| 1409 | Log.i(this, "%s Starting with speakerphone because its a video call.", call); |
Santos Cordon | a04bdca | 2015-03-04 16:57:55 -0800 | [diff] [blame] | 1410 | } |
Bryce Lee | 88f900a | 2015-11-06 08:51:22 -0800 | [diff] [blame] | 1411 | |
Tyler Gunn | 6ffe531 | 2015-08-12 08:19:20 -0700 | [diff] [blame] | 1412 | if (call.isEmergencyCall()) { |
Abhijith Shastry | cf27f68 | 2016-03-01 18:28:44 -0800 | [diff] [blame] | 1413 | new AsyncEmergencyContactNotifier(mContext).execute(); |
Nancy Chen | 53ceedc | 2014-07-08 18:56:51 -0700 | [diff] [blame] | 1414 | } |
Nancy Chen | d9de92c | 2014-07-21 16:09:13 -0700 | [diff] [blame] | 1415 | |
Bryce Lee | 6afefa4 | 2015-10-25 22:25:53 -0700 | [diff] [blame] | 1416 | final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean( |
| 1417 | com.android.internal.R.bool.config_requireCallCapableAccountForHandle); |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 1418 | final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call, |
| 1419 | call.getTargetPhoneAccount()); |
Tyler Gunn | 6ffe531 | 2015-08-12 08:19:20 -0700 | [diff] [blame] | 1420 | if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) { |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1421 | // 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 Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 1423 | if (call.isSelfManaged() && !isOutgoingCallPermitted) { |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 1424 | notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call); |
| 1425 | } else { |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 1426 | if (call.isEmergencyCall()) { |
Tyler Gunn | fe79113 | 2018-03-30 07:37:51 -0700 | [diff] [blame] | 1427 | // Drop any ongoing self-managed calls to make way for an emergency call. |
| 1428 | disconnectSelfManagedCalls("place emerg call" /* reason */); |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 1429 | } |
| 1430 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 1431 | call.startCreateConnection(mPhoneAccountRegistrar); |
| 1432 | } |
Bryce Lee | 6afefa4 | 2015-10-25 22:25:53 -0700 | [diff] [blame] | 1433 | } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts( |
Tony Mak | 240656f | 2015-12-04 11:36:22 +0000 | [diff] [blame] | 1434 | requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false, |
| 1435 | call.getInitiatingUser()).isEmpty()) { |
Bryce Lee | de0ac37 | 2015-10-23 16:06:00 -0700 | [diff] [blame] | 1436 | // 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 Chen | d9de92c | 2014-07-21 16:09:13 -0700 | [diff] [blame] | 1440 | } |
Yorke Lee | f98fb57 | 2014-03-05 10:56:55 -0800 | [diff] [blame] | 1441 | } |
| 1442 | |
| 1443 | /** |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 1444 | * Attempts to start a conference call for the specified call. |
| 1445 | * |
Santos Cordon | 12d6182 | 2014-07-29 16:02:20 -0700 | [diff] [blame] | 1446 | * @param call The call to conference. |
| 1447 | * @param otherCall The other call to conference with. |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 1448 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1449 | @VisibleForTesting |
| 1450 | public void conference(Call call, Call otherCall) { |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 1451 | call.conferenceWith(otherCall); |
Santos Cordon | a161070 | 2014-06-04 20:22:56 -0700 | [diff] [blame] | 1452 | } |
| 1453 | |
| 1454 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1455 | * 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 Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1457 | * the user opting to answer said call. |
Andrew Lee | 38931d0 | 2014-07-16 10:17:36 -0700 | [diff] [blame] | 1458 | * |
| 1459 | * @param call The call to answer. |
| 1460 | * @param videoState The video state in which to answer the call. |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1461 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1462 | @VisibleForTesting |
| 1463 | public void answerCall(Call call, int videoState) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1464 | if (!mCalls.contains(call)) { |
| 1465 | Log.i(this, "Request to answer a non-existent call %s", call); |
Santos Cordon | 61d0f70 | 2014-02-19 02:52:23 -0800 | [diff] [blame] | 1466 | } else { |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1467 | // 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 Meng | 2fc5527 | 2018-03-19 17:36:32 -0700 | [diff] [blame] | 1470 | holdActiveCallForNewCall(call); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1471 | mConnectionSvrFocusMgr.requestFocus( |
| 1472 | call, |
| 1473 | new RequestCallback(new ActionAnswerCall(call, videoState))); |
Santos Cordon | 61d0f70 | 2014-02-19 02:52:23 -0800 | [diff] [blame] | 1474 | } |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1475 | } |
| 1476 | |
Tyler Gunn | b360f0d | 2015-12-01 14:34:03 -0800 | [diff] [blame] | 1477 | /** |
Pooja Jain | ad4ebc0 | 2017-12-28 14:23:13 +0530 | [diff] [blame] | 1478 | * 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 Gunn | b360f0d | 2015-12-01 14:34:03 -0800 | [diff] [blame] | 1492 | * 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 Liu | 9696c21 | 2016-06-24 16:09:02 -0700 | [diff] [blame] | 1499 | public boolean isSpeakerphoneAutoEnabledForVideoCalls(int videoState) { |
Tyler Gunn | b360f0d | 2015-12-01 14:34:03 -0800 | [diff] [blame] | 1500 | return VideoProfile.isVideo(videoState) && |
| 1501 | !mWiredHeadsetManager.isPluggedIn() && |
Hall Liu | 486cb19 | 2016-10-21 18:23:33 -0700 | [diff] [blame] | 1502 | !mBluetoothRouteManager.isBluetoothAvailable() && |
Tyler Gunn | b360f0d | 2015-12-01 14:34:03 -0800 | [diff] [blame] | 1503 | isSpeakerEnabledForVideoCalls(); |
| 1504 | } |
| 1505 | |
| 1506 | /** |
John Eckerdal | 6348600 | 2015-11-20 13:17:29 +0100 | [diff] [blame] | 1507 | * 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 Liu | 486cb19 | 2016-10-21 18:23:33 -0700 | [diff] [blame] | 1516 | !mBluetoothRouteManager.isBluetoothAvailable(); |
John Eckerdal | 6348600 | 2015-11-20 13:17:29 +0100 | [diff] [blame] | 1517 | } |
| 1518 | |
| 1519 | /** |
Tyler Gunn | b360f0d | 2015-12-01 14:34:03 -0800 | [diff] [blame] | 1520 | * 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 Kumar | d240fe8 | 2015-04-01 21:45:57 -0700 | [diff] [blame] | 1524 | 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 Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1530 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1531 | * 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 Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1533 | * the user opting to reject said call. |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1534 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1535 | @VisibleForTesting |
| 1536 | public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1537 | if (!mCalls.contains(call)) { |
| 1538 | Log.i(this, "Request to reject a non-existent call %s", call); |
Santos Cordon | 61d0f70 | 2014-02-19 02:52:23 -0800 | [diff] [blame] | 1539 | } else { |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 1540 | for (CallsManagerListener listener : mListeners) { |
Ihab Awad | ff7493a | 2014-06-10 13:47:44 -0700 | [diff] [blame] | 1541 | listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 1542 | } |
Ihab Awad | ff7493a | 2014-06-10 13:47:44 -0700 | [diff] [blame] | 1543 | call.reject(rejectWithMessage, textMessage); |
Santos Cordon | 61d0f70 | 2014-02-19 02:52:23 -0800 | [diff] [blame] | 1544 | } |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1545 | } |
| 1546 | |
| 1547 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1548 | * Instructs Telecom to play the specified DTMF tone within the specified call. |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1549 | * |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1550 | * @param digit The DTMF digit to play. |
| 1551 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1552 | @VisibleForTesting |
| 1553 | public void playDtmfTone(Call call, char digit) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1554 | if (!mCalls.contains(call)) { |
| 1555 | Log.i(this, "Request to play DTMF in a non-existent call %s", call); |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1556 | } else { |
Tyler Gunn | ad97242 | 2017-11-13 13:36:35 -0800 | [diff] [blame] | 1557 | 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 Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1563 | } |
| 1564 | } |
| 1565 | |
| 1566 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1567 | * Instructs Telecom to stop the currently playing DTMF tone, if any. |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1568 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1569 | @VisibleForTesting |
| 1570 | public void stopDtmfTone(Call call) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1571 | if (!mCalls.contains(call)) { |
| 1572 | Log.i(this, "Request to stop DTMF in a non-existent call %s", call); |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1573 | } else { |
| 1574 | call.stopDtmfTone(); |
Santos Cordon | 92a2d81 | 2014-04-30 15:19:01 -0700 | [diff] [blame] | 1575 | mDtmfLocalTonePlayer.stopTone(call); |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1576 | } |
| 1577 | } |
| 1578 | |
| 1579 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1580 | * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any. |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1581 | */ |
Evan Charlton | 352105c | 2014-06-03 14:10:54 -0700 | [diff] [blame] | 1582 | void postDialContinue(Call call, boolean proceed) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1583 | if (!mCalls.contains(call)) { |
| 1584 | Log.i(this, "Request to continue post-dial string in a non-existent call %s", call); |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1585 | } else { |
Evan Charlton | 352105c | 2014-06-03 14:10:54 -0700 | [diff] [blame] | 1586 | call.postDialContinue(proceed); |
Ihab Awad | 74549ec | 2014-03-10 15:33:25 -0700 | [diff] [blame] | 1587 | } |
| 1588 | } |
| 1589 | |
| 1590 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1591 | * Instructs Telecom to disconnect the specified call. Intended to be invoked by the |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1592 | * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by |
| 1593 | * the user hitting the end-call button. |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1594 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1595 | @VisibleForTesting |
| 1596 | public void disconnectCall(Call call) { |
Santos Cordon | 682fe6b | 2014-05-20 08:56:39 -0700 | [diff] [blame] | 1597 | Log.v(this, "disconnectCall %s", call); |
| 1598 | |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1599 | if (!mCalls.contains(call)) { |
| 1600 | Log.w(this, "Unknown call (%s) asked to disconnect", call); |
Santos Cordon | 049b7b6 | 2014-01-30 05:34:26 -0800 | [diff] [blame] | 1601 | } else { |
Ihab Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 1602 | mLocallyDisconnectingCalls.add(call); |
Santos Cordon | 049b7b6 | 2014-01-30 05:34:26 -0800 | [diff] [blame] | 1603 | call.disconnect(); |
| 1604 | } |
Santos Cordon | e3d76ab | 2014-01-28 17:25:20 -0800 | [diff] [blame] | 1605 | } |
Santos Cordon | 681663d | 2014-01-30 04:32:15 -0800 | [diff] [blame] | 1606 | |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1607 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1608 | * Instructs Telecom to disconnect all calls. |
Tyler Gunn | 61b9210 | 2014-08-19 07:42:20 -0700 | [diff] [blame] | 1609 | */ |
| 1610 | void disconnectAllCalls() { |
| 1611 | Log.v(this, "disconnectAllCalls"); |
| 1612 | |
| 1613 | for (Call call : mCalls) { |
| 1614 | disconnectCall(call); |
| 1615 | } |
| 1616 | } |
| 1617 | |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 1618 | /** |
| 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 Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 1631 | |
Tyler Gunn | 61b9210 | 2014-08-19 07:42:20 -0700 | [diff] [blame] | 1632 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1633 | * Instructs Telecom to put the specified call on hold. Intended to be invoked by the |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1634 | * 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 Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1636 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1637 | @VisibleForTesting |
| 1638 | public void holdCall(Call call) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1639 | if (!mCalls.contains(call)) { |
| 1640 | Log.w(this, "Unknown call (%s) asked to be put on hold", call); |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1641 | } else { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1642 | Log.d(this, "Putting call on hold: (%s)", call); |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1643 | call.hold(); |
| 1644 | } |
| 1645 | } |
| 1646 | |
| 1647 | /** |
Tyler Gunn | 7cc70b4 | 2014-09-12 22:17:27 -0700 | [diff] [blame] | 1648 | * Instructs Telecom to release the specified call from hold. Intended to be invoked by |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1649 | * 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 Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1651 | */ |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 1652 | @VisibleForTesting |
| 1653 | public void unholdCall(Call call) { |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1654 | if (!mCalls.contains(call)) { |
| 1655 | Log.w(this, "Unknown call (%s) asked to be removed from hold", call); |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1656 | } else { |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1657 | Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1658 | String activeCallId = null; |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1659 | if (activeCall != null) { |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1660 | activeCallId = activeCall.getId(); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1661 | if (canHold(activeCall)) { |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1662 | 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1665 | } 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1670 | activeCall.disconnect("Swap to " + call.getId()); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1671 | } else { |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1672 | activeCall.hold("Swap to " + call.getId()); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1673 | } |
Sai Cheemalapati | 45b3e3f | 2014-08-15 09:33:00 -0700 | [diff] [blame] | 1674 | } |
| 1675 | } |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1676 | mConnectionSvrFocusMgr.requestFocus( |
| 1677 | call, |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1678 | new RequestCallback(new ActionUnHoldCall(call, activeCallId))); |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1679 | } |
| 1680 | } |
| 1681 | |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1682 | @Override |
Ravi Paluri | 6f4a715 | 2017-12-01 15:55:53 +0530 | [diff] [blame] | 1683 | 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 Gunn | 961694a | 2016-03-21 16:01:40 -0700 | [diff] [blame] | 1691 | public void onExtrasChanged(Call c, int source, Bundle extras) { |
| 1692 | if (source != Call.SOURCE_CONNECTION_SERVICE) { |
| 1693 | return; |
| 1694 | } |
| 1695 | handleCallTechnologyChange(c); |
Brad Ebinger | baf52ba | 2016-03-21 16:38:32 -0700 | [diff] [blame] | 1696 | handleChildAddressChange(c); |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 1697 | updateCanAddCall(); |
Tyler Gunn | 961694a | 2016-03-21 16:01:40 -0700 | [diff] [blame] | 1698 | } |
| 1699 | |
Brad Ebinger | 3da5764 | 2016-04-04 17:32:19 -0700 | [diff] [blame] | 1700 | // 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 Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1703 | @VisibleForTesting |
| 1704 | public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user, |
| 1705 | boolean isVideo) { |
Brad Ebinger | 3da5764 | 2016-04-04 17:32:19 -0700 | [diff] [blame] | 1706 | if (handle == null) { |
| 1707 | return Collections.emptyList(); |
| 1708 | } |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1709 | // If we're specifically looking for video capable accounts, then include that capability, |
| 1710 | // otherwise specify no additional capability constraints. |
Brad Ebinger | 3da5764 | 2016-04-04 17:32:19 -0700 | [diff] [blame] | 1711 | List<PhoneAccountHandle> allAccounts = |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 1712 | mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user, |
| 1713 | isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0 /* any */); |
Brad Ebinger | 3da5764 | 2016-04-04 17:32:19 -0700 | [diff] [blame] | 1714 | // 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 Gunn | 1a40c4f | 2016-04-14 14:29:45 -0700 | [diff] [blame] | 1743 | /** |
| 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 Gunn | 961694a | 2016-03-21 16:01:40 -0700 | [diff] [blame] | 1758 | private void handleCallTechnologyChange(Call call) { |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 1759 | 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 Ebinger | baf52ba | 2016-03-21 16:38:32 -0700 | [diff] [blame] | 1771 | 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 Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 1780 | /** Called by the in-call UI to change the mute state. */ |
| 1781 | void mute(boolean shouldMute) { |
Hall Liu | ccd793f | 2018-03-16 14:48:59 -0700 | [diff] [blame] | 1782 | if (hasEmergencyCall() && shouldMute) { |
| 1783 | Log.i(this, "Refusing to turn on mute because we're in an emergency call"); |
| 1784 | shouldMute = false; |
| 1785 | } |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 1786 | 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 Liu | 9086fb1 | 2017-11-07 18:01:53 -0800 | [diff] [blame] | 1793 | void setAudioRoute(int route, String bluetoothAddress) { |
Hall Liu | ccd793f | 2018-03-16 14:48:59 -0700 | [diff] [blame] | 1794 | 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 Liu | 9086fb1 | 2017-11-07 18:01:53 -0800 | [diff] [blame] | 1799 | mCallAudioManager.setAudioRoute(route, bluetoothAddress); |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 1800 | } |
| 1801 | |
Yorke Lee | d134687 | 2014-07-28 14:38:45 -0700 | [diff] [blame] | 1802 | /** 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 Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1816 | private boolean isRttSettingOn() { |
Hall Liu | 4eba63b | 2018-04-02 15:40:15 -0700 | [diff] [blame] | 1817 | return Settings.Secure.getInt(mContext.getContentResolver(), |
| 1818 | Settings.Secure.RTT_CALLING_MODE, 0) != 0; |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1819 | } |
| 1820 | |
Nancy Chen | f5e5d3c | 2014-10-21 18:45:56 -0700 | [diff] [blame] | 1821 | void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) { |
Nancy Chen | 53ceedc | 2014-07-08 18:56:51 -0700 | [diff] [blame] | 1822 | if (!mCalls.contains(call)) { |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1823 | Log.i(this, "Attempted to add account to unknown call %s", call); |
Nancy Chen | 53ceedc | 2014-07-08 18:56:51 -0700 | [diff] [blame] | 1824 | } else { |
Ihab Awad | b78b276 | 2014-07-25 15:16:23 -0700 | [diff] [blame] | 1825 | call.setTargetPhoneAccount(account); |
Srikanth Chintala | 5b8facb | 2015-02-17 11:39:21 +0530 | [diff] [blame] | 1826 | 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 Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1834 | if (isRttSettingOn() || call.getIntentExtras() |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1835 | .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1836 | Log.d(this, "Outgoing call after account selection requesting RTT," + |
| 1837 | " rtt setting is %b", isRttSettingOn()); |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1838 | if (realPhoneAccount != null |
| 1839 | && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { |
Hall Liu | 996ecdb | 2018-02-09 16:48:57 -0800 | [diff] [blame] | 1840 | call.createRttStreams(); |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1841 | } |
Hall Liu | fb8d5ab | 2018-01-29 17:34:54 -0800 | [diff] [blame] | 1842 | // 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 Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 1845 | } |
| 1846 | |
Hall Liu | 4b1759d | 2016-02-02 18:17:40 -0800 | [diff] [blame] | 1847 | if (!call.isNewOutgoingCallIntentBroadcastDone()) { |
| 1848 | return; |
| 1849 | } |
| 1850 | |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1851 | // Note: emergency calls never go through account selection dialog so they never |
| 1852 | // arrive here. |
| 1853 | if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) { |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 1854 | call.startCreateConnection(mPhoneAccountRegistrar); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1855 | } else { |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 1856 | call.disconnect("no room"); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1857 | } |
Nancy Chen | f5e5d3c | 2014-10-21 18:45:56 -0700 | [diff] [blame] | 1858 | |
| 1859 | if (setDefault) { |
Tony Mak | 4a3e2fd | 2015-12-04 11:58:38 +0000 | [diff] [blame] | 1860 | mPhoneAccountRegistrar |
| 1861 | .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser()); |
Nancy Chen | f5e5d3c | 2014-10-21 18:45:56 -0700 | [diff] [blame] | 1862 | } |
Nancy Chen | 53ceedc | 2014-07-08 18:56:51 -0700 | [diff] [blame] | 1863 | } |
| 1864 | } |
| 1865 | |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 1866 | /** Called when the audio state changes. */ |
Hall Liu | f62630a | 2015-10-27 14:53:39 -0700 | [diff] [blame] | 1867 | @VisibleForTesting |
| 1868 | public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState |
| 1869 | newAudioState) { |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 1870 | Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState); |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 1871 | for (CallsManagerListener listener : mListeners) { |
Yorke Lee | 2a66f7b | 2015-05-13 14:21:19 -0700 | [diff] [blame] | 1872 | listener.onCallAudioStateChanged(oldAudioState, newAudioState); |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 1873 | } |
Sailesh Nepal | 6aca10a | 2014-03-24 16:11:02 -0700 | [diff] [blame] | 1874 | } |
| 1875 | |
Honggang | b3ccbb3 | 2016-05-31 14:46:22 +0800 | [diff] [blame] | 1876 | /** |
| 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 Liu | 561cede | 2018-01-03 15:38:38 -0800 | [diff] [blame] | 1883 | @VisibleForTesting |
| 1884 | public void onDisconnectedTonePlaying(boolean isTonePlaying) { |
Honggang | b3ccbb3 | 2016-05-31 14:46:22 +0800 | [diff] [blame] | 1885 | Log.v(this, "onDisconnectedTonePlaying, %s", isTonePlaying ? "started" : "stopped"); |
| 1886 | for (CallsManagerListener listener : mListeners) { |
| 1887 | listener.onDisconnectedTonePlaying(isTonePlaying); |
| 1888 | } |
| 1889 | } |
| 1890 | |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1891 | void markCallAsRinging(Call call) { |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 1892 | setCallState(call, CallState.RINGING, "ringing set explicitly"); |
Santos Cordon | 681663d | 2014-01-30 04:32:15 -0800 | [diff] [blame] | 1893 | } |
| 1894 | |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 1895 | void markCallAsDialing(Call call) { |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 1896 | setCallState(call, CallState.DIALING, "dialing set explicitly"); |
Santos Cordon | d6782cd | 2015-04-16 09:57:50 -0700 | [diff] [blame] | 1897 | maybeMoveToSpeakerPhone(call); |
Omata Shou | 6bc865c | 2017-07-26 13:39:27 +0900 | [diff] [blame] | 1898 | maybeTurnOffMute(call); |
Santos Cordon | 681663d | 2014-01-30 04:32:15 -0800 | [diff] [blame] | 1899 | } |
| 1900 | |
Tyler Gunn | 1e37be5 | 2016-07-11 08:54:23 -0700 | [diff] [blame] | 1901 | void markCallAsPulling(Call call) { |
| 1902 | setCallState(call, CallState.PULLING, "pulling set explicitly"); |
| 1903 | maybeMoveToSpeakerPhone(call); |
| 1904 | } |
| 1905 | |
Pengquan Meng | 2fc5527 | 2018-03-19 17:36:32 -0700 | [diff] [blame] | 1906 | /** |
| 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1937 | if (call.isSelfManaged()) { |
| 1938 | // backward compatibility, the self-managed connection service will set the call state |
Pengquan Meng | 2fc5527 | 2018-03-19 17:36:32 -0700 | [diff] [blame] | 1939 | // 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 1943 | 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 Cordon | 681663d | 2014-01-30 04:32:15 -0800 | [diff] [blame] | 1953 | } |
| 1954 | |
Tyler Gunn | ad97242 | 2017-11-13 13:36:35 -0800 | [diff] [blame] | 1955 | @VisibleForTesting |
| 1956 | public void markCallAsOnHold(Call call) { |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 1957 | setCallState(call, CallState.ON_HOLD, "on-hold set explicitly"); |
Yorke Lee | cdf3ebd | 2014-03-12 18:31:41 -0700 | [diff] [blame] | 1958 | } |
| 1959 | |
Santos Cordon | 049b7b6 | 2014-01-30 05:34:26 -0800 | [diff] [blame] | 1960 | /** |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 1961 | * 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 Cordon | 049b7b6 | 2014-01-30 05:34:26 -0800 | [diff] [blame] | 1963 | * |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 1964 | * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}. |
Santos Cordon | 049b7b6 | 2014-01-30 05:34:26 -0800 | [diff] [blame] | 1965 | */ |
Andrew Lee | 701dc00 | 2014-09-11 21:29:12 -0700 | [diff] [blame] | 1966 | void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { |
| 1967 | call.setDisconnectCause(disconnectCause); |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 1968 | setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly"); |
Sailesh Nepal | 6ab6fb7 | 2014-04-01 20:03:19 -0700 | [diff] [blame] | 1969 | } |
| 1970 | |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 1971 | /** |
Ihab Awad | a02bef5 | 2014-08-13 18:18:42 -0700 | [diff] [blame] | 1972 | * Removes an existing disconnected call, and notifies the in-call app. |
| 1973 | */ |
| 1974 | void markCallAsRemoved(Call call) { |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 1975 | call.maybeCleanupHandover(); |
Ihab Awad | a02bef5 | 2014-08-13 18:18:42 -0700 | [diff] [blame] | 1976 | removeCall(call); |
Tyler Gunn | 7d58ba5 | 2016-07-25 15:05:48 -0700 | [diff] [blame] | 1977 | Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); |
Ihab Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 1978 | if (mLocallyDisconnectingCalls.contains(call)) { |
Srikanth Chintala | 5bf7502 | 2017-02-03 10:10:35 +0530 | [diff] [blame] | 1979 | boolean isDisconnectingChildCall = call.isDisconnectingChildCall(); |
| 1980 | Log.v(this, "markCallAsRemoved: isDisconnectingChildCall = " |
| 1981 | + isDisconnectingChildCall + "call -> %s", call); |
Ihab Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 1982 | mLocallyDisconnectingCalls.remove(call); |
Srikanth Chintala | 5bf7502 | 2017-02-03 10:10:35 +0530 | [diff] [blame] | 1983 | // 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 Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 1988 | foregroundCall.unhold(); |
Ihab Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 1989 | } |
Tyler Gunn | 7d58ba5 | 2016-07-25 15:05:48 -0700 | [diff] [blame] | 1990 | } 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 Awad | 5b28132 | 2014-09-25 18:25:29 -0700 | [diff] [blame] | 1999 | } |
Ihab Awad | a02bef5 | 2014-08-13 18:18:42 -0700 | [diff] [blame] | 2000 | } |
| 2001 | |
| 2002 | /** |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 2003 | * 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 Nepal | c92c436 | 2014-07-04 18:33:21 -0700 | [diff] [blame] | 2030 | * Cleans up any calls currently associated with the specified connection service when the |
Sailesh Nepal | 905dfba | 2014-07-14 08:20:41 -0700 | [diff] [blame] | 2031 | * service binder disconnects unexpectedly. |
Santos Cordon | 4b2c119 | 2014-03-19 18:15:38 -0700 | [diff] [blame] | 2032 | * |
Sailesh Nepal | c92c436 | 2014-07-04 18:33:21 -0700 | [diff] [blame] | 2033 | * @param service The connection service that disconnected. |
Santos Cordon | 4b2c119 | 2014-03-19 18:15:38 -0700 | [diff] [blame] | 2034 | */ |
Sailesh Nepal | c92c436 | 2014-07-04 18:33:21 -0700 | [diff] [blame] | 2035 | void handleConnectionServiceDeath(ConnectionServiceWrapper service) { |
Jay Shrauner | acb91eb | 2014-08-08 16:04:53 -0700 | [diff] [blame] | 2036 | if (service != null) { |
Tyler Gunn | 1a97fb2 | 2017-06-21 19:55:54 -0700 | [diff] [blame] | 2037 | Log.i(this, "handleConnectionServiceDeath: service %s died", service); |
Jay Shrauner | a82c8f7 | 2014-08-14 15:49:16 -0700 | [diff] [blame] | 2038 | for (Call call : mCalls) { |
Jay Shrauner | acb91eb | 2014-08-08 16:04:53 -0700 | [diff] [blame] | 2039 | if (call.getConnectionService() == service) { |
Yorke Lee | 2af16b6 | 2014-11-11 17:36:57 -0800 | [diff] [blame] | 2040 | if (call.getState() != CallState.DISCONNECTED) { |
Tyler Gunn | 1a97fb2 | 2017-06-21 19:55:54 -0700 | [diff] [blame] | 2041 | markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR, |
| 2042 | "CS_DEATH")); |
Yorke Lee | 2af16b6 | 2014-11-11 17:36:57 -0800 | [diff] [blame] | 2043 | } |
| 2044 | markCallAsRemoved(call); |
Jay Shrauner | acb91eb | 2014-08-08 16:04:53 -0700 | [diff] [blame] | 2045 | } |
Santos Cordon | 4b2c119 | 2014-03-19 18:15:38 -0700 | [diff] [blame] | 2046 | } |
| 2047 | } |
| 2048 | } |
| 2049 | |
Tyler Gunn | 1a40c4f | 2016-04-14 14:29:45 -0700 | [diff] [blame] | 2050 | /** |
| 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 Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2055 | boolean hasAnyCalls() { |
Tyler Gunn | 1a40c4f | 2016-04-14 14:29:45 -0700 | [diff] [blame] | 2056 | 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 Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2066 | } |
| 2067 | |
Santos Cordon | c7e85d4 | 2014-05-22 02:51:48 -0700 | [diff] [blame] | 2068 | boolean hasActiveOrHoldingCall() { |
Santos Cordon | 23baed3 | 2014-06-27 14:45:39 -0700 | [diff] [blame] | 2069 | return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null; |
Santos Cordon | c7e85d4 | 2014-05-22 02:51:48 -0700 | [diff] [blame] | 2070 | } |
| 2071 | |
| 2072 | boolean hasRingingCall() { |
Santos Cordon | 23baed3 | 2014-06-27 14:45:39 -0700 | [diff] [blame] | 2073 | return getFirstCallWithState(CallState.RINGING) != null; |
Santos Cordon | c7e85d4 | 2014-05-22 02:51:48 -0700 | [diff] [blame] | 2074 | } |
| 2075 | |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2076 | boolean onMediaButton(int type) { |
| 2077 | if (hasAnyCalls()) { |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2078 | Call ringingCall = getFirstCallWithState(CallState.RINGING); |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2079 | if (HeadsetMediaButton.SHORT_PRESS == type) { |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2080 | if (ringingCall == null) { |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2081 | Call callToHangup = getFirstCallWithState(CallState.RINGING, CallState.DIALING, |
| 2082 | CallState.PULLING, CallState.ACTIVE, CallState.ON_HOLD); |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2083 | Log.addEvent(callToHangup, LogUtils.Events.INFO, |
| 2084 | "media btn short press - end call."); |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2085 | if (callToHangup != null) { |
Srikanth Chintala | 29644dc | 2017-07-19 10:27:11 +0530 | [diff] [blame] | 2086 | disconnectCall(callToHangup); |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2087 | return true; |
| 2088 | } |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2089 | } else { |
Hall Liu | 3777a7e | 2016-07-25 14:46:48 -0700 | [diff] [blame] | 2090 | ringingCall.answer(VideoProfile.STATE_AUDIO_ONLY); |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2091 | return true; |
| 2092 | } |
| 2093 | } else if (HeadsetMediaButton.LONG_PRESS == type) { |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2094 | if (ringingCall != null) { |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2095 | Log.addEvent(getForegroundCall(), |
| 2096 | LogUtils.Events.INFO, "media btn long press - reject"); |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2097 | ringingCall.reject(false, null); |
| 2098 | } else { |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2099 | Log.addEvent(getForegroundCall(), LogUtils.Events.INFO, |
| 2100 | "media btn long press - mute"); |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2101 | mCallAudioManager.toggleMute(); |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2102 | } |
yifan.bai | c73ca8d | 2016-04-21 16:19:12 +0800 | [diff] [blame] | 2103 | return true; |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2104 | } |
| 2105 | } |
| 2106 | return false; |
| 2107 | } |
| 2108 | |
| 2109 | /** |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2110 | * Returns true if telecom supports adding another top-level call. |
Santos Cordon | 10838c2 | 2014-06-11 17:36:04 -0700 | [diff] [blame] | 2111 | */ |
Hall Liu | 28b82f0 | 2016-07-26 17:38:56 -0700 | [diff] [blame] | 2112 | @VisibleForTesting |
| 2113 | public boolean canAddCall() { |
Hall Liu | f1422e7 | 2016-01-27 11:07:07 -0800 | [diff] [blame] | 2114 | 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 Lee | 113ad62 | 2014-12-01 18:33:10 -0800 | [diff] [blame] | 2121 | if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) { |
| 2122 | return false; |
| 2123 | } |
| 2124 | |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2125 | 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 Gunn | 1e37be5 | 2016-07-11 08:54:23 -0700 | [diff] [blame] | 2130 | } else if (call.isExternalCall()) { |
| 2131 | // External calls don't count. |
| 2132 | continue; |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2133 | } else if (call.getParentCall() == null) { |
| 2134 | count++; |
| 2135 | } |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 2136 | Bundle extras = call.getExtras(); |
| 2137 | if (extras != null) { |
Hall Liu | 140c840 | 2016-07-06 16:30:46 -0700 | [diff] [blame] | 2138 | if (extras.getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) { |
| 2139 | return false; |
| 2140 | } |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 2141 | } |
Santos Cordon | 10838c2 | 2014-06-11 17:36:04 -0700 | [diff] [blame] | 2142 | |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2143 | // 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 Cordon | 10838c2 | 2014-06-11 17:36:04 -0700 | [diff] [blame] | 2149 | return false; |
| 2150 | } |
| 2151 | } |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 2152 | |
Santos Cordon | 10838c2 | 2014-06-11 17:36:04 -0700 | [diff] [blame] | 2153 | return true; |
| 2154 | } |
| 2155 | |
Ihab Awad | a365390 | 2015-01-23 13:44:46 -0800 | [diff] [blame] | 2156 | @VisibleForTesting |
P.Y. Laligand | d35be75 | 2015-03-06 14:12:43 -0800 | [diff] [blame] | 2157 | public Call getRingingCall() { |
Santos Cordon | 68d1a6b | 2014-09-19 12:25:58 -0700 | [diff] [blame] | 2158 | return getFirstCallWithState(CallState.RINGING); |
| 2159 | } |
| 2160 | |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 2161 | public Call getActiveCall() { |
Santos Cordon | 68d1a6b | 2014-09-19 12:25:58 -0700 | [diff] [blame] | 2162 | return getFirstCallWithState(CallState.ACTIVE); |
| 2163 | } |
| 2164 | |
Nancy Chen | 05a9e40 | 2014-09-26 14:14:32 -0700 | [diff] [blame] | 2165 | Call getDialingCall() { |
| 2166 | return getFirstCallWithState(CallState.DIALING); |
Santos Cordon | 68d1a6b | 2014-09-19 12:25:58 -0700 | [diff] [blame] | 2167 | } |
| 2168 | |
Brad Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 2169 | @VisibleForTesting |
| 2170 | public Call getHeldCall() { |
Santos Cordon | 68d1a6b | 2014-09-19 12:25:58 -0700 | [diff] [blame] | 2171 | return getFirstCallWithState(CallState.ON_HOLD); |
| 2172 | } |
| 2173 | |
Pengquan Meng | 2fc5527 | 2018-03-19 17:36:32 -0700 | [diff] [blame] | 2174 | 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 Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 2182 | @VisibleForTesting |
| 2183 | public int getNumHeldCalls() { |
Santos Cordon | c0ca76e | 2014-10-21 15:54:19 -0700 | [diff] [blame] | 2184 | 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 Ebinger | 5385513 | 2015-10-30 10:58:19 -0700 | [diff] [blame] | 2193 | @VisibleForTesting |
| 2194 | public Call getOutgoingCall() { |
Roshan Pius | 7d7cf27 | 2015-08-28 11:10:56 -0700 | [diff] [blame] | 2195 | return getFirstCallWithState(OUTGOING_CALL_STATES); |
| 2196 | } |
| 2197 | |
Hall Liu | 0a6dd30 | 2015-12-16 15:06:49 -0800 | [diff] [blame] | 2198 | @VisibleForTesting |
| 2199 | public Call getFirstCallWithState(int... states) { |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2200 | return getFirstCallWithState(null, states); |
| 2201 | } |
| 2202 | |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 2203 | @VisibleForTesting |
| 2204 | public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() { |
| 2205 | return mPhoneNumberUtilsAdapter; |
| 2206 | } |
| 2207 | |
Santos Cordon | 10838c2 | 2014-06-11 17:36:04 -0700 | [diff] [blame] | 2208 | /** |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2209 | * 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 Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2212 | * |
| 2213 | * @param callToSkip Call that this method should skip while searching |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2214 | */ |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2215 | Call getFirstCallWithState(Call callToSkip, int... states) { |
Ihab Awad | 6fb37c8 | 2014-08-07 19:48:57 -0700 | [diff] [blame] | 2216 | for (int currentState : states) { |
Santos Cordon | 23baed3 | 2014-06-27 14:45:39 -0700 | [diff] [blame] | 2217 | // check the foreground first |
Hall Liu | e091ab9 | 2015-12-18 17:05:30 -0800 | [diff] [blame] | 2218 | Call foregroundCall = getForegroundCall(); |
| 2219 | if (foregroundCall != null && foregroundCall.getState() == currentState) { |
| 2220 | return foregroundCall; |
Santos Cordon | 23baed3 | 2014-06-27 14:45:39 -0700 | [diff] [blame] | 2221 | } |
| 2222 | |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2223 | for (Call call : mCalls) { |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2224 | 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 Gunn | f15dc33 | 2016-06-07 16:01:41 -0700 | [diff] [blame] | 2233 | if (call.isExternalCall()) { |
| 2234 | continue; |
| 2235 | } |
| 2236 | |
Santos Cordon | deb8c89 | 2014-05-30 01:38:03 -0700 | [diff] [blame] | 2237 | if (currentState == call.getState()) { |
| 2238 | return call; |
| 2239 | } |
| 2240 | } |
| 2241 | } |
| 2242 | return null; |
| 2243 | } |
| 2244 | |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2245 | Call createConferenceCall( |
Tyler Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 2246 | String callId, |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2247 | PhoneAccountHandle phoneAccount, |
| 2248 | ParcelableConference parcelableConference) { |
Tyler Gunn | d92e737 | 2015-01-09 15:59:45 -0800 | [diff] [blame] | 2249 | |
| 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 Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 2256 | long connectElapsedTime = |
| 2257 | parcelableConference.getConnectElapsedTimeMillis() == |
| 2258 | Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : |
| 2259 | parcelableConference.getConnectElapsedTimeMillis(); |
Tyler Gunn | d92e737 | 2015-01-09 15:59:45 -0800 | [diff] [blame] | 2260 | |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2261 | Call call = new Call( |
Tyler Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 2262 | callId, |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 2263 | mContext, |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 2264 | this, |
Ihab Awad | e6dbc9d | 2015-03-26 10:33:44 -0700 | [diff] [blame] | 2265 | mLock, |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2266 | mConnectionServiceRepository, |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 2267 | mContactsAsyncHelper, |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 2268 | mCallerInfoAsyncQueryFactory, |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 2269 | mPhoneNumberUtilsAdapter, |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2270 | null /* handle */, |
| 2271 | null /* gatewayInfo */, |
| 2272 | null /* connectionManagerPhoneAccount */, |
| 2273 | phoneAccount, |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2274 | Call.CALL_DIRECTION_UNDEFINED /* callDirection */, |
| 2275 | false /* forceAttachToExistingConnection */, |
Tyler Gunn | d92e737 | 2015-01-09 15:59:45 -0800 | [diff] [blame] | 2276 | true /* isConference */, |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 2277 | connectTime, |
| 2278 | connectElapsedTime, |
| 2279 | mClockProxy); |
Santos Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2280 | |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 2281 | setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()), |
| 2282 | "new conference call"); |
Ihab Awad | 07bc5ee | 2014-11-12 13:42:52 -0800 | [diff] [blame] | 2283 | call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities()); |
Tyler Gunn | 571d5e6 | 2016-03-15 15:55:18 -0700 | [diff] [blame] | 2284 | call.setConnectionProperties(parcelableConference.getConnectionProperties()); |
Tyler Gunn | 13e8a69 | 2016-08-03 18:38:00 -0700 | [diff] [blame] | 2285 | call.setVideoState(parcelableConference.getVideoState()); |
Rekha Kumar | d240fe8 | 2015-04-01 21:45:57 -0700 | [diff] [blame] | 2286 | call.setVideoProvider(parcelableConference.getVideoProvider()); |
Andrew Lee | 57412d3 | 2015-04-14 13:38:44 -0700 | [diff] [blame] | 2287 | call.setStatusHints(parcelableConference.getStatusHints()); |
Tyler Gunn | 961694a | 2016-03-21 16:01:40 -0700 | [diff] [blame] | 2288 | call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras()); |
Tyler Gunn | 9b618b8 | 2016-10-17 15:54:35 -0700 | [diff] [blame] | 2289 | // 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 Cordon | 0fbe632 | 2014-08-14 04:04:25 -0700 | [diff] [blame] | 2295 | |
| 2296 | // TODO: Move this to be a part of addCall() |
| 2297 | call.addListener(this); |
| 2298 | addCall(call); |
| 2299 | return call; |
| 2300 | } |
| 2301 | |
Yorke Lee | f86db2e | 2014-09-12 17:56:48 -0700 | [diff] [blame] | 2302 | /** |
| 2303 | * @return the call state currently tracked by {@link PhoneStateBroadcaster} |
| 2304 | */ |
| 2305 | int getCallState() { |
| 2306 | return mPhoneStateBroadcaster.getCallState(); |
| 2307 | } |
Nancy Chen | bc9ef17 | 2014-08-15 17:11:57 -0700 | [diff] [blame] | 2308 | |
Santos Cordon | 4b2c119 | 2014-03-19 18:15:38 -0700 | [diff] [blame] | 2309 | /** |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 2310 | * 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 Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2327 | * Retrieves the {@link IncomingCallNotifier}. |
| 2328 | * @return The {@link IncomingCallNotifier}. |
| 2329 | */ |
| 2330 | IncomingCallNotifier getIncomingCallNotifier() { |
| 2331 | return mIncomingCallNotifier; |
| 2332 | } |
| 2333 | |
| 2334 | /** |
Brad Ebinger | 3165d50 | 2015-12-15 17:22:29 -0800 | [diff] [blame] | 2335 | * 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 Gunn | 165ee75 | 2016-09-06 13:40:10 -0700 | [diff] [blame] | 2339 | 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 Ebinger | 3165d50 | 2015-12-15 17:22:29 -0800 | [diff] [blame] | 2349 | // 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 Yen | 982c0bd | 2016-04-14 13:59:54 -0700 | [diff] [blame] | 2352 | mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE, |
| 2353 | true /*showNotificationForMissedCall*/); |
Brad Ebinger | 3165d50 | 2015-12-15 17:22:29 -0800 | [diff] [blame] | 2354 | } |
| 2355 | |
| 2356 | /** |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 2357 | * Adds the specified call to the main list of live calls. |
| 2358 | * |
| 2359 | * @param call The call to add. |
| 2360 | */ |
Tyler Gunn | c74b3e2 | 2017-11-07 15:03:24 -0800 | [diff] [blame] | 2361 | @VisibleForTesting |
| 2362 | public void addCall(Call call) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2363 | Trace.beginSection("addCall"); |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 2364 | Log.v(this, "addCall(%s)", call); |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 2365 | call.addListener(this); |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2366 | mCalls.add(call); |
Santos Cordon | 40f78c2 | 2014-04-07 02:11:42 -0700 | [diff] [blame] | 2367 | |
Sailesh Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 2368 | // 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 Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 2374 | updateCanAddCall(); |
Santos Cordon | 40f78c2 | 2014-04-07 02:11:42 -0700 | [diff] [blame] | 2375 | // onCallAdded for calls which immediately take the foreground (like the first call). |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2376 | for (CallsManagerListener listener : mListeners) { |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2377 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2378 | Trace.beginSection(listener.getClass().toString() + " addCall"); |
| 2379 | } |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2380 | listener.onCallAdded(call); |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2381 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2382 | Trace.endSection(); |
| 2383 | } |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2384 | } |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2385 | Trace.endSection(); |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 2386 | } |
| 2387 | |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2388 | private void removeCall(Call call) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2389 | Trace.beginSection("removeCall"); |
Santos Cordon | c499c1c | 2014-04-14 17:13:14 -0700 | [diff] [blame] | 2390 | Log.v(this, "removeCall(%s)", call); |
Sailesh Nepal | 4857f47 | 2014-04-07 22:26:27 -0700 | [diff] [blame] | 2391 | |
Tyler Gunn | 7658171 | 2017-05-09 14:39:42 -0700 | [diff] [blame] | 2392 | call.setParentAndChildCall(null); // clean up parent relationship before destroying. |
Santos Cordon | 766d04f | 2014-05-06 10:28:25 -0700 | [diff] [blame] | 2393 | call.removeListener(this); |
Sailesh Nepal | c92c436 | 2014-07-04 18:33:21 -0700 | [diff] [blame] | 2394 | call.clearConnectionService(); |
Hall Liu | dd68bc3 | 2017-01-25 17:14:23 -0800 | [diff] [blame] | 2395 | // TODO: clean up RTT pipes |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2396 | |
| 2397 | boolean shouldNotify = false; |
| 2398 | if (mCalls.contains(call)) { |
| 2399 | mCalls.remove(call); |
| 2400 | shouldNotify = true; |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2401 | } |
Ben Gilad | a0d9f75 | 2014-02-26 11:49:03 -0800 | [diff] [blame] | 2402 | |
Santos Cordon | 2685dab | 2015-04-17 17:12:09 -0700 | [diff] [blame] | 2403 | call.destroy(); |
| 2404 | |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2405 | // Only broadcast changes for calls that are being tracked. |
| 2406 | if (shouldNotify) { |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 2407 | updateCanAddCall(); |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2408 | for (CallsManagerListener listener : mListeners) { |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2409 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2410 | Trace.beginSection(listener.getClass().toString() + " onCallRemoved"); |
| 2411 | } |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2412 | listener.onCallRemoved(call); |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2413 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2414 | Trace.endSection(); |
| 2415 | } |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2416 | } |
Yorke Lee | 604a90f | 2014-10-20 12:13:32 -0700 | [diff] [blame] | 2417 | } |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2418 | Trace.endSection(); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2419 | } |
Evan Charlton | 5c670a9 | 2014-03-06 14:58:20 -0800 | [diff] [blame] | 2420 | |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2421 | /** |
Santos Cordon | 1ae2b85 | 2014-03-19 03:03:10 -0700 | [diff] [blame] | 2422 | * Sets the specified state on the specified call. |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2423 | * |
| 2424 | * @param call The call. |
| 2425 | * @param newState The new state of the call. |
| 2426 | */ |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 2427 | private void setCallState(Call call, int newState, String tag) { |
Jay Shrauner | acb91eb | 2014-08-08 16:04:53 -0700 | [diff] [blame] | 2428 | if (call == null) { |
| 2429 | return; |
| 2430 | } |
Ihab Awad | 6fb37c8 | 2014-08-07 19:48:57 -0700 | [diff] [blame] | 2431 | int oldState = call.getState(); |
Nancy Chen | 308ab8b | 2014-09-02 16:18:30 -0700 | [diff] [blame] | 2432 | Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState), |
| 2433 | CallState.toString(newState), call); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2434 | if (newState != oldState) { |
Tyler Gunn | ad97242 | 2017-11-13 13:36:35 -0800 | [diff] [blame] | 2435 | // 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 Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2441 | // 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 Awad | 6fb37c8 | 2014-08-07 19:48:57 -0700 | [diff] [blame] | 2443 | // sense (e.g., STATE_ACTIVE -> STATE_RINGING). |
Santos Cordon | df39986 | 2014-08-06 04:39:15 -0700 | [diff] [blame] | 2444 | // TODO: Consider putting a stop to the above and turning CallState |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2445 | // into a well-defined state machine. |
Santos Cordon | df39986 | 2014-08-06 04:39:15 -0700 | [diff] [blame] | 2446 | // TODO: Define expected state transitions here, and log when an |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2447 | // unexpected transition occurs. |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 2448 | call.setState(newState, tag); |
Xueren Zhang | c0e8e9d | 2015-03-06 14:16:55 +0100 | [diff] [blame] | 2449 | maybeShowErrorDialogOnDisconnect(call); |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2450 | |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2451 | Trace.beginSection("onCallStateChanged"); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 2452 | |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2453 | maybeHandleHandover(call, newState); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 2454 | |
Sailesh Nepal | 810735e | 2014-03-18 18:15:46 -0700 | [diff] [blame] | 2455 | // Only broadcast state change for calls that are being tracked. |
Sailesh Nepal | e59bb19 | 2014-04-01 18:33:59 -0700 | [diff] [blame] | 2456 | if (mCalls.contains(call)) { |
Hall Liu | fc130b2 | 2016-06-15 17:54:48 -0700 | [diff] [blame] | 2457 | updateCanAddCall(); |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2458 | for (CallsManagerListener listener : mListeners) { |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2459 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2460 | Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); |
| 2461 | } |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2462 | listener.onCallStateChanged(call, oldState, newState); |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2463 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2464 | Trace.endSection(); |
| 2465 | } |
Santos Cordon | a56f276 | 2014-03-24 15:55:53 -0700 | [diff] [blame] | 2466 | } |
Santos Cordon | 4e9fffe | 2014-03-04 18:13:41 -0800 | [diff] [blame] | 2467 | } |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2468 | Trace.endSection(); |
Santos Cordon | 4e9fffe | 2014-03-04 18:13:41 -0800 | [diff] [blame] | 2469 | } |
| 2470 | } |
| 2471 | |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2472 | /** |
| 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 Gunn | 845a677 | 2017-06-23 08:30:10 -0700 | [diff] [blame] | 2509 | // 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2513 | } 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 Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 2531 | call.onHandoverComplete(); |
| 2532 | |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2533 | // Inform the "to" ConnectionService that handover to it has completed. |
| 2534 | handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); |
Tyler Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 2535 | handoverTo.onHandoverComplete(); |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2536 | answerCall(handoverTo, handoverTo.getVideoState()); |
| 2537 | call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); |
Tyler Gunn | 632547e | 2017-08-22 15:40:38 -0700 | [diff] [blame] | 2538 | |
| 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2544 | } |
| 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 2549 | Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected", |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2550 | handoverTo.getId(), handoverFrom.getId()); |
Tyler Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 2551 | Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected", |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2552 | 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 2558 | handoverFrom.onHandoverFailed(android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED); |
| 2559 | |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2560 | // 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 2567 | handoverTo.getConnectionService().handoverFailed(handoverTo, |
| 2568 | android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED); |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2569 | } |
| 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 2577 | handoverTo.onHandoverComplete(); |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2578 | handoverFrom.setHandoverState(HandoverState.HANDOVER_ACCEPTED); |
Tyler Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 2579 | handoverFrom.onHandoverComplete(); |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2580 | |
| 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 Gunn | 632547e | 2017-08-22 15:40:38 -0700 | [diff] [blame] | 2588 | // 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2593 | } |
| 2594 | |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2595 | private void updateCanAddCall() { |
| 2596 | boolean newCanAddCall = canAddCall(); |
| 2597 | if (newCanAddCall != mCanAddCall) { |
| 2598 | mCanAddCall = newCanAddCall; |
| 2599 | for (CallsManagerListener listener : mListeners) { |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2600 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2601 | Trace.beginSection(listener.getClass().toString() + " updateCanAddCall"); |
| 2602 | } |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2603 | listener.onCanAddCallChanged(mCanAddCall); |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 2604 | if (LogUtils.SYSTRACE_DEBUG) { |
Yorke Lee | e4a9c41 | 2014-11-14 16:59:42 -0800 | [diff] [blame] | 2605 | Trace.endSection(); |
| 2606 | } |
Santos Cordon | 91bd74c | 2014-11-07 16:10:22 -0800 | [diff] [blame] | 2607 | } |
| 2608 | } |
| 2609 | } |
| 2610 | |
Yorke Lee | b701f6d | 2014-08-12 14:04:19 -0700 | [diff] [blame] | 2611 | private boolean isPotentialMMICode(Uri handle) { |
| 2612 | return (handle != null && handle.getSchemeSpecificPart() != null |
| 2613 | && handle.getSchemeSpecificPart().contains("#")); |
| 2614 | } |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2615 | |
Tyler Gunn | 5b452df | 2014-10-01 15:02:58 -0700 | [diff] [blame] | 2616 | /** |
| 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 Mahajan | 8c4e22a | 2015-12-14 13:24:15 -0800 | [diff] [blame] | 2621 | * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)} |
Tyler Gunn | 5b452df | 2014-10-01 15:02:58 -0700 | [diff] [blame] | 2622 | * |
| 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 Zhang | c0e8e9d | 2015-03-06 14:16:55 +0100 | [diff] [blame] | 2628 | handle.getScheme() != null && |
Tyler Gunn | 5b452df | 2014-10-01 15:02:58 -0700 | [diff] [blame] | 2629 | 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 Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2642 | @VisibleForTesting |
| 2643 | public int getNumCallsWithState(final boolean isSelfManaged, Call excludeCall, |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2644 | 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 Gunn | f15dc33 | 2016-06-07 16:01:41 -0700 | [diff] [blame] | 2664 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2665 | 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 Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2669 | call.getParentCall() == null && !call.isExternalCall()); |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2670 | |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2671 | 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 Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2678 | if (excludeCall != null) { |
| 2679 | callsStream = callsStream.filter(call -> call != excludeCall); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2680 | } |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2681 | |
| 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 Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2689 | } |
| 2690 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2691 | private boolean hasMaximumLiveCalls(Call exceptCall) { |
| 2692 | return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL, |
| 2693 | exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES); |
| 2694 | } |
| 2695 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2696 | private boolean hasMaximumManagedLiveCalls(Call exceptCall) { |
| 2697 | return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(false /* isSelfManaged */, |
| 2698 | exceptCall, null /* phoneAccountHandle */, LIVE_CALL_STATES); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2699 | } |
| 2700 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2701 | private boolean hasMaximumSelfManagedCalls(Call exceptCall, |
| 2702 | PhoneAccountHandle phoneAccountHandle) { |
| 2703 | return MAXIMUM_SELF_MANAGED_CALLS <= getNumCallsWithState(true /* isSelfManaged */, |
| 2704 | exceptCall, phoneAccountHandle, ANY_CALL_STATE); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2705 | } |
| 2706 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2707 | private boolean hasMaximumManagedHoldingCalls(Call exceptCall) { |
| 2708 | return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, |
| 2709 | null /* phoneAccountHandle */, CallState.ON_HOLD); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2710 | } |
| 2711 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2712 | private boolean hasMaximumManagedRingingCalls(Call exceptCall) { |
| 2713 | return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, |
| 2714 | null /* phoneAccountHandle */, CallState.RINGING); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2715 | } |
| 2716 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2717 | private boolean hasMaximumSelfManagedRingingCalls(Call exceptCall, |
| 2718 | PhoneAccountHandle phoneAccountHandle) { |
| 2719 | return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(true /* isSelfManaged */, exceptCall, |
| 2720 | phoneAccountHandle, CallState.RINGING); |
| 2721 | } |
| 2722 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2723 | private boolean hasMaximumOutgoingCalls(Call exceptCall) { |
| 2724 | return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL, |
| 2725 | exceptCall, null /* phoneAccountHandle */, OUTGOING_CALL_STATES); |
| 2726 | } |
| 2727 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2728 | 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 Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2745 | 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 Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2755 | !phoneAccountHandle.equals(call.getTargetPhoneAccount()) && |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2756 | call.getParentCall() == null && |
| 2757 | !call.isExternalCall()).count(); |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2758 | } |
| 2759 | |
| 2760 | /** |
Tyler Gunn | 37e782b | 2017-02-10 09:42:03 -0800 | [diff] [blame] | 2761 | * Determines if there are any managed calls. |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 2762 | * @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 Pius | 4de4a89 | 2015-08-24 11:06:58 -0700 | [diff] [blame] | 2767 | } |
| 2768 | |
Tyler Gunn | 37e782b | 2017-02-10 09:42:03 -0800 | [diff] [blame] | 2769 | /** |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2770 | * 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 Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2778 | * 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 Gunn | 37e782b | 2017-02-10 09:42:03 -0800 | [diff] [blame] | 2791 | * 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 Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2796 | CALL_FILTER_MANAGED, null /* excludeCall */, |
Tyler Gunn | 37e782b | 2017-02-10 09:42:03 -0800 | [diff] [blame] | 2797 | null /* phoneAccountHandle */, |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2798 | ONGOING_CALL_STATES) > 0; |
Tyler Gunn | 37e782b | 2017-02-10 09:42:03 -0800 | [diff] [blame] | 2799 | } |
| 2800 | |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2801 | /** |
Tyler Gunn | baf105b | 2017-04-11 15:21:03 -0700 | [diff] [blame] | 2802 | * Determines if the system incoming call UI should be shown. |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2803 | * 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 Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 2810 | hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) && |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 2811 | incomingCall.getHandoverSourceCall() == null; |
Tyler Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 2812 | } |
| 2813 | |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2814 | private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2815 | if (hasMaximumLiveCalls(call)) { |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2816 | // NOTE: If the amount of live calls changes beyond 1, this logic will probably |
| 2817 | // have to change. |
Tony Mak | f5a32e4 | 2016-02-02 12:10:26 +0000 | [diff] [blame] | 2818 | Call liveCall = getFirstCallWithState(LIVE_CALL_STATES); |
Anju Mathapati | c88574c | 2014-12-16 14:46:19 +0530 | [diff] [blame] | 2819 | Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " + |
| 2820 | liveCall); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2821 | |
Santos Cordon | d176650 | 2014-09-26 15:45:39 -0700 | [diff] [blame] | 2822 | if (call == liveCall) { |
| 2823 | // If the call is already the foreground call, then we are golden. |
Santos Cordon | 9269451 | 2015-04-23 14:47:28 -0700 | [diff] [blame] | 2824 | // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT |
Santos Cordon | d176650 | 2014-09-26 15:45:39 -0700 | [diff] [blame] | 2825 | // state since the call was already populated into the list. |
| 2826 | return true; |
| 2827 | } |
| 2828 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2829 | if (hasMaximumOutgoingCalls(call)) { |
Yorke Lee | 867907d | 2015-06-03 12:24:43 -0700 | [diff] [blame] | 2830 | 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 Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2835 | call.getAnalytics().setCallIsAdditional(true); |
| 2836 | outgoingCall.getAnalytics().setCallIsInterrupted(true); |
Yorke Lee | 867907d | 2015-06-03 12:24:43 -0700 | [diff] [blame] | 2837 | 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 Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2843 | call.getAnalytics().setCallIsAdditional(true); |
| 2844 | outgoingCall.getAnalytics().setCallIsInterrupted(true); |
Yorke Lee | 867907d | 2015-06-03 12:24:43 -0700 | [diff] [blame] | 2845 | outgoingCall.disconnect(); |
| 2846 | return true; |
Andrew Lee | e659518 | 2014-09-23 18:43:05 -0700 | [diff] [blame] | 2847 | } |
| 2848 | return false; |
| 2849 | } |
| 2850 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2851 | // 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 2855 | liveCall.disconnect("emergency, can't hold"); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2856 | return true; |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2857 | } |
| 2858 | |
Tyler Gunn | e4cd916 | 2015-08-11 15:36:04 -0700 | [diff] [blame] | 2859 | // 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 Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2875 | // 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 Gunn | e4cd916 | 2015-08-11 15:36:04 -0700 | [diff] [blame] | 2878 | if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) { |
| 2879 | Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches."); |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2880 | call.getAnalytics().setCallIsAdditional(true); |
| 2881 | liveCall.getAnalytics().setCallIsInterrupted(true); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2882 | 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 2894 | if (canHold(liveCall)) { |
Tyler Gunn | e4cd916 | 2015-08-11 15:36:04 -0700 | [diff] [blame] | 2895 | Log.i(this, "makeRoomForOutgoingCall: holding live call."); |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2896 | call.getAnalytics().setCallIsAdditional(true); |
| 2897 | liveCall.getAnalytics().setCallIsInterrupted(true); |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 2898 | liveCall.hold("calling " + call.getId()); |
Santos Cordon | f193ba4 | 2014-09-12 06:37:39 -0700 | [diff] [blame] | 2899 | 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 Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 2907 | |
| 2908 | /** |
Tyler Gunn | e4cd916 | 2015-08-11 15:36:04 -0700 | [diff] [blame] | 2909 | * 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 Cordon | d6782cd | 2015-04-16 09:57:50 -0700 | [diff] [blame] | 2925 | * Checks to see if the call should be on speakerphone and if so, set it. |
| 2926 | */ |
| 2927 | private void maybeMoveToSpeakerPhone(Call call) { |
Tyler Gunn | b821f92 | 2017-06-21 14:40:19 -0700 | [diff] [blame] | 2928 | 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 Cordon | d6782cd | 2015-04-16 09:57:50 -0700 | [diff] [blame] | 2933 | if (call.getStartWithSpeakerphoneOn()) { |
Hall Liu | 9086fb1 | 2017-11-07 18:01:53 -0800 | [diff] [blame] | 2934 | setAudioRoute(CallAudioState.ROUTE_SPEAKER, null); |
Santos Cordon | d6782cd | 2015-04-16 09:57:50 -0700 | [diff] [blame] | 2935 | call.setStartWithSpeakerphoneOn(false); |
| 2936 | } |
| 2937 | } |
| 2938 | |
| 2939 | /** |
Omata Shou | 6bc865c | 2017-07-26 13:39:27 +0900 | [diff] [blame] | 2940 | * 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 Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2949 | * 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 Liu | 4b9e556 | 2016-07-14 15:34:56 -0700 | [diff] [blame] | 2956 | boolean isDowngradedConference = (connection.getConnectionProperties() |
| 2957 | & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2958 | Call call = new Call( |
Tyler Gunn | 234d542 | 2015-12-08 14:27:49 -0800 | [diff] [blame] | 2959 | callId, |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2960 | mContext, |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 2961 | this, |
Ihab Awad | e6dbc9d | 2015-03-26 10:33:44 -0700 | [diff] [blame] | 2962 | mLock, |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2963 | mConnectionServiceRepository, |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 2964 | mContactsAsyncHelper, |
Ihab Awad | abcbce4 | 2015-04-07 14:04:01 -0700 | [diff] [blame] | 2965 | mCallerInfoAsyncQueryFactory, |
Brad Ebinger | 6e8f3d7 | 2016-06-20 11:35:42 -0700 | [diff] [blame] | 2966 | mPhoneNumberUtilsAdapter, |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2967 | connection.getHandle() /* handle */, |
| 2968 | null /* gatewayInfo */, |
| 2969 | null /* connectionManagerPhoneAccount */, |
| 2970 | connection.getPhoneAccount(), /* targetPhoneAccountHandle */ |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2971 | Call.CALL_DIRECTION_UNDEFINED /* callDirection */, |
| 2972 | false /* forceAttachToExistingConnection */, |
Hall Liu | 4b9e556 | 2016-07-14 15:34:56 -0700 | [diff] [blame] | 2973 | isDowngradedConference /* isConference */, |
Tyler Gunn | 8bb2b01 | 2017-08-04 09:28:59 -0700 | [diff] [blame] | 2974 | connection.getConnectTimeMillis() /* connectTimeMillis */, |
| 2975 | connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */ |
| 2976 | mClockProxy); |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2977 | |
Hall Liu | 3258720 | 2015-11-18 11:10:08 -0800 | [diff] [blame] | 2978 | call.initAnalytics(); |
| 2979 | call.getAnalytics().setCreatedFromExistingConnection(true); |
| 2980 | |
Santos Cordon | 5fa4e4f | 2015-06-10 14:56:01 -0700 | [diff] [blame] | 2981 | setCallState(call, Call.getStateFromConnectionState(connection.getState()), |
| 2982 | "existing connection"); |
Ihab Awad | 07bc5ee | 2014-11-12 13:42:52 -0800 | [diff] [blame] | 2983 | call.setConnectionCapabilities(connection.getConnectionCapabilities()); |
Tyler Gunn | 571d5e6 | 2016-03-15 15:55:18 -0700 | [diff] [blame] | 2984 | call.setConnectionProperties(connection.getConnectionProperties()); |
Tyler Gunn | 26d478f | 2017-06-20 11:16:57 -0700 | [diff] [blame] | 2985 | call.setHandle(connection.getHandle(), connection.getHandlePresentation()); |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2986 | call.setCallerDisplayName(connection.getCallerDisplayName(), |
| 2987 | connection.getCallerDisplayNamePresentation()); |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 2988 | call.addListener(this); |
Tyler Gunn | 9b618b8 | 2016-10-17 15:54:35 -0700 | [diff] [blame] | 2989 | |
| 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 Gunn | 7658171 | 2017-05-09 14:39:42 -0700 | [diff] [blame] | 2996 | 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 Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 3013 | addCall(call); |
Tyler Gunn | 7658171 | 2017-05-09 14:39:42 -0700 | [diff] [blame] | 3014 | 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 Gunn | 988ccc8 | 2017-06-01 11:22:10 -0700 | [diff] [blame] | 3018 | call.notifyParentChanged(parentCall); |
Tyler Gunn | 7658171 | 2017-05-09 14:39:42 -0700 | [diff] [blame] | 3019 | } |
Tyler Gunn | 6e2b94e | 2014-10-30 11:05:22 -0700 | [diff] [blame] | 3020 | |
| 3021 | return call; |
| 3022 | } |
| 3023 | |
| 3024 | /** |
Tyler Gunn | 9b618b8 | 2016-10-17 15:54:35 -0700 | [diff] [blame] | 3025 | * 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 Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 3049 | * @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 Liu | aeece4e | 2017-02-14 16:42:12 -0800 | [diff] [blame] | 3057 | public int getNextRttRequestId() { |
| 3058 | synchronized (mLock) { |
| 3059 | return (++mRttRequestId); |
| 3060 | } |
| 3061 | } |
| 3062 | |
Tyler Gunn | 8452be0 | 2015-09-17 09:57:02 -0700 | [diff] [blame] | 3063 | /** |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 3064 | * 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 Gunn | 1bf0e6b | 2016-11-30 15:19:13 -0800 | [diff] [blame] | 3067 | @VisibleForTesting |
| 3068 | public void onUserSwitch(UserHandle userHandle) { |
Santos Cordon | f0f99f3 | 2016-02-18 16:13:57 -0800 | [diff] [blame] | 3069 | mCurrentUserHandle = userHandle; |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 3070 | 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 Liu | c9cf544 | 2016-06-29 10:08:10 -0700 | [diff] [blame] | 3088 | public TelecomSystem.SyncRoot getLock() { |
| 3089 | return mLock; |
| 3090 | } |
| 3091 | |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 3092 | private void reloadMissedCallsOfUser(UserHandle userHandle) { |
Hall Liu | 3037ac6 | 2016-09-22 14:40:03 -0700 | [diff] [blame] | 3093 | mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper, |
| 3094 | new MissedCallNotifier.CallInfoFactory(), userHandle); |
| 3095 | } |
| 3096 | |
| 3097 | public void onBootCompleted() { |
| 3098 | mMissedCallNotifier.reloadAfterBootComplete(mCallerInfoLookupHelper, |
| 3099 | new MissedCallNotifier.CallInfoFactory()); |
Tony Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 3100 | } |
| 3101 | |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 3102 | 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 Gunn | 2b17f23 | 2017-03-08 08:51:00 -0800 | [diff] [blame] | 3123 | !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle); |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 3124 | } |
| 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3148 | // 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 Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 3157 | } |
| 3158 | } |
| 3159 | |
Hall Liu | 18c9449 | 2018-04-09 17:27:01 -0700 | [diff] [blame^] | 3160 | 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 Mak | a993094 | 2016-01-15 10:57:14 +0000 | [diff] [blame] | 3175 | /** |
Tyler Gunn | 9e806ee | 2017-02-06 20:49:24 -0800 | [diff] [blame] | 3176 | * 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 Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3198 | * 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3212 | disconnectSelfManagedCalls("outgoing call " + callId); |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3213 | |
| 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 Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3257 | 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 Meng | fc9c8ff | 2018-01-17 05:50:49 +0000 | [diff] [blame] | 3262 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3263 | 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 Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3269 | } |
| 3270 | |
| 3271 | /** |
| 3272 | * Disconnects all self-managed calls. |
| 3273 | */ |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3274 | private void disconnectSelfManagedCalls(String reason) { |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3275 | // 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3281 | .forEach(c -> c.disconnect(reason)); |
Tyler Gunn | 911d4de | 2017-12-19 08:11:35 -0800 | [diff] [blame] | 3282 | |
| 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 Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3288 | } |
| 3289 | |
| 3290 | /** |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 3291 | * 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 Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 3297 | 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 Gunn | 9787e0e | 2014-10-14 14:36:12 -0700 | [diff] [blame] | 3305 | |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3306 | if (mPendingCall != null) { |
| 3307 | pw.print("mPendingCall:"); |
| 3308 | pw.println(mPendingCall.getId()); |
| 3309 | } |
| 3310 | |
Tyler Gunn | 9787e0e | 2014-10-14 14:36:12 -0700 | [diff] [blame] | 3311 | 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 Liu | 7c92832 | 2016-12-06 18:15:39 -0800 | [diff] [blame] | 3332 | if (mDefaultDialerCache != null) { |
| 3333 | pw.println("mDefaultDialerCache:"); |
| 3334 | pw.increaseIndent(); |
| 3335 | mDefaultDialerCache.dumpCache(pw); |
| 3336 | pw.decreaseIndent(); |
| 3337 | } |
| 3338 | |
Tyler Gunn | 9787e0e | 2014-10-14 14:36:12 -0700 | [diff] [blame] | 3339 | if (mConnectionServiceRepository != null) { |
| 3340 | pw.println("mConnectionServiceRepository:"); |
| 3341 | pw.increaseIndent(); |
| 3342 | mConnectionServiceRepository.dump(pw); |
| 3343 | pw.decreaseIndent(); |
| 3344 | } |
Tyler Gunn | 91d43cf | 2014-09-17 12:19:39 -0700 | [diff] [blame] | 3345 | } |
Xueren Zhang | c0e8e9d | 2015-03-06 14:16:55 +0100 | [diff] [blame] | 3346 | |
| 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 Chintala | ba01845 | 2017-07-03 11:39:43 +0530 | [diff] [blame] | 3354 | || isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(call)) { |
Xueren Zhang | c0e8e9d | 2015-03-06 14:16:55 +0100 | [diff] [blame] | 3355 | 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 Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 3366 | |
| 3367 | private void setIntentExtrasAndStartTime(Call call, Bundle extras) { |
Tyler Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 3368 | 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 Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 3374 | |
Tyler Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 3375 | // 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 Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 3378 | SystemClock.elapsedRealtime()); |
| 3379 | |
Tyler Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 3380 | call.setIntentExtras(extras); |
Sailesh Nepal | 703a1af | 2016-04-14 20:10:12 -0700 | [diff] [blame] | 3381 | } |
Tyler Gunn | a90ba73 | 2017-01-26 07:24:08 -0800 | [diff] [blame] | 3382 | |
| 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 Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3404 | |
| 3405 | /** |
Sanket Padawe | 29411c9 | 2017-11-30 16:51:08 -0800 | [diff] [blame] | 3406 | * 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3416 | call.disconnect("handover failed"); |
Sanket Padawe | 29411c9 | 2017-11-30 16:51:08 -0800 | [diff] [blame] | 3417 | } |
| 3418 | |
| 3419 | /** |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3420 | * 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 Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 3430 | * @param initiatingExtras Extras associated with the handover, to be passed to the handover |
| 3431 | * {@link android.telecom.ConnectionService}. |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3432 | */ |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3433 | private void requestHandoverViaEvents(Call handoverFromCall, |
| 3434 | PhoneAccountHandle handoverToHandle, |
| 3435 | int videoState, Bundle initiatingExtras) { |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3436 | |
| 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 3450 | extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, |
| 3451 | handoverFromCall.getTargetPhoneAccount()); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3452 | extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); |
Tyler Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 3453 | if (initiatingExtras != null) { |
| 3454 | extras.putAll(initiatingExtras); |
| 3455 | } |
| 3456 | extras.putParcelable(TelecomManager.EXTRA_CALL_AUDIO_STATE, |
| 3457 | mCallAudioManager.getCallAudioState()); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3458 | Call handoverToCall = startOutgoingCall(handoverFromCall.getHandle(), handoverToHandle, |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 3459 | extras, getCurrentUserHandle(), null /* originalIntent */); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3460 | Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, |
| 3461 | "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId()); |
Tyler Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 3462 | handoverFromCall.setHandoverDestinationCall(handoverToCall); |
| 3463 | handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); |
| 3464 | handoverToCall.setHandoverState(HandoverState.HANDOVER_TO_STARTED); |
| 3465 | handoverToCall.setHandoverSourceCall(handoverFromCall); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3466 | handoverToCall.setNewOutgoingCallIntentBroadcastIsDone(); |
| 3467 | placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */, |
| 3468 | false /* startwithSpeaker */, |
| 3469 | videoState); |
| 3470 | } |
| 3471 | |
| 3472 | /** |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3473 | * 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 Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 3482 | * @param extras Extras associated with the handover, to be passed to the handover |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3483 | * {@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 Gunn | 8c4e621 | 2018-03-19 20:07:57 +0000 | [diff] [blame] | 3491 | android.telecom.Call.Callback.HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL); |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3492 | 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 3501 | android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED); |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3502 | 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 Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3528 | // 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 Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 3551 | |
| 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 Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3559 | 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3596 | disconnectSelfManagedCalls("emergency call"); |
Sanket Padawe | 4ebc3ca | 2017-11-21 12:49:43 -0800 | [diff] [blame] | 3597 | } |
| 3598 | |
| 3599 | call.startCreateConnection(mPhoneAccountRegistrar); |
| 3600 | } |
| 3601 | |
| 3602 | } |
| 3603 | |
| 3604 | /** |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3605 | * 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 Gunn | 6f6f1c5 | 2017-04-17 18:22:04 -0700 | [diff] [blame] | 3611 | return getBooleanPhoneAccountExtra(from, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM); |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3612 | } |
| 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 Gunn | 141ef58 | 2017-05-26 13:38:13 -0700 | [diff] [blame] | 3649 | return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null || |
| 3650 | c.getHandoverDestinationCall() != null).count() > 0; |
Tyler Gunn | 6f9ceb2 | 2017-04-06 08:47:01 -0700 | [diff] [blame] | 3651 | } |
Tyler Gunn | ea4c6fb | 2017-04-14 13:46:21 -0700 | [diff] [blame] | 3652 | |
| 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 Padawe | ccdf364 | 2017-11-10 14:49:05 -0800 | [diff] [blame] | 3698 | |
| 3699 | public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) { |
Sanket Padawe | 29411c9 | 2017-11-30 16:51:08 -0800 | [diff] [blame] | 3700 | |
| 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 3732 | android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED); |
Sanket Padawe | 29411c9 | 2017-11-30 16:51:08 -0800 | [diff] [blame] | 3733 | 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 Gunn | 97f30dc | 2018-03-07 21:09:58 +0000 | [diff] [blame] | 3740 | android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED); |
Sanket Padawe | 29411c9 | 2017-11-30 16:51:08 -0800 | [diff] [blame] | 3741 | 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 Gunn | 7c031f2 | 2018-01-18 15:00:41 -0800 | [diff] [blame] | 3769 | 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 Padawe | 29411c9 | 2017-11-30 16:51:08 -0800 | [diff] [blame] | 3777 | call.startCreateConnection(mPhoneAccountRegistrar); |
Sanket Padawe | ccdf364 | 2017-11-10 14:49:05 -0800 | [diff] [blame] | 3778 | } |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3779 | |
| 3780 | ConnectionServiceFocusManager getConnectionServiceFocusManager() { |
| 3781 | return mConnectionSvrFocusMgr; |
| 3782 | } |
| 3783 | |
| 3784 | private boolean canHold(Call call) { |
| 3785 | return call.can(Connection.CAPABILITY_HOLD); |
| 3786 | } |
| 3787 | |
Tyler Gunn | 60f230e | 2018-03-16 15:11:27 -0700 | [diff] [blame] | 3788 | private boolean supportsHold(Call call) { |
| 3789 | return call.can(Connection.CAPABILITY_SUPPORT_HOLD); |
| 3790 | } |
| 3791 | |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3792 | 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 Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3813 | private final String mPreviouslyHeldCallId; |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3814 | |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3815 | ActionUnHoldCall(Call call, String previouslyHeldCallId) { |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3816 | mCall = call; |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3817 | mPreviouslyHeldCallId = previouslyHeldCallId; |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3818 | } |
| 3819 | |
| 3820 | @Override |
| 3821 | public void performAction() { |
| 3822 | Log.d(this, "perform unhold call for %s", mCall); |
Tyler Gunn | f4f0539 | 2018-03-26 18:57:59 +0000 | [diff] [blame] | 3823 | mCall.unhold("held " + mPreviouslyHeldCallId); |
Pengquan Meng | 4832f20 | 2017-12-20 16:13:04 -0800 | [diff] [blame] | 3824 | } |
| 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 Gilad | 9f2bed3 | 2013-12-12 17:43:26 -0800 | [diff] [blame] | 3868 | } |