summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWei Liu <luciferleo@google.com>2016-12-14 19:36:52 -0800
committerWei Liu <luciferleo@google.com>2017-02-06 16:01:08 -0800
commit5e0ffbe1aca1366c00dd565cdb83ba01c5229d3d (patch)
tree1d2d21d56ab2d22ed2f1ae0ab33728893ff38157
parent73a5e9ae0ba566ac6f0bbf40c643ef3e08eae917 (diff)
downloadandroid_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.xml3
-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.java155
-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