1. Originally CallManager registers for all phone events provided by registerFor interface, which will cause error msg such as "registerForXXX() in PhoneBase should not be called, CDMAPhone inactive." for CDMA only events. Register for those events only if phone is CDMA phone. 2. Add registerForPostDialCharacter(). Note, we can't use notifyRegistrants() in handler since we need send the character that is being processed in msg.arg1. Change-Id: I48037c8a8e225bb82e203836a041f81f2445f4d8
1494 lines
52 KiB
Java
1494 lines
52 KiB
Java
/*
|
|
* 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 ="Phone";
|
|
private static final boolean LOCAL_DEBUG = 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
|
|
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
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return Phone.State.RINGING;
|
|
} else if (phone.getState() == Phone.State.OFFHOOK) {
|
|
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
|
|
* @return true if register successfully
|
|
*/
|
|
public boolean registerPhone(Phone phone) {
|
|
if (phone != null && !mPhones.contains(phone)) {
|
|
if (mPhones.isEmpty()) {
|
|
mDefaultPhone = phone;
|
|
}
|
|
mPhones.add(phone);
|
|
mRingingCalls.add(phone.getRingingCall());
|
|
mBackgroundCalls.add(phone.getBackgroundCall());
|
|
mForegroundCalls.add(phone.getForegroundCall());
|
|
registerForPhoneStates(phone);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
|
|
/**
|
|
* unregister phone from CallManager
|
|
* @param phone
|
|
*/
|
|
public void unregisterPhone(Phone phone) {
|
|
if (phone != null && mPhones.contains(phone)) {
|
|
mPhones.remove(phone);
|
|
mRingingCalls.remove(phone.getRingingCall());
|
|
mBackgroundCalls.remove(phone.getBackgroundCall());
|
|
mForegroundCalls.remove(phone.getForegroundCall());
|
|
unregisterForPhoneStates(phone);
|
|
if (phone == mDefaultPhone) {
|
|
if (mPhones.isEmpty()) {
|
|
mDefaultPhone = null;
|
|
} else {
|
|
mDefaultPhone = mPhones.get(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 ( hasActiveFgCall() ) {
|
|
Phone activePhone = getActiveFgCall().getPhone();
|
|
boolean hasBgCall = ! (activePhone.getBackgroundCall().isIdle());
|
|
boolean sameChannel = (activePhone == ringingPhone);
|
|
|
|
if (LOCAL_DEBUG) {
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
Phone ringingPhone = ringingCall.getPhone();
|
|
|
|
ringingPhone.rejectCall();
|
|
}
|
|
|
|
/**
|
|
* 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 (hasActiveFgCall()) {
|
|
activePhone = getActiveFgCall().getPhone();
|
|
}
|
|
|
|
if (heldCall != null) {
|
|
heldPhone = heldCall.getPhone();
|
|
}
|
|
|
|
if (activePhone != null) {
|
|
activePhone.switchHoldingAndActive();
|
|
}
|
|
|
|
if (heldPhone != null && heldPhone != activePhone) {
|
|
heldPhone.switchHoldingAndActive();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 (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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
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"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
if ( hasActiveFgCall() ) {
|
|
Phone activePhone = getActiveFgCall().getPhone();
|
|
boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle());
|
|
|
|
if (LOCAL_DEBUG) {
|
|
Log.d(LOG_TAG, "hasBgCall: "+ hasBgCall + "sameChannel:" + (activePhone != phone));
|
|
}
|
|
|
|
if (activePhone != phone) {
|
|
if (hasBgCall) {
|
|
Log.d(LOG_TAG, "Hangup");
|
|
getActiveFgCall().hangup();
|
|
} else {
|
|
Log.d(LOG_TAG, "Switch");
|
|
activePhone.switchHoldingAndActive();
|
|
}
|
|
}
|
|
}
|
|
return phone.dial(dialString);
|
|
}
|
|
|
|
/**
|
|
* 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 (canTransfer(heldCall)) {
|
|
heldCall.getPhone().explicitCallTransfer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
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) {
|
|
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 (hasActiveFgCall()) {
|
|
getActiveFgCall().getPhone().setMute(muted);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
if (hasActiveFgCall()) {
|
|
getActiveFgCall().getPhone().sendDtmf(c);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
if (hasActiveFgCall()) {
|
|
getActiveFgCall().getPhone().sendDtmf(c);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Stop the playing DTMF tone. Ignored if there is no playing DTMF
|
|
* tone or no active call.
|
|
*/
|
|
public void stopDtmf() {
|
|
if (hasActiveFgCall()) getFgPhone().stopDtmf();
|
|
}
|
|
|
|
/**
|
|
* 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() {
|
|
for (Call call : mForegroundCalls) {
|
|
if (call.getState() != Call.State.IDLE) {
|
|
return call;
|
|
}
|
|
}
|
|
return (mDefaultPhone == null) ?
|
|
null : mDefaultPhone.getForegroundCall();
|
|
}
|
|
|
|
/**
|
|
* 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() {
|
|
for (Call call : mBackgroundCalls) {
|
|
if (!call.isIdle()) {
|
|
return call;
|
|
}
|
|
}
|
|
return (mDefaultPhone == null) ?
|
|
null : mDefaultPhone.getBackgroundCall();
|
|
}
|
|
|
|
/**
|
|
* 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() {
|
|
for (Call call : mRingingCalls) {
|
|
if (!call.isIdle()) {
|
|
return call;
|
|
}
|
|
}
|
|
return (mDefaultPhone == null) ?
|
|
null : mDefaultPhone.getRingingCall();
|
|
}
|
|
|
|
/**
|
|
* @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 = getActiveFgCall();
|
|
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:
|
|
mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_PRECISE_CALL_STATE_CHANGED:
|
|
mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_NEW_RINGING_CONNECTION:
|
|
mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_UNKNOWN_CONNECTION:
|
|
mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case 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:
|
|
mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_IN_CALL_VOICE_PRIVACY_ON:
|
|
mInCallVoicePrivacyOnRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_IN_CALL_VOICE_PRIVACY_OFF:
|
|
mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_CALL_WAITING:
|
|
mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_DISPLAY_INFO:
|
|
mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_SIGNAL_INFO:
|
|
mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_CDMA_OTA_STATUS_CHANGE:
|
|
mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_RESEND_INCALL_MUTE:
|
|
mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_MMI_INITIATE:
|
|
mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_MMI_COMPLETE:
|
|
mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_ECM_TIMER_RESET:
|
|
mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_SUBSCRIPTION_INFO_READY:
|
|
mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case EVENT_SUPP_SERVICE_FAILED:
|
|
mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj);
|
|
break;
|
|
case 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()
|
|
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;
|
|
}
|
|
}
|
|
};
|
|
}
|