Merge changes from topic "update-ims-hold-logic"
* changes:
Cleanup and refactor IMS hold logic
Add extra line to avoid merge conflict
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 0cb4a56..33b83af 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -82,7 +82,6 @@
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -148,8 +147,6 @@
// Message codes used with mMainThreadHandler
private static final int CMD_HANDLE_PIN_MMI = 1;
- private static final int CMD_ANSWER_RINGING_CALL = 4;
- private static final int CMD_END_CALL = 5; // not used yet
private static final int CMD_TRANSMIT_APDU_LOGICAL_CHANNEL = 7;
private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 8;
private static final int CMD_OPEN_CHANNEL = 9;
@@ -376,41 +373,6 @@
break;
}
- case CMD_ANSWER_RINGING_CALL:
- request = (MainThreadRequest) msg.obj;
- int answer_subId = request.subId;
- answerRingingCallInternal(answer_subId);
- request.result = ""; // dummy result for notifying the waiting thread
- // Wake up the requesting thread
- notifyRequester(request);
- break;
-
- case CMD_END_CALL:
- request = (MainThreadRequest) msg.obj;
- int end_subId = request.subId;
- final boolean hungUp;
- Phone phone = getPhone(end_subId);
- if (phone == null) {
- if (DBG) log("CMD_END_CALL: no phone for id: " + end_subId);
- break;
- }
- int phoneType = phone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- // CDMA: If the user presses the Power button we treat it as
- // ending the complete call session
- hungUp = PhoneUtils.hangupRingingAndActive(getPhone(end_subId));
- } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- // GSM: End the call as per the Phone state
- hungUp = PhoneUtils.hangup(mCM);
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
- request.result = hungUp;
- // Wake up the requesting thread
- notifyRequester(request);
- break;
-
case CMD_TRANSMIT_APDU_LOGICAL_CHANNEL:
request = (MainThreadRequest) msg.obj;
iccArgument = (IccAPDUArgument) request.argument;
@@ -1244,7 +1206,7 @@
* @return true is a call was ended
*/
public boolean endCall() {
- return endCallForSubscriber(getDefaultSubscription());
+ return false;
}
/**
@@ -1252,75 +1214,15 @@
* @return true is a call was ended
*/
public boolean endCallForSubscriber(int subId) {
- if (mApp.checkCallingOrSelfPermission(permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED) {
- Log.i(LOG_TAG, "endCall: called without modify phone state.");
- EventLog.writeEvent(0x534e4554, "67862398", -1, "");
- throw new SecurityException("MODIFY_PHONE_STATE permission required.");
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- return (Boolean) sendRequest(CMD_END_CALL, null, new Integer(subId));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ return false;
}
public void answerRingingCall() {
- answerRingingCallForSubscriber(getDefaultSubscription());
+ // Deprecated.
}
public void answerRingingCallForSubscriber(int subId) {
- if (DBG) log("answerRingingCall...");
- // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
- // but that can probably wait till the big TelephonyManager API overhaul.
- // For now, protect this call with the MODIFY_PHONE_STATE permission.
- enforceModifyPermission();
-
- final long identity = Binder.clearCallingIdentity();
- try {
- sendRequest(CMD_ANSWER_RINGING_CALL, null, new Integer(subId));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Make the actual telephony calls to implement answerRingingCall().
- * This should only be called from the main thread of the Phone app.
- * @see #answerRingingCall
- *
- * TODO: it would be nice to return true if we answered the call, or
- * false if there wasn't actually a ringing incoming call, or some
- * other error occurred. (In other words, pass back the return value
- * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
- * But that would require calling this method via sendRequest() rather
- * than sendRequestAsync(), and right now we don't actually *need* that
- * return value, so let's just return void for now.
- */
- private void answerRingingCallInternal(int subId) {
- final boolean hasRingingCall = !getPhone(subId).getRingingCall().isIdle();
- if (hasRingingCall) {
- final boolean hasActiveCall = !getPhone(subId).getForegroundCall().isIdle();
- final boolean hasHoldingCall = !getPhone(subId).getBackgroundCall().isIdle();
- if (hasActiveCall && hasHoldingCall) {
- // Both lines are in use!
- // TODO: provide a flag to let the caller specify what
- // policy to use if both lines are in use. (The current
- // behavior is hardwired to "answer incoming, end ongoing",
- // which is how the CALL button is specced to behave.)
- PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
- return;
- } else {
- // answerCall() will automatically hold the current active
- // call, if there is one.
- PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
- return;
- }
- } else {
- // No call was ringing.
- return;
- }
+ // Deprecated
}
/**
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 712b8cb..c5625e9 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -19,7 +19,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
-import android.bluetooth.IBluetoothHeadsetPhone;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -30,7 +29,6 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.VideoProfile;
@@ -106,9 +104,6 @@
/** Phone state changed event*/
private static final int PHONE_STATE_CHANGED = -1;
- /** check status then decide whether answerCall */
- private static final int MSG_CHECK_STATUS_ANSWERCALL = 100;
-
/** poll phone DISCONNECTING status interval */
private static final int DISCONNECTING_POLLING_INTERVAL_MS = 200;
@@ -125,15 +120,6 @@
*/
private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
- private static class FgRingCalls {
- private Call fgCall;
- private Call ringing;
- public FgRingCalls(Call fg, Call ring) {
- fgCall = fg;
- ringing = ring;
- }
- }
-
/** USSD information used to aggregate all USSD messages */
private static AlertDialog sUssdDialog = null;
private static StringBuilder sUssdMsg = new StringBuilder();
@@ -147,34 +133,6 @@
* Mute settings for each connection as needed.
*/
private static class ConnectionHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_CHECK_STATUS_ANSWERCALL:
- FgRingCalls frC = (FgRingCalls) msg.obj;
- // wait for finishing disconnecting
- // before check the ringing call state
- if ((frC.fgCall != null) &&
- (frC.fgCall.getState() == Call.State.DISCONNECTING) &&
- (msg.arg1 < DISCONNECTING_POLLING_TIMES_LIMIT)) {
- Message retryMsg =
- mConnectionHandler.obtainMessage(MSG_CHECK_STATUS_ANSWERCALL);
- retryMsg.arg1 = 1 + msg.arg1;
- retryMsg.obj = msg.obj;
- mConnectionHandler.sendMessageDelayed(retryMsg,
- DISCONNECTING_POLLING_INTERVAL_MS);
- // since hangupActiveCall() also accepts the ringing call
- // check if the ringing call was already answered or not
- // only answer it when the call still is ringing
- } else if (frC.ringing.isRinging()) {
- if (msg.arg1 == DISCONNECTING_POLLING_TIMES_LIMIT) {
- Log.e(LOG_TAG, "DISCONNECTING time out");
- }
- answerCall(frC.ringing);
- }
- break;
- }
- }
}
/**
@@ -195,294 +153,6 @@
}
/**
- * Answer the currently-ringing call.
- *
- * @return true if we answered the call, or false if there wasn't
- * actually a ringing incoming call, or some other error occurred.
- *
- * @see #answerAndEndHolding(CallManager, Call)
- * @see #answerAndEndActive(CallManager, Call)
- */
- /* package */ static boolean answerCall(Call ringingCall) {
- log("answerCall(" + ringingCall + ")...");
- final PhoneGlobals app = PhoneGlobals.getInstance();
- final CallNotifier notifier = app.notifier;
-
- final Phone phone = ringingCall.getPhone();
- final boolean phoneIsCdma = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA);
- boolean answered = false;
- IBluetoothHeadsetPhone btPhone = null;
-
- if (phoneIsCdma) {
- // Stop any signalInfo tone being played when a Call waiting gets answered
- if (ringingCall.getState() == Call.State.WAITING) {
- notifier.stopSignalInfoTone();
- }
- }
-
- if (ringingCall != null && ringingCall.isRinging()) {
- if (DBG) log("answerCall: call state = " + ringingCall.getState());
- try {
- if (phoneIsCdma) {
- if (app.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.IDLE) {
- // This is the FIRST incoming call being answered.
- // Set the Phone Call State to SINGLE_ACTIVE
- app.cdmaPhoneCallState.setCurrentCallState(
- CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE);
- } else {
- // This is the CALL WAITING call being answered.
- // Set the Phone Call State to CONF_CALL
- app.cdmaPhoneCallState.setCurrentCallState(
- CdmaPhoneCallState.PhoneCallState.CONF_CALL);
- // Enable "Add Call" option after answering a Call Waiting as the user
- // should be allowed to add another call in case one of the parties
- // drops off
- app.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true);
- }
- }
-
- final boolean isRealIncomingCall = isRealIncomingCall(ringingCall.getState());
-
- //if (DBG) log("sPhone.acceptCall");
- app.mCM.acceptCall(ringingCall);
- answered = true;
-
- setAudioMode();
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "answerCall: caught " + ex, ex);
-
- if (phoneIsCdma) {
- // restore the cdmaPhoneCallState and btPhone.cdmaSetSecondCallState:
- app.cdmaPhoneCallState.setCurrentCallState(
- app.cdmaPhoneCallState.getPreviousCallState());
- if (btPhone != null) {
- try {
- btPhone.cdmaSetSecondCallState(false);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
- }
- }
- }
- }
- }
- return answered;
- }
-
- /**
- * Hangs up all active calls.
- */
- static void hangupAllCalls(CallManager cm) {
- final Call ringing = cm.getFirstActiveRingingCall();
- final Call fg = cm.getActiveFgCall();
- final Call bg = cm.getFirstActiveBgCall();
-
- // We go in reverse order, BG->FG->RINGING because hanging up a ringing call or an active
- // call can move a bg call to a fg call which would force us to loop over each call
- // several times. This ordering works best to ensure we dont have any more calls.
- if (bg != null && !bg.isIdle()) {
- hangup(bg);
- }
- if (fg != null && !fg.isIdle()) {
- hangup(fg);
- }
- if (ringing != null && !ringing.isIdle()) {
- hangupRingingCall(fg);
- }
- }
-
- /**
- * Smart "hang up" helper method which hangs up exactly one connection,
- * based on the current Phone state, as follows:
- * <ul>
- * <li>If there's a ringing call, hang that up.
- * <li>Else if there's a foreground call, hang that up.
- * <li>Else if there's a background call, hang that up.
- * <li>Otherwise do nothing.
- * </ul>
- * @return true if we successfully hung up, or false
- * if there were no active calls at all.
- */
- static boolean hangup(CallManager cm) {
- boolean hungup = false;
- Call ringing = cm.getFirstActiveRingingCall();
- Call fg = cm.getActiveFgCall();
- Call bg = cm.getFirstActiveBgCall();
-
- if (!ringing.isIdle()) {
- log("hangup(): hanging up ringing call");
- hungup = hangupRingingCall(ringing);
- } else if (!fg.isIdle()) {
- log("hangup(): hanging up foreground call");
- hungup = hangup(fg);
- } else if (!bg.isIdle()) {
- log("hangup(): hanging up background call");
- hungup = hangup(bg);
- } else {
- // No call to hang up! This is unlikely in normal usage,
- // since the UI shouldn't be providing an "End call" button in
- // the first place. (But it *can* happen, rarely, if an
- // active call happens to disconnect on its own right when the
- // user is trying to hang up..)
- log("hangup(): no active call to hang up");
- }
- if (DBG) log("==> hungup = " + hungup);
-
- return hungup;
- }
-
- static boolean hangupRingingCall(Call ringing) {
- if (DBG) log("hangup ringing call");
- int phoneType = ringing.getPhone().getPhoneType();
- Call.State state = ringing.getState();
-
- if (state == Call.State.INCOMING) {
- // Regular incoming call (with no other active calls)
- log("hangupRingingCall(): regular incoming call: hangup()");
- return hangup(ringing);
- } else {
- // Unexpected state: the ringing call isn't INCOMING or
- // WAITING, so there's no reason to have called
- // hangupRingingCall() in the first place.
- // (Presumably the incoming call went away at the exact moment
- // we got here, so just do nothing.)
- Log.w(LOG_TAG, "hangupRingingCall: no INCOMING or WAITING call");
- return false;
- }
- }
-
- static boolean hangupActiveCall(Call foreground) {
- if (DBG) log("hangup active call");
- return hangup(foreground);
- }
-
- static boolean hangupHoldingCall(Call background) {
- if (DBG) log("hangup holding call");
- return hangup(background);
- }
-
- /**
- * Used in CDMA phones to end the complete Call session
- * @param phone the Phone object.
- * @return true if *any* call was successfully hung up
- */
- static boolean hangupRingingAndActive(Phone phone) {
- boolean hungUpRingingCall = false;
- boolean hungUpFgCall = false;
- Call ringingCall = phone.getRingingCall();
- Call fgCall = phone.getForegroundCall();
-
- // Hang up any Ringing Call
- if (!ringingCall.isIdle()) {
- log("hangupRingingAndActive: Hang up Ringing Call");
- hungUpRingingCall = hangupRingingCall(ringingCall);
- }
-
- // Hang up any Active Call
- if (!fgCall.isIdle()) {
- log("hangupRingingAndActive: Hang up Foreground Call");
- hungUpFgCall = hangupActiveCall(fgCall);
- }
-
- return hungUpRingingCall || hungUpFgCall;
- }
-
- /**
- * Trivial wrapper around Call.hangup(), except that we return a
- * boolean success code rather than throwing CallStateException on
- * failure.
- *
- * @return true if the call was successfully hung up, or false
- * if the call wasn't actually active.
- */
- static boolean hangup(Call call) {
- try {
- CallManager cm = PhoneGlobals.getInstance().mCM;
-
- if (call.getState() == Call.State.ACTIVE && cm.hasActiveBgCall()) {
- // handle foreground call hangup while there is background call
- log("- hangup(Call): hangupForegroundResumeBackground...");
- cm.hangupForegroundResumeBackground(cm.getFirstActiveBgCall());
- } else {
- log("- hangup(Call): regular hangup()...");
- call.hangup();
- }
- return true;
- } catch (CallStateException ex) {
- Log.e(LOG_TAG, "Call hangup: caught " + ex, ex);
- }
-
- return false;
- }
-
- /**
- * Trivial wrapper around Connection.hangup(), except that we silently
- * do nothing (rather than throwing CallStateException) if the
- * connection wasn't actually active.
- */
- static void hangup(Connection c) {
- try {
- if (c != null) {
- c.hangup();
- }
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "Connection hangup: caught " + ex, ex);
- }
- }
-
- static boolean answerAndEndHolding(CallManager cm, Call ringing) {
- if (DBG) log("end holding & answer waiting: 1");
- if (!hangupHoldingCall(cm.getFirstActiveBgCall())) {
- Log.e(LOG_TAG, "end holding failed!");
- return false;
- }
-
- if (DBG) log("end holding & answer waiting: 2");
- return answerCall(ringing);
-
- }
-
- /**
- * Answers the incoming call specified by "ringing", and ends the currently active phone call.
- *
- * This method is useful when's there's an incoming call which we cannot manage with the
- * current call. e.g. when you are having a phone call with CDMA network and has received
- * a SIP call, then we won't expect our telephony can manage those phone calls simultaneously.
- * Note that some types of network may allow multiple phone calls at once; GSM allows to hold
- * an ongoing phone call, so we don't need to end the active call. The caller of this method
- * needs to check if the network allows multiple phone calls or not.
- *
- * @see #answerCall(Call)
- * @see InCallScreen#internalAnswerCall()
- */
- /* package */ static boolean answerAndEndActive(CallManager cm, Call ringing) {
- if (DBG) log("answerAndEndActive()...");
-
- // Unlike the answerCall() method, we *don't* need to stop the
- // ringer or change audio modes here since the user is already
- // in-call, which means that the audio mode is already set
- // correctly, and that we wouldn't have started the ringer in the
- // first place.
-
- // hanging up the active call also accepts the waiting call
- // while active call and waiting call are from the same phone
- // i.e. both from GSM phone
- Call fgCall = cm.getActiveFgCall();
- if (!hangupActiveCall(fgCall)) {
- Log.w(LOG_TAG, "end active call failed!");
- return false;
- }
-
- mConnectionHandler.removeMessages(MSG_CHECK_STATUS_ANSWERCALL);
- Message msg = mConnectionHandler.obtainMessage(MSG_CHECK_STATUS_ANSWERCALL);
- msg.arg1 = 1;
- msg.obj = new FgRingCalls(fgCall, ringing);
- mConnectionHandler.sendMessage(msg);
-
- return true;
- }
-
- /**
* For a CDMA phone, advance the call state upon making a new
* outgoing call.
*
@@ -676,87 +346,6 @@
return builder.toString();
}
- /**
- * Wrapper function to control when to send an empty Flash command to the network.
- * Mainly needed for CDMA networks, such as scenarios when we need to send a blank flash
- * to the network prior to placing a 3-way call for it to be successful.
- */
- static void sendEmptyFlash(Phone phone) {
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- Call fgCall = phone.getForegroundCall();
- if (fgCall.getState() == Call.State.ACTIVE) {
- // Send the empty flash
- if (DBG) Log.d(LOG_TAG, "onReceive: (CDMA) sending empty flash to network");
- switchHoldingAndActive(phone.getBackgroundCall());
- }
- }
- }
-
- static void swap() {
- final PhoneGlobals mApp = PhoneGlobals.getInstance();
- if (!okToSwapCalls(mApp.mCM)) {
- // TODO: throw an error instead?
- return;
- }
-
- // Swap the fg and bg calls.
- // In the future we may provide some way for user to choose among
- // multiple background calls, for now, always act on the first background call.
- PhoneUtils.switchHoldingAndActive(mApp.mCM.getFirstActiveBgCall());
- }
-
- /**
- * @param heldCall is the background call want to be swapped
- */
- static void switchHoldingAndActive(Call heldCall) {
- log("switchHoldingAndActive()...");
- try {
- CallManager cm = PhoneGlobals.getInstance().mCM;
- if (heldCall.isIdle()) {
- // no heldCall, so it is to hold active call
- cm.switchHoldingAndActive(cm.getFgPhone().getBackgroundCall());
- } else {
- // has particular heldCall, so to switch
- cm.switchHoldingAndActive(heldCall);
- }
- setAudioMode(cm);
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "switchHoldingAndActive: caught " + ex, ex);
- }
- }
-
- static void mergeCalls() {
- mergeCalls(PhoneGlobals.getInstance().mCM);
- }
-
- static void mergeCalls(CallManager cm) {
- int phoneType = cm.getFgPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- log("mergeCalls(): CDMA...");
- PhoneGlobals app = PhoneGlobals.getInstance();
- if (app.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
- // Set the Phone Call State to conference
- app.cdmaPhoneCallState.setCurrentCallState(
- CdmaPhoneCallState.PhoneCallState.CONF_CALL);
-
- // Send flash cmd
- // TODO: Need to change the call from switchHoldingAndActive to
- // something meaningful as we are not actually trying to swap calls but
- // instead are merging two calls by sending a Flash command.
- log("- sending flash...");
- switchHoldingAndActive(cm.getFirstActiveBgCall());
- }
- } else {
- try {
- log("mergeCalls(): calling cm.conference()...");
- cm.conference(cm.getFirstActiveBgCall());
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "mergeCalls: caught " + ex, ex);
- }
- }
- }
-
static void separateCall(Connection c) {
try {
if (DBG) log("separateCall: " + toLogSafePhoneNumber(c.getAddress()));
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 6904874..31fe68f 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -883,6 +883,13 @@
Log.v(this, "Holding active call");
try {
Phone phone = mOriginalConnection.getCall().getPhone();
+
+ // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ ImsPhone imsPhone = (ImsPhone) phone;
+ imsPhone.holdActiveCall();
+ return;
+ }
Call ringingCall = phone.getRingingCall();
// Although the method says switchHoldingAndActive, it eventually calls a RIL method
@@ -912,6 +919,13 @@
Log.v(this, "performUnhold");
if (Call.State.HOLDING == mConnectionState) {
try {
+ Phone phone = mOriginalConnection.getCall().getPhone();
+ // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ ImsPhone imsPhone = (ImsPhone) phone;
+ imsPhone.unholdHeldCall();
+ return;
+ }
// Here's the deal--Telephony hold/unhold is weird because whenever there exists
// more than one call, one of them must always be active. In other words, if you
// have an active call and holding call, and you put the active call on hold, it