/* * Copyright 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.telecom; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.telecom.Log; import android.telecom.Logging.Runnable; import android.telecom.PhoneAccountHandle; import android.telephony.TelephonyManager; import java.util.Collection; import java.util.Objects; /** * Registers a timeout for a call and disconnects the call when the timeout expires. */ final class CreateConnectionTimeout extends Runnable { private final Context mContext; private final PhoneAccountRegistrar mPhoneAccountRegistrar; private final ConnectionServiceWrapper mConnectionService; private final Call mCall; private final Handler mHandler = new Handler(Looper.getMainLooper()); private boolean mIsRegistered; private boolean mIsCallTimedOut; CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar, ConnectionServiceWrapper service, Call call) { super("CCT", null /*lock*/); mContext = context; mPhoneAccountRegistrar = phoneAccountRegistrar; mConnectionService = service; mCall = call; } boolean isTimeoutNeededForCall(Collection accounts, PhoneAccountHandle currentAccount) { // Non-emergency calls timeout automatically at the radio layer. No need for a timeout here. if (!mCall.isEmergencyCall()) { return false; } // If there's no connection manager to fallback on then there's no point in having a // timeout. PhoneAccountHandle connectionManager = mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall); if (!accounts.contains(connectionManager)) { return false; } // No need to add a timeout if the current attempt is over the connection manager. if (Objects.equals(connectionManager, currentAccount)) { return false; } // Timeout is only supported for SIM call managers that are set by the carrier. if (connectionManager != null && !Objects.equals(connectionManager.getComponentName(), mPhoneAccountRegistrar.getSystemSimCallManagerComponent())) { Log.d(this, "isTimeoutNeededForCall, not a system sim call manager"); return false; } Log.i(this, "isTimeoutNeededForCall, returning true"); return true; } void registerTimeout() { Log.d(this, "registerTimeout"); mIsRegistered = true; long timeoutLengthMillis = getTimeoutLengthMillis(); if (timeoutLengthMillis <= 0) { Log.d(this, "registerTimeout, timeout set to %d, skipping", timeoutLengthMillis); } else { mHandler.postDelayed(prepare(), timeoutLengthMillis); } } void unregisterTimeout() { Log.d(this, "unregisterTimeout"); mIsRegistered = false; mHandler.removeCallbacksAndMessages(null); cancel(); } boolean isCallTimedOut() { return mIsCallTimedOut; } @Override public void loggedRun() { if (mIsRegistered && isCallBeingPlaced(mCall)) { Log.i(this, "run, call timed out, calling disconnect"); mIsCallTimedOut = true; mConnectionService.disconnect(mCall); } } static boolean isCallBeingPlaced(Call call) { int state = call.getState(); return state == CallState.NEW || state == CallState.CONNECTING || state == CallState.DIALING || state == CallState.PULLING; } private long getTimeoutLengthMillis() { // If the radio is off then use a longer timeout. This gives us more time to power on the // radio. TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); if (telephonyManager.isRadioOn()) { return Timeouts.getEmergencyCallTimeoutMillis(mContext.getContentResolver()); } else { return Timeouts.getEmergencyCallTimeoutRadioOffMillis( mContext.getContentResolver()); } } }