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 android.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.NetworkInfo; 22 import android.os.Handler; 23 import android.telecom.CallState; 24 import android.telecom.PhoneAccountHandle; 25 import android.telephony.TelephonyManager; 26 import android.telephony.PhoneStateListener; 27 import android.telephony.ServiceState; 28 29 import java.util.Collection; 30 import java.util.Objects; 31 32 /** 33 * Registers a timeout for a call and disconnects the call when the timeout expires. 34 */ 35 final class CreateConnectionTimeout extends PhoneStateListener implements Runnable { 36 private final Context mContext; 37 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 38 private final ConnectionServiceWrapper mConnectionService; 39 private final Call mCall; 40 private final Handler mHandler = new Handler(); 41 private boolean mIsRegistered; 42 private boolean mIsCallTimedOut; 43 CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar, ConnectionServiceWrapper service, Call call)44 CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar, 45 ConnectionServiceWrapper service, Call call) { 46 mContext = context; 47 mPhoneAccountRegistrar = phoneAccountRegistrar; 48 mConnectionService = service; 49 mCall = call; 50 } 51 isTimeoutNeededForCall(Collection<PhoneAccountHandle> accounts, PhoneAccountHandle currentAccount)52 boolean isTimeoutNeededForCall(Collection<PhoneAccountHandle> accounts, 53 PhoneAccountHandle currentAccount) { 54 // Non-emergency calls timeout automatically at the radio layer. No need for a timeout here. 55 if (!TelephonyUtil.shouldProcessAsEmergency(mContext, mCall.getHandle())) { 56 return false; 57 } 58 59 // If there's no connection manager to fallback on then there's no point in having a 60 // timeout. 61 PhoneAccountHandle connectionManager = mPhoneAccountRegistrar.getSimCallManager(); 62 if (!accounts.contains(connectionManager)) { 63 return false; 64 } 65 66 // No need to add a timeout if the current attempt is over the connection manager. 67 if (Objects.equals(connectionManager, currentAccount)) { 68 return false; 69 } 70 71 // To reduce the number of scenarios where a timeout is needed, only use a timeout if 72 // we're connected to Wi-Fi. This ensures that the fallback connection manager has an 73 // alternate route to place the call. TODO: remove this condition or allow connection 74 // managers to specify transports. See http://b/19199181. 75 if (!isConnectedToWifi()) { 76 return false; 77 } 78 79 Log.d(this, "isTimeoutNeededForCall, returning true"); 80 return true; 81 } 82 registerTimeout()83 void registerTimeout() { 84 Log.d(this, "registerTimeout"); 85 mIsRegistered = true; 86 // First find out the cellular service state. Based on the state we decide whether a timeout 87 // will actually be enforced and if so how long it should be. 88 TelephonyManager telephonyManager = 89 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 90 telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE); 91 telephonyManager.listen(this, 0); 92 } 93 unregisterTimeout()94 void unregisterTimeout() { 95 Log.d(this, "unregisterTimeout"); 96 mIsRegistered = false; 97 mHandler.removeCallbacksAndMessages(null); 98 } 99 isCallTimedOut()100 boolean isCallTimedOut() { 101 return mIsCallTimedOut; 102 } 103 104 @Override onServiceStateChanged(ServiceState serviceState)105 public void onServiceStateChanged(ServiceState serviceState) { 106 long timeoutLengthMillis = getTimeoutLengthMillis(serviceState); 107 if (!mIsRegistered) { 108 Log.d(this, "onServiceStateChanged, timeout no longer registered, skipping"); 109 } else if (timeoutLengthMillis <= 0) { 110 Log.d(this, "onServiceStateChanged, timeout set to %d, skipping", timeoutLengthMillis); 111 } else if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) { 112 // If cellular service is available then don't bother with a timeout. 113 Log.d(this, "onServiceStateChanged, cellular service available, skipping"); 114 } else { 115 mHandler.postDelayed(this, timeoutLengthMillis); 116 } 117 } 118 119 @Override run()120 public void run() { 121 if (mIsRegistered && isCallBeingPlaced(mCall)) { 122 Log.d(this, "run, call timed out, calling disconnect"); 123 mIsCallTimedOut = true; 124 mConnectionService.disconnect(mCall); 125 } 126 } 127 isCallBeingPlaced(Call call)128 static boolean isCallBeingPlaced(Call call) { 129 int state = call.getState(); 130 return state == CallState.NEW 131 || state == CallState.CONNECTING 132 || state == CallState.DIALING; 133 } 134 getTimeoutLengthMillis(ServiceState serviceState)135 private long getTimeoutLengthMillis(ServiceState serviceState) { 136 // If the radio is off then use a longer timeout. This gives us more time to power on the 137 // radio. 138 if (serviceState.getState() == ServiceState.STATE_POWER_OFF) { 139 return Timeouts.getEmergencyCallTimeoutRadioOffMillis( 140 mContext.getContentResolver()); 141 } else { 142 return Timeouts.getEmergencyCallTimeoutMillis(mContext.getContentResolver()); 143 } 144 } 145 isConnectedToWifi()146 private boolean isConnectedToWifi() { 147 ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( 148 Context.CONNECTIVITY_SERVICE); 149 if (cm != null) { 150 NetworkInfo ni = cm.getActiveNetworkInfo(); 151 return ni != null && ni.isConnected() && ni.getType() == ConnectivityManager.TYPE_WIFI; 152 } 153 return false; 154 } 155 } 156