diff options
author | Wei Liu <luciferleo@google.com> | 2016-12-14 19:36:52 -0800 |
---|---|---|
committer | Wei Liu <luciferleo@google.com> | 2017-02-06 16:01:08 -0800 |
commit | 5e0ffbe1aca1366c00dd565cdb83ba01c5229d3d (patch) | |
tree | 1d2d21d56ab2d22ed2f1ae0ab33728893ff38157 | |
parent | 73a5e9ae0ba566ac6f0bbf40c643ef3e08eae917 (diff) | |
download | android_packages_services_Telephony-5e0ffbe1aca1366c00dd565cdb83ba01c5229d3d.tar.gz android_packages_services_Telephony-5e0ffbe1aca1366c00dd565cdb83ba01c5229d3d.tar.bz2 android_packages_services_Telephony-5e0ffbe1aca1366c00dd565cdb83ba01c5229d3d.zip |
DO NOT MERGE Refactor EmergencyCallHelper -> RadioOnCallHelper
To make phone calls when the cellular radio is turned off automatically,
we will use a mechanism similar to the EmergencyCallHelper and share
the handling of the onComplete() callback.
Change-Id: Id782c759d3abd29d432782575f88d2ed83f083b2
-rw-r--r-- | res/values/config.xml | 3 | ||||
-rw-r--r-- | src/com/android/services/telephony/RadioOnHelper.java (renamed from src/com/android/services/telephony/EmergencyCallHelper.java) | 44 | ||||
-rw-r--r-- | src/com/android/services/telephony/RadioOnStateListener.java (renamed from src/com/android/services/telephony/EmergencyCallStateListener.java) | 28 | ||||
-rw-r--r-- | src/com/android/services/telephony/TelephonyConnectionService.java | 155 | ||||
-rw-r--r-- | tests/src/com/android/services/telephony/RadioOnStateListenerTest.java (renamed from tests/src/com/android/services/telephony/EmergencyCallStateListenerTest.java) | 20 |
5 files changed, 151 insertions, 99 deletions
diff --git a/res/values/config.xml b/res/values/config.xml index 1f23c6bd7..2ec338894 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -218,4 +218,7 @@ <!-- Whether the emergency only account can make emergency calls --> <bool name="config_pstnCanPlaceEmergencyCalls">true</bool> + + <!-- Whether the cellular radio is allowed to be power down when the Bluetooth can provide the data/call capabilities --> + <bool name="config_allowRadioPowerDownOnBluetooth">false</bool> </resources> diff --git a/src/com/android/services/telephony/EmergencyCallHelper.java b/src/com/android/services/telephony/RadioOnHelper.java index 295f4f7f8..81a5b1941 100644 --- a/src/com/android/services/telephony/EmergencyCallHelper.java +++ b/src/com/android/services/telephony/RadioOnHelper.java @@ -30,21 +30,22 @@ import java.util.HashSet; import java.util.List; /** - * Helper class that implements special behavior related to emergency calls. Specifically, this - * class handles the case of the user trying to dial an emergency number while the radio is off - * (i.e. the device is in airplane mode), by forcibly turning the radio back on, waiting for it to - * come up, and then retrying the emergency call. + * Helper class that implements special behavior related to emergency calls or make phone calls when + * radio is power off due to the device being on Bluetooth. Specifically, this class handles the + * case of the user trying to dial an emergency number while the radio is off (i.e. the device is + * in airplane mode) or a normal number while the radio is off (because of the device is on + * Bluetooth), by forcibly turning the radio back on, waiting for it to come up, and then retrying + * the call. */ -public class EmergencyCallHelper implements EmergencyCallStateListener.Callback { +public class RadioOnHelper implements RadioOnStateListener.Callback { private final Context mContext; - private EmergencyCallStateListener.Callback mCallback; - private List<EmergencyCallStateListener> mListeners; - private List<EmergencyCallStateListener> mInProgressListeners; - private boolean mIsEmergencyCallingEnabled; + private RadioOnStateListener.Callback mCallback; + private List<RadioOnStateListener> mListeners; + private List<RadioOnStateListener> mInProgressListeners; + private boolean mIsRadioOnCallingEnabled; - - public EmergencyCallHelper(Context context) { + public RadioOnHelper(Context context) { mContext = context; mInProgressListeners = new ArrayList<>(2); } @@ -55,12 +56,12 @@ public class EmergencyCallHelper implements EmergencyCallStateListener.Callback } mListeners = new ArrayList<>(2); for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { - mListeners.add(new EmergencyCallStateListener()); + mListeners.add(new RadioOnStateListener()); } } /** * Starts the "turn on radio" sequence. This is the (single) external API of the - * EmergencyCallHelper class. + * RadioOnHelper class. * * This method kicks off the following sequence: * - Power on the radio for each Phone @@ -69,14 +70,14 @@ public class EmergencyCallHelper implements EmergencyCallStateListener.Callback * - Finally, clean up any leftover state. * * This method is safe to call from any thread, since it simply posts a message to the - * EmergencyCallHelper's handler (thus ensuring that the rest of the sequence is entirely + * RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely * serialized, and runs on the main looper.) */ - public void enableEmergencyCalling(EmergencyCallStateListener.Callback callback) { + public void enableRadioOnCalling(RadioOnStateListener.Callback callback) { setupListeners(); mCallback = callback; mInProgressListeners.clear(); - mIsEmergencyCallingEnabled = false; + mIsRadioOnCallingEnabled = false; for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { Phone phone = PhoneFactory.getPhone(i); if (phone == null) @@ -120,11 +121,16 @@ public class EmergencyCallHelper implements EmergencyCallStateListener.Callback * Synchronization is not necessary. */ @Override - public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) { - mIsEmergencyCallingEnabled |= isRadioReady; + public void onComplete(RadioOnStateListener listener, boolean isRadioReady) { + mIsRadioOnCallingEnabled |= isRadioReady; mInProgressListeners.remove(listener); if (mCallback != null && mInProgressListeners.isEmpty()) { - mCallback.onComplete(null, mIsEmergencyCallingEnabled); + mCallback.onComplete(null, mIsRadioOnCallingEnabled); } } + + @Override + public boolean isOkToCall(Phone phone, int serviceState) { + return (mCallback == null) ? false : mCallback.isOkToCall(phone, serviceState); + } } diff --git a/src/com/android/services/telephony/EmergencyCallStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java index 036872dc8..7bfa9c63b 100644 --- a/src/com/android/services/telephony/EmergencyCallStateListener.java +++ b/src/com/android/services/telephony/RadioOnStateListener.java @@ -31,15 +31,16 @@ import com.android.internal.telephony.SubscriptionController; /** * Helper class that listens to a Phone's radio state and sends a callback when the radio state of - * that Phone is either "in service" or "emergency calls only." + * that Phone is either "in service" or ("emergency calls only." if is emergency). */ -public class EmergencyCallStateListener { +public class RadioOnStateListener { /** - * Receives the result of the EmergencyCallStateListener's attempt to turn on the radio. + * Receives the result of the RadioOnStateListener's attempt to turn on the radio. */ interface Callback { - void onComplete(EmergencyCallStateListener listener, boolean isRadioReady); + void onComplete(RadioOnStateListener listener, boolean isRadioReady); + boolean isOkToCall(Phone phone, int serviceState); } // Number of times to retry the call, and time between retry attempts. @@ -62,8 +63,8 @@ public class EmergencyCallStateListener { SomeArgs args = (SomeArgs) msg.obj; try { Phone phone = (Phone) args.arg1; - EmergencyCallStateListener.Callback callback = - (EmergencyCallStateListener.Callback) args.arg2; + RadioOnStateListener.Callback callback = + (RadioOnStateListener.Callback) args.arg2; startSequenceInternal(phone, callback); } finally { args.recycle(); @@ -89,7 +90,7 @@ public class EmergencyCallStateListener { /** * Starts the "wait for radio" sequence. This is the (single) external API of the - * EmergencyCallStateListener class. + * RadioOnStateListener class. * * This method kicks off the following sequence: * - Listen for the service state change event telling us the radio has come up. @@ -98,7 +99,7 @@ public class EmergencyCallStateListener { * - Finally, clean up any leftover state. * * This method is safe to call from any thread, since it simply posts a message to the - * EmergencyCallStateListener's handler (thus ensuring that the rest of the sequence is entirely + * RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely * serialized, and runs only on the handler thread.) */ public void waitForRadioOn(Phone phone, Callback callback) { @@ -123,7 +124,7 @@ public class EmergencyCallStateListener { private void startSequenceInternal(Phone phone, Callback callback) { Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId()); - // First of all, clean up any state left over from a prior emergency call sequence. This + // First of all, clean up any state left over from a prior RadioOn call sequence. This // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while // we're already in the middle of the sequence. cleanup(); @@ -140,7 +141,7 @@ public class EmergencyCallStateListener { /** * Handles the SERVICE_STATE_CHANGED event. Normally this event tells us that the radio has - * finally come up. In that case, it's now safe to actually place the emergency call. + * finally come up. In that case, it's now safe to actually place the RadioOn call. */ private void onServiceStateChanged(ServiceState state) { Log.d(this, "onServiceStateChanged(), new state = %s, Phone = %s", state, @@ -171,8 +172,7 @@ public class EmergencyCallStateListener { * UNAVAILABLE state, even if it is reporting the OUT_OF_SERVICE state. */ private boolean isOkToCall(int serviceState) { - return (mPhone.getState() == PhoneConstants.State.OFFHOOK) || - mPhone.getServiceStateTracker().isRadioOn(); + return (mCallback == null) ? false : mCallback.isOkToCall(mPhone, serviceState); } /** @@ -223,7 +223,7 @@ public class EmergencyCallStateListener { * - Clean up any extraneous handler messages (like retry timeouts) still in the queue * * Basically this method guarantees that there will be no more activity from the - * EmergencyCallStateListener until someone kicks off the whole sequence again with another call + * RadioOnStateListener until someone kicks off the whole sequence again with another call * to {@link #waitForRadioOn} * * TODO: Do the work for the comment below: @@ -298,7 +298,7 @@ public class EmergencyCallStateListener { if (this == o) return true; if (o == null || !getClass().equals(o.getClass())) return false; - EmergencyCallStateListener that = (EmergencyCallStateListener) o; + RadioOnStateListener that = (RadioOnStateListener) o; if (mNumRetriesSoFar != that.mNumRetriesSoFar) { return false; diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java index 5afdd727a..d27054aa6 100644 --- a/src/com/android/services/telephony/TelephonyConnectionService.java +++ b/src/com/android/services/telephony/TelephonyConnectionService.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.provider.Settings; import android.telecom.Conference; import android.telecom.Connection; import android.telecom.ConnectionRequest; @@ -79,7 +80,6 @@ public class TelephonyConnectionService extends ConnectionService { new ImsConferenceController(this); private ComponentName mExpectedComponentName = null; - private EmergencyCallHelper mEmergencyCallHelper; private EmergencyTonePlayer mEmergencyTonePlayer; // Contains one TelephonyConnection that has placed a call and a memory of which Phones it has @@ -217,70 +217,39 @@ public class TelephonyConnectionService extends ConnectionService { final boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, numberToDial); - if (isEmergencyNumber && !isRadioOn()) { - final Uri emergencyHandle = handle; + if ((isEmergencyNumber && !isRadioOn()) || isRadioPowerDownOnBluetooth()) { + final Uri resultHandle = handle; // By default, Connection based on the default Phone, since we need to return to Telecom // now. - final int defaultPhoneType = PhoneFactory.getDefaultPhone().getPhoneType(); - final Connection emergencyConnection = getTelephonyConnection(request, numberToDial, - isEmergencyNumber, emergencyHandle, PhoneFactory.getDefaultPhone()); - if (mEmergencyCallHelper == null) { - mEmergencyCallHelper = new EmergencyCallHelper(this); - } - mEmergencyCallHelper.enableEmergencyCalling(new EmergencyCallStateListener.Callback() { + final Connection resultConnection = getTelephonyConnection(request, numberToDial, + isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone()); + RadioOnHelper radioOnHelper = new RadioOnHelper(this); + radioOnHelper.enableRadioOnCalling(new RadioOnStateListener.Callback() { @Override - public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) { - // Make sure the Call has not already been canceled by the user. - if (emergencyConnection.getState() == Connection.STATE_DISCONNECTED) { - Log.i(this, "Emergency call disconnected before the outgoing call was " + - "placed. Skipping emergency call placement."); - return; - } - if (isRadioReady) { - // Get the right phone object since the radio has been turned on - // successfully. - final Phone phone = getPhoneForAccount(request.getAccountHandle(), - isEmergencyNumber); - // If the PhoneType of the Phone being used is different than the Default - // Phone, then we need create a new Connection using that PhoneType and - // replace it in Telecom. - if (phone.getPhoneType() != defaultPhoneType) { - Connection repConnection = getTelephonyConnection(request, numberToDial, - isEmergencyNumber, emergencyHandle, phone); - // If there was a failure, the resulting connection will not be a - // TelephonyConnection, so don't place the call, just return! - if (repConnection instanceof TelephonyConnection) { - placeOutgoingConnection((TelephonyConnection) repConnection, phone, - request); - } - // Notify Telecom of the new Connection type. - // TODO: Switch out the underlying connection instead of creating a new - // one and causing UI Jank. - addExistingConnection(PhoneUtils.makePstnPhoneAccountHandle(phone), - repConnection); - // Remove the old connection from Telecom after. - emergencyConnection.setDisconnected( - DisconnectCauseUtil.toTelecomDisconnectCause( - android.telephony.DisconnectCause.OUTGOING_CANCELED, - "Reconnecting outgoing Emergency Call.")); - emergencyConnection.destroy(); - } else { - placeOutgoingConnection((TelephonyConnection) emergencyConnection, - phone, request); - } + public void onComplete(RadioOnStateListener listener, + boolean isRadioReady) { + handleOnComplete(isRadioReady, + isEmergencyNumber, + resultConnection, + request, + numberToDial, + resultHandle); + } + + @Override + public boolean isOkToCall(Phone phone, int serviceState) { + if (isEmergencyNumber) { + return (phone.getState() == PhoneConstants.State.OFFHOOK) || + phone.getServiceStateTracker().isRadioOn(); } else { - Log.w(this, "onCreateOutgoingConnection, failed to turn on radio"); - emergencyConnection.setDisconnected( - DisconnectCauseUtil.toTelecomDisconnectCause( - android.telephony.DisconnectCause.POWER_OFF, - "Failed to turn on radio.")); - emergencyConnection.destroy(); + return (phone.getState() == PhoneConstants.State.OFFHOOK) || + serviceState == ServiceState.STATE_IN_SERVICE; } } }); // Return the still unconnected GsmConnection and wait for the Radios to boot before // connecting it to the underlying Phone. - return emergencyConnection; + return resultConnection; } else { if (!canAddCall() && !isEmergencyNumber) { Log.d(this, "onCreateOutgoingConnection, cannot add call ."); @@ -307,6 +276,76 @@ public class TelephonyConnectionService extends ConnectionService { } /** + * Whether the cellular radio is power off because the device is on Bluetooth. + */ + private boolean isRadioPowerDownOnBluetooth() { + final Context context = getApplicationContext(); + final boolean allowed = context.getResources().getBoolean( + R.bool.config_allowRadioPowerDownOnBluetooth); + final int cellOn = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.CELL_ON, + PhoneConstants.CELL_OFF_FLAG); + return (allowed && cellOn == PhoneConstants.CELL_ON_FLAG && !isRadioOn()); + } + + /** + * Handle the onComplete callback of RadioOnStateListener. + */ + private void handleOnComplete(boolean isRadioReady, + boolean isEmergencyNumber, + Connection originalConnection, + ConnectionRequest request, + String numberToDial, + Uri handle) { + // Make sure the Call has not already been canceled by the user. + if (originalConnection.getState() == Connection.STATE_DISCONNECTED) { + Log.i(this, "Emergency call disconnected before the outgoing call was " + + "placed. Skipping emergency call placement."); + return; + } + if (isRadioReady) { + // Get the right phone object since the radio has been turned on + // successfully. + final Phone phone = getPhoneForAccount(request.getAccountHandle(), + isEmergencyNumber); + // If the PhoneType of the Phone being used is different than the Default + // Phone, then we need create a new Connection using that PhoneType and + // replace it in Telecom. + if (phone.getPhoneType() != PhoneFactory.getDefaultPhone().getPhoneType()) { + Connection repConnection = getTelephonyConnection(request, numberToDial, + isEmergencyNumber, handle, phone); + // If there was a failure, the resulting connection will not be a + // TelephonyConnection, so don't place the call, just return! + if (repConnection instanceof TelephonyConnection) { + placeOutgoingConnection((TelephonyConnection) repConnection, phone, + request); + } + // Notify Telecom of the new Connection type. + // TODO: Switch out the underlying connection instead of creating a new + // one and causing UI Jank. + addExistingConnection(PhoneUtils.makePstnPhoneAccountHandle(phone), + repConnection); + // Remove the old connection from Telecom after. + originalConnection.setDisconnected( + DisconnectCauseUtil.toTelecomDisconnectCause( + android.telephony.DisconnectCause.OUTGOING_CANCELED, + "Reconnecting outgoing Emergency Call.")); + originalConnection.destroy(); + } else { + placeOutgoingConnection((TelephonyConnection) originalConnection, + phone, request); + } + } else { + Log.w(this, "onCreateOutgoingConnection, failed to turn on radio"); + originalConnection.setDisconnected( + DisconnectCauseUtil.toTelecomDisconnectCause( + android.telephony.DisconnectCause.POWER_OFF, + "Failed to turn on radio.")); + originalConnection.destroy(); + } + } + + /** * @return {@code true} if any other call is disabling the ability to add calls, {@code false} * otherwise. */ @@ -410,6 +449,10 @@ public class TelephonyConnectionService extends ConnectionService { "ServiceState.STATE_OUT_OF_SERVICE")); } case ServiceState.STATE_POWER_OFF: + // Don't disconnect if radio is power off because the device is on Bluetooth. + if (isRadioPowerDownOnBluetooth()) { + break; + } return Connection.createFailedConnection( DisconnectCauseUtil.toTelecomDisconnectCause( android.telephony.DisconnectCause.POWER_OFF, diff --git a/tests/src/com/android/services/telephony/EmergencyCallStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java index 64cf0528a..a8142e1d2 100644 --- a/tests/src/com/android/services/telephony/EmergencyCallStateListenerTest.java +++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java @@ -41,23 +41,23 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.when; /** - * Tests the EmergencyCallStateListener, which listens to one Phone and waits until its service + * Tests the RadioOnStateListener, which listens to one Phone and waits until its service * state changes to accepting emergency calls or in service. If it can not find a tower to camp onto * for emergency calls, then it will fail after a timeout period. */ @RunWith(AndroidJUnit4.class) -public class EmergencyCallStateListenerTest extends TelephonyTestBase { +public class RadioOnStateListenerTest extends TelephonyTestBase { private static final long TIMEOUT_MS = 100; @Mock Phone mMockPhone; - @Mock EmergencyCallStateListener.Callback mCallback; - EmergencyCallStateListener mListener; + @Mock RadioOnStateListener.Callback mCallback; + RadioOnStateListener mListener; @Before public void setUp() throws Exception { super.setUp(); - mListener = new EmergencyCallStateListener(); + mListener = new RadioOnStateListener(); } @After @@ -74,7 +74,7 @@ public class EmergencyCallStateListenerTest extends TelephonyTestBase { verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class)); verify(mMockPhone).registerForServiceStateChanged(any(Handler.class), - eq(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED), isNull()); + eq(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED), isNull()); } @Test @@ -85,7 +85,7 @@ public class EmergencyCallStateListenerTest extends TelephonyTestBase { mListener.waitForRadioOn(mMockPhone, mCallback); waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS); - mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED, + mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED, new AsyncResult(null, state, null)).sendToTarget(); waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS); @@ -102,7 +102,7 @@ public class EmergencyCallStateListenerTest extends TelephonyTestBase { mListener.waitForRadioOn(mMockPhone, mCallback); waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS); - mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED, + mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED, new AsyncResult(null, state, null)).sendToTarget(); waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS); @@ -120,11 +120,11 @@ public class EmergencyCallStateListenerTest extends TelephonyTestBase { // Don't expect any answer, since it is not the one that we want and the timeout for giving // up hasn't expired yet. - mListener.getHandler().obtainMessage(EmergencyCallStateListener.MSG_SERVICE_STATE_CHANGED, + mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED, new AsyncResult(null, state, null)).sendToTarget(); waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS); - verify(mCallback, never()).onComplete(any(EmergencyCallStateListener.class), anyBoolean()); + verify(mCallback, never()).onComplete(any(RadioOnStateListener.class), anyBoolean()); } @Test |