blob: 7a026fa9a97783d4eb7b6e6b44bad24fa70058e7 [file] [log] [blame]
/*
* Copyright (C) 2010 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.internal.telephony;
import com.android.internal.telephony.sip.SipPhone;
import android.content.Context;
import android.media.AudioManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.RegistrantList;
import android.os.Registrant;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @hide
*
* CallManager class provides an abstract layer for PhoneApp to access
* and control calls. It implements Phone interface.
*
* CallManager provides call and connection control as well as
* channel capability.
*
* There are three categories of APIs CallManager provided
*
* 1. Call control and operation, such as dial() and hangup()
* 2. Channel capabilities, such as CanConference()
* 3. Register notification
*
*
*/
public final class CallManager {
private static final String LOG_TAG ="CallManager";
private static final boolean DBG = true;
private static final boolean VDBG = true;
private static final int EVENT_DISCONNECT = 100;
private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 101;
private static final int EVENT_NEW_RINGING_CONNECTION = 102;
private static final int EVENT_UNKNOWN_CONNECTION = 103;
private static final int EVENT_INCOMING_RING = 104;
private static final int EVENT_RINGBACK_TONE = 105;
private static final int EVENT_IN_CALL_VOICE_PRIVACY_ON = 106;
private static final int EVENT_IN_CALL_VOICE_PRIVACY_OFF = 107;
private static final int EVENT_CALL_WAITING = 108;
private static final int EVENT_DISPLAY_INFO = 109;
private static final int EVENT_SIGNAL_INFO = 110;
private static final int EVENT_CDMA_OTA_STATUS_CHANGE = 111;
private static final int EVENT_RESEND_INCALL_MUTE = 112;
private static final int EVENT_MMI_INITIATE = 113;
private static final int EVENT_MMI_COMPLETE = 114;
private static final int EVENT_ECM_TIMER_RESET = 115;
private static final int EVENT_SUBSCRIPTION_INFO_READY = 116;
private static final int EVENT_SUPP_SERVICE_FAILED = 117;
private static final int EVENT_SERVICE_STATE_CHANGED = 118;
private static final int EVENT_POST_DIAL_CHARACTER = 119;
// Singleton instance
private static final CallManager INSTANCE = new CallManager();
// list of registered phones, which are PhoneBase objs
private final ArrayList<Phone> mPhones;
// list of supported ringing calls
private final ArrayList<Call> mRingingCalls;
// list of supported background calls
private final ArrayList<Call> mBackgroundCalls;
// list of supported foreground calls
private final ArrayList<Call> mForegroundCalls;
// empty connection list
private final ArrayList<Connection> emptyConnections = new ArrayList<Connection>();
// default phone as the first phone registered, which is PhoneBase obj
private Phone mDefaultPhone;
// state registrants
protected final RegistrantList mPreciseCallStateRegistrants
= new RegistrantList();
protected final RegistrantList mNewRingingConnectionRegistrants
= new RegistrantList();
protected final RegistrantList mIncomingRingRegistrants
= new RegistrantList();
protected final RegistrantList mDisconnectRegistrants
= new RegistrantList();
protected final RegistrantList mMmiRegistrants
= new RegistrantList();
protected final RegistrantList mUnknownConnectionRegistrants
= new RegistrantList();
protected final RegistrantList mRingbackToneRegistrants
= new RegistrantList();
protected final RegistrantList mInCallVoicePrivacyOnRegistrants
= new RegistrantList();
protected final RegistrantList mInCallVoicePrivacyOffRegistrants
= new RegistrantList();
protected final RegistrantList mCallWaitingRegistrants
= new RegistrantList();
protected final RegistrantList mDisplayInfoRegistrants
= new RegistrantList();
protected final RegistrantList mSignalInfoRegistrants
= new RegistrantList();
protected final RegistrantList mCdmaOtaStatusChangeRegistrants
= new RegistrantList();
protected final RegistrantList mResendIncallMuteRegistrants
= new RegistrantList();
protected final RegistrantList mMmiInitiateRegistrants
= new RegistrantList();
protected final RegistrantList mMmiCompleteRegistrants
= new RegistrantList();
protected final RegistrantList mEcmTimerResetRegistrants
= new RegistrantList();
protected final RegistrantList mSubscriptionInfoReadyRegistrants
= new RegistrantList();
protected final RegistrantList mSuppServiceFailedRegistrants
= new RegistrantList();
protected final RegistrantList mServiceStateChangedRegistrants
= new RegistrantList();
protected final RegistrantList mPostDialCharacterRegistrants
= new RegistrantList();
private CallManager() {
mPhones = new ArrayList<Phone>();
mRingingCalls = new ArrayList<Call>();
mBackgroundCalls = new ArrayList<Call>();
mForegroundCalls = new ArrayList<Call>();
mDefaultPhone = null;
}
/**
* get singleton instance of CallManager
* @return CallManager
*/
public static CallManager getInstance() {
return INSTANCE;
}
/**
* Get the corresponding PhoneBase obj
*
* @param phone a Phone object
* @return the corresponding PhoneBase obj in Phone if Phone
* is a PhoneProxy obj
* or the Phone itself if Phone is not a PhoneProxy obj
*/
private static Phone getPhoneBase(Phone phone) {
if (phone instanceof PhoneProxy) {
return phone.getForegroundCall().getPhone();
}
return phone;
}
/**
* Check if two phones refer to the same PhoneBase obj
*
* Note: PhoneBase, not PhoneProxy, is to be used inside of CallManager
*
* Both PhoneBase and PhoneProxy implement Phone interface, so
* they have same phone APIs, such as dial(). The real implementation, for
* example in GSM, is in GSMPhone as extend from PhoneBase, so that
* foregroundCall.getPhone() returns GSMPhone obj. On the other hand,
* PhoneFactory.getDefaultPhone() returns PhoneProxy obj, which has a class
* member of GSMPhone.
*
* So for phone returned by PhoneFacotry, which is used by PhoneApp,
* phone.getForegroundCall().getPhone() != phone
* but
* isSamePhone(phone, phone.getForegroundCall().getPhone()) == true
*
* @param p1 is the first Phone obj
* @param p2 is the second Phone obj
* @return true if p1 and p2 refer to the same phone
*/
public static boolean isSamePhone(Phone p1, Phone p2) {
return (getPhoneBase(p1) == getPhoneBase(p2));
}
/**
* Returns all the registered phone objects.
* @return all the registered phone objects.
*/
public List<Phone> getAllPhones() {
return Collections.unmodifiableList(mPhones);
}
/**
* Get current coarse-grained voice call state.
* If the Call Manager has an active call and call waiting occurs,
* then the phone state is RINGING not OFFHOOK
*
*/
public Phone.State getState() {
Phone.State s = Phone.State.IDLE;
for (Phone phone : mPhones) {
if (phone.getState() == Phone.State.RINGING) {
s = Phone.State.RINGING;
} else if (phone.getState() == Phone.State.OFFHOOK) {
if (s == Phone.State.IDLE) s = Phone.State.OFFHOOK;
}
}
return s;
}
/**
* @return the service state of CallManager, which represents the
* highest priority state of all the service states of phones
*
* The priority is defined as
*
* STATE_IN_SERIVCE > STATE_OUT_OF_SERIVCE > STATE_EMERGENCY > STATE_POWER_OFF
*
*/
public int getServiceState() {
int resultState = ServiceState.STATE_OUT_OF_SERVICE;
for (Phone phone : mPhones) {
int serviceState = phone.getServiceState().getState();
if (serviceState == ServiceState.STATE_IN_SERVICE) {
// IN_SERVICE has the highest priority
resultState = serviceState;
break;
} else if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
// OUT_OF_SERVICE replaces EMERGENCY_ONLY and POWER_OFF
// Note: EMERGENCY_ONLY is not in use at this moment
if ( resultState == ServiceState.STATE_EMERGENCY_ONLY ||
resultState == ServiceState.STATE_POWER_OFF) {
resultState = serviceState;
}
} else if (serviceState == ServiceState.STATE_EMERGENCY_ONLY) {
if (resultState == ServiceState.STATE_POWER_OFF) {
resultState = serviceState;
}
}
}
return resultState;
}
/**
* Register phone to CallManager
* @param phone to be registered
* @return true if register successfully
*/
public boolean registerPhone(Phone phone) {
Phone basePhone = getPhoneBase(phone);
if (basePhone != null && !mPhones.contains(basePhone)) {
if (VDBG) {
Log.d(LOG_TAG, "registerPhone(" +
phone.getPhoneName() + " " + phone + ")");
}
if (mPhones.isEmpty()) {
mDefaultPhone = basePhone;
}
mPhones.add(basePhone);
mRingingCalls.add(basePhone.getRingingCall());
mBackgroundCalls.add(basePhone.getBackgroundCall());
mForegroundCalls.add(basePhone.getForegroundCall());
registerForPhoneStates(basePhone);
return true;
}
return false;
}
/**
* unregister phone from CallManager
* @param phone to be unregistered
*/
public void unregisterPhone(Phone phone) {
Phone basePhone = getPhoneBase(phone);
if (basePhone != null && mPhones.contains(basePhone)) {
if (VDBG) {
Log.d(LOG_TAG, "unregisterPhone(" +
phone.getPhoneName() + " " + phone + ")");
}
mPhones.remove(basePhone);
mRingingCalls.remove(basePhone.getRingingCall());
mBackgroundCalls.remove(basePhone.getBackgroundCall());
mForegroundCalls.remove(basePhone.getForegroundCall());
unregisterForPhoneStates(basePhone);
if (basePhone == mDefaultPhone) {
if (mPhones.isEmpty()) {
mDefaultPhone = null;
} else {
mDefaultPhone = mPhones.get(0);
}
}
}
}
/**
* return the default phone or null if no phone available
*/
public Phone getDefaultPhone() {
return mDefaultPhone;
}
/**
* @return the phone associated with the foreground call
*/
public Phone getFgPhone() {
return getActiveFgCall().getPhone();
}
/**
* @return the phone associated with the background call
*/
public Phone getBgPhone() {
return getFirstActiveBgCall().getPhone();
}
/**
* @return the phone associated with the ringing call
*/
public Phone getRingingPhone() {
return getFirstActiveRingingCall().getPhone();
}
public void setAudioMode() {
Context context = getContext();
if (context == null) return;
AudioManager audioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);
int mode = AudioManager.MODE_NORMAL;
switch (getState()) {
case RINGING:
mode = AudioManager.MODE_RINGTONE;
break;
case OFFHOOK:
Phone fgPhone = getFgPhone();
if (!(fgPhone instanceof SipPhone)) {
mode = AudioManager.MODE_IN_CALL;
}
break;
}
// calling audioManager.setMode() multiple times in a short period of
// time seems to break the audio recorder in in-call mode
if (audioManager.getMode() != mode) audioManager.setMode(mode);
}
private Context getContext() {
Phone defaultPhone = getDefaultPhone();
return ((defaultPhone == null) ? null : defaultPhone.getContext());
}
private void registerForPhoneStates(Phone phone) {
// for common events supported by all phones
phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null);
phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
phone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null);
phone.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, null);
phone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING, null);
phone.registerForRingbackTone(mHandler, EVENT_RINGBACK_TONE, null);
phone.registerForInCallVoicePrivacyOn(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_ON, null);
phone.registerForInCallVoicePrivacyOff(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, null);
phone.registerForDisplayInfo(mHandler, EVENT_DISPLAY_INFO, null);
phone.registerForSignalInfo(mHandler, EVENT_SIGNAL_INFO, null);
phone.registerForResendIncallMute(mHandler, EVENT_RESEND_INCALL_MUTE, null);
phone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null);
phone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null);
phone.registerForSuppServiceFailed(mHandler, EVENT_SUPP_SERVICE_FAILED, null);
phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, null);
// for events supported only by GSM and CDMA phone
if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM ||
phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
phone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL_CHARACTER, null);
}
// for events supported only by CDMA phone
if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA ){
phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null);
phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null);
phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null);
phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null);
}
}
private void unregisterForPhoneStates(Phone phone) {
// for common events supported by all phones
phone.unregisterForPreciseCallStateChanged(mHandler);
phone.unregisterForDisconnect(mHandler);
phone.unregisterForNewRingingConnection(mHandler);
phone.unregisterForUnknownConnection(mHandler);
phone.unregisterForIncomingRing(mHandler);
phone.unregisterForRingbackTone(mHandler);
phone.unregisterForInCallVoicePrivacyOn(mHandler);
phone.unregisterForInCallVoicePrivacyOff(mHandler);
phone.unregisterForDisplayInfo(mHandler);
phone.unregisterForSignalInfo(mHandler);
phone.unregisterForResendIncallMute(mHandler);
phone.unregisterForMmiInitiate(mHandler);
phone.unregisterForMmiComplete(mHandler);
phone.unregisterForSuppServiceFailed(mHandler);
phone.unregisterForServiceStateChanged(mHandler);
// for events supported only by GSM and CDMA phone
if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM ||
phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {
phone.setOnPostDialCharacter(null, EVENT_POST_DIAL_CHARACTER, null);
}
// for events supported only by CDMA phone
if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA ){
phone.unregisterForCdmaOtaStatusChange(mHandler);
phone.unregisterForSubscriptionInfoReady(mHandler);
phone.unregisterForCallWaiting(mHandler);
phone.unregisterForEcmTimerReset(mHandler);
}
}
/**
* Answers a ringing or waiting call.
*
* Active call, if any, go on hold.
* If active call can't be held, i.e., a background call of the same channel exists,
* the active call will be hang up.
*
* Answering occurs asynchronously, and final notification occurs via
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException when call is not ringing or waiting
*/
public void acceptCall(Call ringingCall) throws CallStateException {
Phone ringingPhone = ringingCall.getPhone();
if (VDBG) {
Log.d(LOG_TAG, "acceptCall(" +ringingCall + " from " + ringingCall.getPhone() + ")");
Log.d(LOG_TAG, this.toString());
}
if ( hasActiveFgCall() ) {
Phone activePhone = getActiveFgCall().getPhone();
boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
boolean sameChannel = (activePhone == ringingPhone);
if (DBG) {
Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + sameChannel);
}
if (sameChannel && hasBgCall) {
getActiveFgCall().hangup();
} else if (!sameChannel && !hasBgCall) {
activePhone.switchHoldingAndActive();
} else if (!sameChannel && hasBgCall) {
getActiveFgCall().hangup();
}
}
ringingPhone.acceptCall();
if (VDBG) {
Log.d(LOG_TAG, "End acceptCall(" +ringingCall + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Reject (ignore) a ringing call. In GSM, this means UDUB
* (User Determined User Busy). Reject occurs asynchronously,
* and final notification occurs via
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException when no call is ringing or waiting
*/
public void rejectCall(Call ringingCall) throws CallStateException {
if (VDBG) {
Log.d(LOG_TAG, "rejectCall(" +ringingCall + ")");
Log.d(LOG_TAG, this.toString());
}
Phone ringingPhone = ringingCall.getPhone();
ringingPhone.rejectCall();
if (VDBG) {
Log.d(LOG_TAG, "End rejectCall(" +ringingCall + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Places active call on hold, and makes held call active.
* Switch occurs asynchronously and may fail.
*
* There are 4 scenarios
* 1. only active call but no held call, aka, hold
* 2. no active call but only held call, aka, unhold
* 3. both active and held calls from same phone, aka, swap
* 4. active and held calls from different phones, aka, phone swap
*
* Final notification occurs via
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if active call is ringing, waiting, or
* dialing/alerting, or heldCall can't be active.
* In these cases, this operation may not be performed.
*/
public void switchHoldingAndActive(Call heldCall) throws CallStateException {
Phone activePhone = null;
Phone heldPhone = null;
if (VDBG) {
Log.d(LOG_TAG, "switchHoldingAndActive(" +heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) {
activePhone = getActiveFgCall().getPhone();
}
if (heldCall != null) {
heldPhone = heldCall.getPhone();
}
if (activePhone != null) {
activePhone.switchHoldingAndActive();
}
if (heldPhone != null && heldPhone != activePhone) {
heldPhone.switchHoldingAndActive();
}
if (VDBG) {
Log.d(LOG_TAG, "End switchHoldingAndActive(" +heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Hangup foreground call and resume the specific background call
*
* Note: this is noop if there is no foreground call or the heldCall is null
*
* @param heldCall to become foreground
* @throws CallStateException
*/
public void hangupForegroundResumeBackground(Call heldCall) throws CallStateException {
Phone foregroundPhone = null;
Phone backgroundPhone = null;
if (VDBG) {
Log.d(LOG_TAG, "hangupForegroundResumeBackground(" +heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) {
foregroundPhone = getFgPhone();
if (heldCall != null) {
backgroundPhone = heldCall.getPhone();
if (foregroundPhone == backgroundPhone) {
getActiveFgCall().hangup();
} else {
// the call to be hangup and resumed belongs to different phones
getActiveFgCall().hangup();
switchHoldingAndActive(heldCall);
}
}
}
if (VDBG) {
Log.d(LOG_TAG, "End hangupForegroundResumeBackground(" +heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Whether or not the phone can conference in the current phone
* state--that is, one call holding and one call active.
* @return true if the phone can conference; false otherwise.
*/
public boolean canConference(Call heldCall) {
Phone activePhone = null;
Phone heldPhone = null;
if (hasActiveFgCall()) {
activePhone = getActiveFgCall().getPhone();
}
if (heldCall != null) {
heldPhone = heldCall.getPhone();
}
return heldPhone.getClass().equals(activePhone.getClass());
}
/**
* Conferences holding and active. Conference occurs asynchronously
* and may fail. Final notification occurs via
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if canConference() would return false.
* In these cases, this operation may not be performed.
*/
public void conference(Call heldCall) throws CallStateException {
if (VDBG) {
Log.d(LOG_TAG, "conference(" +heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
Phone fgPhone = getFgPhone();
if (fgPhone instanceof SipPhone) {
((SipPhone) fgPhone).conference(heldCall);
} else if (canConference(heldCall)) {
fgPhone.conference();
} else {
throw(new CallStateException("Can't conference foreground and selected background call"));
}
if (VDBG) {
Log.d(LOG_TAG, "End conference(" +heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Initiate a new voice connection. This happens asynchronously, so you
* cannot assume the audio path is connected (or a call index has been
* assigned) until PhoneStateChanged notification has occurred.
*
* @exception CallStateException if a new outgoing call is not currently
* possible because no more call slots exist or a call exists that is
* dialing, alerting, ringing, or waiting. Other errors are
* handled asynchronously.
*/
public Connection dial(Phone phone, String dialString) throws CallStateException {
Phone basePhone = getPhoneBase(phone);
Connection result;
if (VDBG) {
Log.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
Log.d(LOG_TAG, this.toString());
}
if ( hasActiveFgCall() ) {
Phone activePhone = getActiveFgCall().getPhone();
boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
if (DBG) {
Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == basePhone));
}
if (activePhone != basePhone) {
if (hasBgCall) {
Log.d(LOG_TAG, "Hangup");
getActiveFgCall().hangup();
} else {
Log.d(LOG_TAG, "Switch");
activePhone.switchHoldingAndActive();
}
}
}
result = basePhone.dial(dialString);
if (VDBG) {
Log.d(LOG_TAG, "End dial(" + basePhone + ", "+ dialString + ")");
Log.d(LOG_TAG, this.toString());
}
return result;
}
/**
* Initiate a new voice connection. This happens asynchronously, so you
* cannot assume the audio path is connected (or a call index has been
* assigned) until PhoneStateChanged notification has occurred.
*
* @exception CallStateException if a new outgoing call is not currently
* possible because no more call slots exist or a call exists that is
* dialing, alerting, ringing, or waiting. Other errors are
* handled asynchronously.
*/
public Connection dial(Phone phone, String dialString, UUSInfo uusInfo) throws CallStateException {
return phone.dial(dialString, uusInfo);
}
/**
* clear disconnect connection for each phone
*/
public void clearDisconnected() {
for(Phone phone : mPhones) {
phone.clearDisconnected();
}
}
/**
* Whether or not the phone can do explicit call transfer in the current
* phone state--that is, one call holding and one call active.
* @return true if the phone can do explicit call transfer; false otherwise.
*/
public boolean canTransfer(Call heldCall) {
Phone activePhone = null;
Phone heldPhone = null;
if (hasActiveFgCall()) {
activePhone = getActiveFgCall().getPhone();
}
if (heldCall != null) {
heldPhone = heldCall.getPhone();
}
return (heldPhone == activePhone && activePhone.canTransfer());
}
/**
* Connects the held call and active call
* Disconnects the subscriber from both calls
*
* Explicit Call Transfer occurs asynchronously
* and may fail. Final notification occurs via
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if canTransfer() would return false.
* In these cases, this operation may not be performed.
*/
public void explicitCallTransfer(Call heldCall) throws CallStateException {
if (VDBG) {
Log.d(LOG_TAG, " explicitCallTransfer(" + heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
if (canTransfer(heldCall)) {
heldCall.getPhone().explicitCallTransfer();
}
if (VDBG) {
Log.d(LOG_TAG, "End explicitCallTransfer(" + heldCall + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Returns a list of MMI codes that are pending for a phone. (They have initiated
* but have not yet completed).
* Presently there is only ever one.
*
* Use <code>registerForMmiInitiate</code>
* and <code>registerForMmiComplete</code> for change notification.
* @return null if phone doesn't have or support mmi code
*/
public List<? extends MmiCode> getPendingMmiCodes(Phone phone) {
Log.e(LOG_TAG, "getPendingMmiCodes not implemented");
return null;
}
/**
* Sends user response to a USSD REQUEST message. An MmiCode instance
* representing this response is sent to handlers registered with
* registerForMmiInitiate.
*
* @param ussdMessge Message to send in the response.
* @return false if phone doesn't support ussd service
*/
public boolean sendUssdResponse(Phone phone, String ussdMessge) {
Log.e(LOG_TAG, "sendUssdResponse not implemented");
return false;
}
/**
* Mutes or unmutes the microphone for the active call. The microphone
* is automatically unmuted if a call is answered, dialed, or resumed
* from a holding state.
*
* @param muted true to mute the microphone,
* false to activate the microphone.
*/
public void setMute(boolean muted) {
if (VDBG) {
Log.d(LOG_TAG, " setMute(" + muted + ")");
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) {
getActiveFgCall().getPhone().setMute(muted);
}
if (VDBG) {
Log.d(LOG_TAG, "End setMute(" + muted + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Gets current mute status. Use
* {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
* java.lang.Object) registerForPreciseCallStateChanged()}
* as a change notifcation, although presently phone state changed is not
* fired when setMute() is called.
*
* @return true is muting, false is unmuting
*/
public boolean getMute() {
if (hasActiveFgCall()) {
return getActiveFgCall().getPhone().getMute();
}
return false;
}
/**
* Enables or disables echo suppression.
*/
public void setEchoSuppressionEnabled(boolean enabled) {
if (VDBG) {
Log.d(LOG_TAG, " setEchoSuppression(" + enabled + ")");
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) {
getActiveFgCall().getPhone().setEchoSuppressionEnabled(enabled);
}
if (VDBG) {
Log.d(LOG_TAG, "End setEchoSuppression(" + enabled + ")");
Log.d(LOG_TAG, this.toString());
}
}
/**
* Play a DTMF tone on the active call.
*
* @param c should be one of 0-9, '*' or '#'. Other values will be
* silently ignored.
* @return false if no active call or the active call doesn't support
* dtmf tone
*/
public boolean sendDtmf(char c) {
boolean result = false;
if (VDBG) {
Log.d(LOG_TAG, " sendDtmf(" + c + ")");
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) {
getActiveFgCall().getPhone().sendDtmf(c);
result = true;
}
if (VDBG) {
Log.d(LOG_TAG, "End sendDtmf(" + c + ")");
Log.d(LOG_TAG, this.toString());
}
return result;
}
/**
* Start to paly a DTMF tone on the active call.
* or there is a playing DTMF tone.
* @param c should be one of 0-9, '*' or '#'. Other values will be
* silently ignored.
*
* @return false if no active call or the active call doesn't support
* dtmf tone
*/
public boolean startDtmf(char c) {
boolean result = false;
if (VDBG) {
Log.d(LOG_TAG, " startDtmf(" + c + ")");
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) {
getActiveFgCall().getPhone().sendDtmf(c);
result = true;
}
if (VDBG) {
Log.d(LOG_TAG, "End startDtmf(" + c + ")");
Log.d(LOG_TAG, this.toString());
}
return result;
}
/**
* Stop the playing DTMF tone. Ignored if there is no playing DTMF
* tone or no active call.
*/
public void stopDtmf() {
if (VDBG) {
Log.d(LOG_TAG, " stopDtmf()" );
Log.d(LOG_TAG, this.toString());
}
if (hasActiveFgCall()) getFgPhone().stopDtmf();
if (VDBG) {
Log.d(LOG_TAG, "End stopDtmf()");
Log.d(LOG_TAG, this.toString());
}
}
/**
* send burst DTMF tone, it can send the string as single character or multiple character
* ignore if there is no active call or not valid digits string.
* Valid digit means only includes characters ISO-LATIN characters 0-9, *, #
* The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character,
* this api can send single character and multiple character, also, this api has response
* back to caller.
*
* @param dtmfString is string representing the dialing digit(s) in the active call
* @param on the DTMF ON length in milliseconds, or 0 for default
* @param off the DTMF OFF length in milliseconds, or 0 for default
* @param onComplete is the callback message when the action is processed by BP
*
*/
public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
if (hasActiveFgCall()) {
getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
return true;
}
return false;
}
/**
* Notifies when a voice connection has disconnected, either due to local
* or remote hangup or error.
*
* Messages received from this will have the following members:<p>
* <ul><li>Message.obj will be an AsyncResult</li>
* <li>AsyncResult.userObj = obj</li>
* <li>AsyncResult.result = a Connection object that is
* no longer connected.</li></ul>
*/
public void registerForDisconnect(Handler h, int what, Object obj) {
mDisconnectRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for voice disconnection notification.
* Extraneous calls are tolerated silently
*/
public void unregisterForDisconnect(Handler h){
mDisconnectRegistrants.remove(h);
}
/**
* Register for getting notifications for change in the Call State {@link Call.State}
* This is called PreciseCallState because the call state is more precise than the
* {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
*
* Resulting events will have an AsyncResult in <code>Message.obj</code>.
* AsyncResult.userData will be set to the obj argument here.
* The <em>h</em> parameter is held only by a weak reference.
*/
public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
mPreciseCallStateRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for voice call state change notifications.
* Extraneous calls are tolerated silently.
*/
public void unregisterForPreciseCallStateChanged(Handler h){
mPreciseCallStateRegistrants.remove(h);
}
/**
* Notifies when a previously untracked non-ringing/waiting connection has appeared.
* This is likely due to some other entity (eg, SIM card application) initiating a call.
*/
public void registerForUnknownConnection(Handler h, int what, Object obj){
mUnknownConnectionRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for unknown connection notifications.
*/
public void unregisterForUnknownConnection(Handler h){
mUnknownConnectionRegistrants.remove(h);
}
/**
* Notifies when a new ringing or waiting connection has appeared.<p>
*
* Messages received from this:
* Message.obj will be an AsyncResult
* AsyncResult.userObj = obj
* AsyncResult.result = a Connection. <p>
* Please check Connection.isRinging() to make sure the Connection
* has not dropped since this message was posted.
* If Connection.isRinging() is true, then
* Connection.getCall() == Phone.getRingingCall()
*/
public void registerForNewRingingConnection(Handler h, int what, Object obj){
mNewRingingConnectionRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for new ringing connection notification.
* Extraneous calls are tolerated silently
*/
public void unregisterForNewRingingConnection(Handler h){
mNewRingingConnectionRegistrants.remove(h);
}
/**
* Notifies when an incoming call rings.<p>
*
* Messages received from this:
* Message.obj will be an AsyncResult
* AsyncResult.userObj = obj
* AsyncResult.result = a Connection. <p>
*/
public void registerForIncomingRing(Handler h, int what, Object obj){
mIncomingRingRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for ring notification.
* Extraneous calls are tolerated silently
*/
public void unregisterForIncomingRing(Handler h){
mIncomingRingRegistrants.remove(h);
}
/**
* Notifies when out-band ringback tone is needed.<p>
*
* Messages received from this:
* Message.obj will be an AsyncResult
* AsyncResult.userObj = obj
* AsyncResult.result = boolean, true to start play ringback tone
* and false to stop. <p>
*/
public void registerForRingbackTone(Handler h, int what, Object obj){
mRingbackToneRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for ringback tone notification.
*/
public void unregisterForRingbackTone(Handler h){
mRingbackToneRegistrants.remove(h);
}
/**
* Registers the handler to reset the uplink mute state to get
* uplink audio.
*/
public void registerForResendIncallMute(Handler h, int what, Object obj){
mResendIncallMuteRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for resend incall mute notifications.
*/
public void unregisterForResendIncallMute(Handler h){
mResendIncallMuteRegistrants.remove(h);
}
/**
* Register for notifications of initiation of a new MMI code request.
* MMI codes for GSM are discussed in 3GPP TS 22.030.<p>
*
* Example: If Phone.dial is called with "*#31#", then the app will
* be notified here.<p>
*
* The returned <code>Message.obj</code> will contain an AsyncResult.
*
* <code>obj.result</code> will be an "MmiCode" object.
*/
public void registerForMmiInitiate(Handler h, int what, Object obj){
mMmiInitiateRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for new MMI initiate notification.
* Extraneous calls are tolerated silently
*/
public void unregisterForMmiInitiate(Handler h){
mMmiInitiateRegistrants.remove(h);
}
/**
* Register for notifications that an MMI request has completed
* its network activity and is in its final state. This may mean a state
* of COMPLETE, FAILED, or CANCELLED.
*
* <code>Message.obj</code> will contain an AsyncResult.
* <code>obj.result</code> will be an "MmiCode" object
*/
public void registerForMmiComplete(Handler h, int what, Object obj){
mMmiCompleteRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for MMI complete notification.
* Extraneous calls are tolerated silently
*/
public void unregisterForMmiComplete(Handler h){
mMmiCompleteRegistrants.remove(h);
}
/**
* Registration point for Ecm timer reset
* @param h handler to notify
* @param what user-defined message code
* @param obj placed in Message.obj
*/
public void registerForEcmTimerReset(Handler h, int what, Object obj){
mEcmTimerResetRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notification for Ecm timer reset
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForEcmTimerReset(Handler h){
mEcmTimerResetRegistrants.remove(h);
}
/**
* Register for ServiceState changed.
* Message.obj will contain an AsyncResult.
* AsyncResult.result will be a ServiceState instance
*/
public void registerForServiceStateChanged(Handler h, int what, Object obj){
mServiceStateChangedRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for ServiceStateChange notification.
* Extraneous calls are tolerated silently
*/
public void unregisterForServiceStateChanged(Handler h){
mServiceStateChangedRegistrants.remove(h);
}
/**
* Register for notifications when a supplementary service attempt fails.
* Message.obj will contain an AsyncResult.
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForSuppServiceFailed(Handler h, int what, Object obj){
mSuppServiceFailedRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notifications when a supplementary service attempt fails.
* Extraneous calls are tolerated silently
*
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForSuppServiceFailed(Handler h){
mSuppServiceFailedRegistrants.remove(h);
}
/**
* Register for notifications when a sInCall VoicePrivacy is enabled
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
mInCallVoicePrivacyOnRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notifications when a sInCall VoicePrivacy is enabled
*
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForInCallVoicePrivacyOn(Handler h){
mInCallVoicePrivacyOnRegistrants.remove(h);
}
/**
* Register for notifications when a sInCall VoicePrivacy is disabled
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
mInCallVoicePrivacyOffRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notifications when a sInCall VoicePrivacy is disabled
*
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForInCallVoicePrivacyOff(Handler h){
mInCallVoicePrivacyOffRegistrants.remove(h);
}
/**
* Register for notifications when CDMA call waiting comes
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForCallWaiting(Handler h, int what, Object obj){
mCallWaitingRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notifications when CDMA Call waiting comes
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForCallWaiting(Handler h){
mCallWaitingRegistrants.remove(h);
}
/**
* Register for signal information notifications from the network.
* Message.obj will contain an AsyncResult.
* AsyncResult.result will be a SuppServiceNotification instance.
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForSignalInfo(Handler h, int what, Object obj){
mSignalInfoRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for signal information notifications.
* Extraneous calls are tolerated silently
*
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForSignalInfo(Handler h){
mSignalInfoRegistrants.remove(h);
}
/**
* Register for display information notifications from the network.
* Message.obj will contain an AsyncResult.
* AsyncResult.result will be a SuppServiceNotification instance.
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForDisplayInfo(Handler h, int what, Object obj){
mDisplayInfoRegistrants.addUnique(h, what, obj);
}
/**
* Unregisters for display information notifications.
* Extraneous calls are tolerated silently
*
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForDisplayInfo(Handler h) {
mDisplayInfoRegistrants.remove(h);
}
/**
* Register for notifications when CDMA OTA Provision status change
*
* @param h Handler that receives the notification message.
* @param what User-defined message code.
* @param obj User object.
*/
public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){
mCdmaOtaStatusChangeRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notifications when CDMA OTA Provision status change
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForCdmaOtaStatusChange(Handler h){
mCdmaOtaStatusChangeRegistrants.remove(h);
}
/**
* Registration point for subscription info ready
* @param h handler to notify
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){
mSubscriptionInfoReadyRegistrants.addUnique(h, what, obj);
}
/**
* Unregister for notifications for subscription info
* @param h Handler to be removed from the registrant list.
*/
public void unregisterForSubscriptionInfoReady(Handler h){
mSubscriptionInfoReadyRegistrants.remove(h);
}
/**
* Sets an event to be fired when the telephony system processes
* a post-dial character on an outgoing call.<p>
*
* Messages of type <code>what</code> will be sent to <code>h</code>.
* The <code>obj</code> field of these Message's will be instances of
* <code>AsyncResult</code>. <code>Message.obj.result</code> will be
* a Connection object.<p>
*
* Message.arg1 will be the post dial character being processed,
* or 0 ('\0') if end of string.<p>
*
* If Connection.getPostDialState() == WAIT,
* the application must call
* {@link com.android.internal.telephony.Connection#proceedAfterWaitChar()
* Connection.proceedAfterWaitChar()} or
* {@link com.android.internal.telephony.Connection#cancelPostDial()
* Connection.cancelPostDial()}
* for the telephony system to continue playing the post-dial
* DTMF sequence.<p>
*
* If Connection.getPostDialState() == WILD,
* the application must call
* {@link com.android.internal.telephony.Connection#proceedAfterWildChar
* Connection.proceedAfterWildChar()}
* or
* {@link com.android.internal.telephony.Connection#cancelPostDial()
* Connection.cancelPostDial()}
* for the telephony system to continue playing the
* post-dial DTMF sequence.<p>
*
*/
public void registerForPostDialCharacter(Handler h, int what, Object obj){
mPostDialCharacterRegistrants.addUnique(h, what, obj);
}
public void unregisterForPostDialCharacter(Handler h){
mPostDialCharacterRegistrants.remove(h);
}
/* APIs to access foregroudCalls, backgroudCalls, and ringingCalls
* 1. APIs to access list of calls
* 2. APIs to check if any active call, which has connection other than
* disconnected ones, pleaser refer to Call.isIdle()
* 3. APIs to return first active call
* 4. APIs to return the connections of first active call
* 5. APIs to return other property of first active call
*/
/**
* @return list of all ringing calls
*/
public List<Call> getRingingCalls() {
return Collections.unmodifiableList(mRingingCalls);
}
/**
* @return list of all foreground calls
*/
public List<Call> getForegroundCalls() {
return Collections.unmodifiableList(mForegroundCalls);
}
/**
* @return list of all background calls
*/
public List<Call> getBackgroundCalls() {
return Collections.unmodifiableList(mBackgroundCalls);
}
/**
* Return true if there is at least one active foreground call
*/
public boolean hasActiveFgCall() {
return (getFirstActiveCall(mForegroundCalls) != null);
}
/**
* Return true if there is at least one active background call
*/
public boolean hasActiveBgCall() {
// TODO since hasActiveBgCall may get called often
// better to cache it to improve performance
return (getFirstActiveCall(mBackgroundCalls) != null);
}
/**
* Return true if there is at least one active ringing call
*
*/
public boolean hasActiveRingingCall() {
return (getFirstActiveCall(mRingingCalls) != null);
}
/**
* return the active foreground call from foreground calls
*
* Active call means the call is NOT in Call.State.IDLE
*
* 1. If there is active foreground call, return it
* 2. If there is no active foreground call, return the
* foreground call associated with default phone, which state is IDLE.
* 3. If there is no phone registered at all, return null.
*
*/
public Call getActiveFgCall() {
Call call = getFirstNonIdleCall(mForegroundCalls);
if (call == null) {
call = (mDefaultPhone == null)
? null
: mDefaultPhone.getForegroundCall();
}
return call;
}
// Returns the first call that is not in IDLE state. If both active calls
// and disconnecting/disconnected calls exist, return the first active call.
private Call getFirstNonIdleCall(List<Call> calls) {
Call result = null;
for (Call call : calls) {
if (!call.isIdle()) {
return call;
} else if (call.getState() != Call.State.IDLE) {
if (result == null) result = call;
}
}
return result;
}
/**
* return one active background call from background calls
*
* Active call means the call is NOT idle defined by Call.isIdle()
*
* 1. If there is only one active background call, return it
* 2. If there is more than one active background call, return the first one
* 3. If there is no active background call, return the background call
* associated with default phone, which state is IDLE.
* 4. If there is no background call at all, return null.
*
* Complete background calls list can be get by getBackgroundCalls()
*/
public Call getFirstActiveBgCall() {
Call call = getFirstNonIdleCall(mBackgroundCalls);
if (call == null) {
call = (mDefaultPhone == null)
? null
: mDefaultPhone.getBackgroundCall();
}
return call;
}
/**
* return one active ringing call from ringing calls
*
* Active call means the call is NOT idle defined by Call.isIdle()
*
* 1. If there is only one active ringing call, return it
* 2. If there is more than one active ringing call, return the first one
* 3. If there is no active ringing call, return the ringing call
* associated with default phone, which state is IDLE.
* 4. If there is no ringing call at all, return null.
*
* Complete ringing calls list can be get by getRingingCalls()
*/
public Call getFirstActiveRingingCall() {
Call call = getFirstNonIdleCall(mRingingCalls);
if (call == null) {
call = (mDefaultPhone == null)
? null
: mDefaultPhone.getRingingCall();
}
return call;
}
/**
* @return the state of active foreground call
* return IDLE if there is no active foreground call
*/
public Call.State getActiveFgCallState() {
Call fgCall = getActiveFgCall();
if (fgCall != null) {
return fgCall.getState();
}
return Call.State.IDLE;
}
/**
* @return the connections of active foreground call
* return empty list if there is no active foreground call
*/
public List<Connection> getFgCallConnections() {
Call fgCall = getActiveFgCall();
if ( fgCall != null) {
return fgCall.getConnections();
}
return emptyConnections;
}
/**
* @return the connections of active background call
* return empty list if there is no active background call
*/
public List<Connection> getBgCallConnections() {
Call bgCall = getFirstActiveBgCall();
if ( bgCall != null) {
return bgCall.getConnections();
}
return emptyConnections;
}
/**
* @return the latest connection of active foreground call
* return null if there is no active foreground call
*/
public Connection getFgCallLatestConnection() {
Call fgCall = getActiveFgCall();
if ( fgCall != null) {
return fgCall.getLatestConnection();
}
return null;
}
/**
* @return true if there is at least one Foreground call in disconnected state
*/
public boolean hasDisconnectedFgCall() {
return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED) != null);
}
/**
* @return true if there is at least one background call in disconnected state
*/
public boolean hasDisconnectedBgCall() {
return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED) != null);
}
/**
* @return the first active call from a call list
*/
private Call getFirstActiveCall(ArrayList<Call> calls) {
for (Call call : calls) {
if (!call.isIdle()) {
return call;
}
}
return null;
}
/**
* @return the first call in a the Call.state from a call list
*/
private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state) {
for (Call call : calls) {
if (call.getState() == state) {
return call;
}
}
return null;
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DISCONNECT:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_PRECISE_CALL_STATE_CHANGED:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)");
mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_NEW_RINGING_CONNECTION:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_UNKNOWN_CONNECTION:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_UNKNOWN_CONNECTION)");
mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_INCOMING_RING:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_INCOMING_RING)");
// The event may come from RIL who's not aware of an ongoing fg call
if (!hasActiveFgCall()) {
mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
}
break;
case EVENT_RINGBACK_TONE:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RINGBACK_TONE)");
mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_IN_CALL_VOICE_PRIVACY_ON:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_ON)");
mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_IN_CALL_VOICE_PRIVACY_OFF:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_OFF)");
mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_CALL_WAITING:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CALL_WAITING)");
mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_DISPLAY_INFO:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_DISPLAY_INFO)");
mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_SIGNAL_INFO:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SIGNAL_INFO)");
mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_CDMA_OTA_STATUS_CHANGE:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_CDMA_OTA_STATUS_CHANGE)");
mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_RESEND_INCALL_MUTE:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_RESEND_INCALL_MUTE)");
mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_MMI_INITIATE:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_INITIATE)");
mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_MMI_COMPLETE:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_MMI_COMPLETE)");
mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_ECM_TIMER_RESET:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_ECM_TIMER_RESET)");
mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_SUBSCRIPTION_INFO_READY:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUBSCRIPTION_INFO_READY)");
mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_SUPP_SERVICE_FAILED:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SUPP_SERVICE_FAILED)");
mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_SERVICE_STATE_CHANGED:
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_SERVICE_STATE_CHANGED)");
mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_POST_DIAL_CHARACTER:
// we need send the character that is being processed in msg.arg1
// so can't use notifyRegistrants()
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_POST_DIAL_CHARACTER)");
for(int i=0; i < mPostDialCharacterRegistrants.size(); i++) {
Message notifyMsg;
notifyMsg = ((Registrant)mPostDialCharacterRegistrants.get(i)).messageForRegistrant();
notifyMsg.obj = msg.obj;
notifyMsg.arg1 = msg.arg1;
notifyMsg.sendToTarget();
}
break;
}
}
};
@Override
public String toString() {
Call call;
StringBuilder b = new StringBuilder();
b.append("########### Dump CallManager ############");
b.append("\nCallManager state = " + getState());
call = getActiveFgCall();
b.append("\n - Foreground: " + getActiveFgCallState());
b.append(" from " + call.getPhone());
b.append("\n Conn: ").append(getFgCallConnections());
call = getFirstActiveBgCall();
b.append("\n - Background: " + call.getState());
b.append(" from " + call.getPhone());
b.append("\n Conn: ").append(getBgCallConnections());
call = getFirstActiveRingingCall();
b.append("\n - Ringing: " +call.getState());
b.append(" from " + call.getPhone());
for (Phone phone : getAllPhones()) {
if (phone != null) {
b.append("\n Phone: " + phone + ", name = " + phone.getPhoneName()
+ ", state = " + phone.getState());
call = phone.getForegroundCall();
b.append("\n - Foreground: ").append(call);
call = phone.getBackgroundCall();
b.append(" Background: ").append(call);
call = phone.getRingingCall();
b.append(" Ringing: ").append(call);
}
}
b.append("\n########## End Dump CallManager ##########");
return b.toString();
}
}