1 /* 2 * Copyright 2015, 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 17 package com.android.server.telecom; 18 19 import static com.android.internal.telephony.flags.Flags.carrierEnabledSatelliteFlag; 20 21 import android.content.Context; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.telecom.Log; 25 import android.telecom.Logging.Runnable; 26 import android.telecom.PhoneAccount; 27 import android.telecom.PhoneAccountHandle; 28 import android.telephony.TelephonyManager; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.util.Collection; 33 import java.util.Objects; 34 35 /** 36 * Registers a timeout for a call and disconnects the call when the timeout expires. 37 */ 38 @VisibleForTesting 39 public final class CreateConnectionTimeout extends Runnable { 40 private final Context mContext; 41 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 42 private final ConnectionServiceWrapper mConnectionService; 43 private final Call mCall; 44 private final Handler mHandler = new Handler(Looper.getMainLooper()); 45 private boolean mIsRegistered; 46 private boolean mIsCallTimedOut; 47 private final Timeouts.Adapter mTimeoutsAdapter; 48 49 @VisibleForTesting CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar, ConnectionServiceWrapper service, Call call, Timeouts.Adapter timeoutsAdapter)50 public CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar, 51 ConnectionServiceWrapper service, Call call, Timeouts.Adapter timeoutsAdapter) { 52 super("CCT", null /*lock*/); 53 mContext = context; 54 mPhoneAccountRegistrar = phoneAccountRegistrar; 55 mConnectionService = service; 56 mCall = call; 57 mTimeoutsAdapter = timeoutsAdapter; 58 } 59 60 @VisibleForTesting isTimeoutNeededForCall(Collection<PhoneAccountHandle> accounts, PhoneAccountHandle currentAccount)61 public boolean isTimeoutNeededForCall(Collection<PhoneAccountHandle> accounts, 62 PhoneAccountHandle currentAccount) { 63 // Non-emergency calls timeout automatically at the radio layer. No need for a timeout here. 64 if (!mCall.isEmergencyCall()) { 65 Log.d(this, "isTimeoutNeededForCall, not an emergency call"); 66 return false; 67 } 68 69 // If there's no connection manager to fallback on then there's no point in having a 70 // timeout. 71 PhoneAccountHandle connectionManager = 72 mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall); 73 if (!accounts.contains(connectionManager)) { 74 Log.d(this, "isTimeoutNeededForCall, no connection manager"); 75 return false; 76 } 77 78 // No need to add a timeout if the current attempt is over the connection manager. 79 if (Objects.equals(connectionManager, currentAccount)) { 80 Log.d(this, "isTimeoutNeededForCall, already attempting over connection manager"); 81 return false; 82 } 83 84 // Timeout is only supported for SIM call managers that are set by the carrier. 85 if (connectionManager != null && !Objects.equals(connectionManager.getComponentName(), 86 mPhoneAccountRegistrar.getSystemSimCallManagerComponent())) { 87 Log.d(this, "isTimeoutNeededForCall, not a system sim call manager"); 88 return false; 89 } 90 91 Log.i(this, "isTimeoutNeededForCall, returning true"); 92 return true; 93 } 94 registerTimeout()95 void registerTimeout() { 96 Log.d(this, "registerTimeout"); 97 mIsRegistered = true; 98 99 long timeoutLengthMillis = getTimeoutLengthMillis(); 100 if (timeoutLengthMillis <= 0) { 101 Log.d(this, "registerTimeout, timeout set to %d, skipping", timeoutLengthMillis); 102 } else { 103 mHandler.postDelayed(prepare(), timeoutLengthMillis); 104 } 105 } 106 unregisterTimeout()107 void unregisterTimeout() { 108 Log.d(this, "unregisterTimeout"); 109 mIsRegistered = false; 110 mHandler.removeCallbacksAndMessages(null); 111 cancel(); 112 } 113 isCallTimedOut()114 boolean isCallTimedOut() { 115 return mIsCallTimedOut; 116 } 117 118 @Override loggedRun()119 public void loggedRun() { 120 if (!carrierEnabledSatelliteFlag()) { 121 timeoutCallIfNeeded(); 122 return; 123 } 124 125 PhoneAccountHandle connectionManager = 126 mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall); 127 if (connectionManager != null) { 128 PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(connectionManager, 129 connectionManager.getUserHandle()); 130 if (account != null && account.hasCapabilities( 131 (PhoneAccount.CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS 132 | PhoneAccount.CAPABILITY_VOICE_CALLING_AVAILABLE))) { 133 // If we have encountered the timeout and there is an in service 134 // ConnectionManager, disconnect the call so that it can be attempted over 135 // the ConnectionManager. 136 timeoutCallIfNeeded(); 137 return; 138 } 139 Log.i( 140 this, 141 "loggedRun, no PhoneAccount with voice calling capabilities, not timing out call"); 142 } 143 } 144 timeoutCallIfNeeded()145 private void timeoutCallIfNeeded() { 146 if (mIsRegistered && isCallBeingPlaced(mCall)) { 147 Log.i(this, "timeoutCallIfNeeded, call timed out, calling disconnect"); 148 mIsCallTimedOut = true; 149 mConnectionService.disconnect(mCall); 150 } 151 } 152 isCallBeingPlaced(Call call)153 static boolean isCallBeingPlaced(Call call) { 154 int state = call.getState(); 155 return state == CallState.NEW 156 || state == CallState.CONNECTING 157 || state == CallState.DIALING 158 || state == CallState.PULLING; 159 } 160 getTimeoutLengthMillis()161 private long getTimeoutLengthMillis() { 162 // If the radio is off then use a longer timeout. This gives us more time to power on the 163 // radio. 164 try { 165 TelephonyManager telephonyManager = 166 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 167 if (telephonyManager.isRadioOn()) { 168 return mTimeoutsAdapter.getEmergencyCallTimeoutMillis( 169 mContext.getContentResolver()); 170 } else { 171 return mTimeoutsAdapter.getEmergencyCallTimeoutRadioOffMillis( 172 mContext.getContentResolver()); 173 } 174 } catch (UnsupportedOperationException uoe) { 175 Log.e(this, uoe, "getTimeoutLengthMillis - telephony is not supported"); 176 return mTimeoutsAdapter.getEmergencyCallTimeoutMillis(mContext.getContentResolver()); 177 } 178 } 179 } 180