| /* |
| * 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 android.compat.annotation.UnsupportedAppUsage; |
| import android.content.Context; |
| import android.os.AsyncResult; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.Registrant; |
| import android.os.RegistrantList; |
| import android.telephony.PhoneNumberUtils; |
| import android.telephony.PhoneStateListener; |
| import android.telephony.ServiceState; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.telephony.imsphone.ImsPhoneConnection; |
| import com.android.telephony.Rlog; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| 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 class CallManager { |
| |
| private static final String LOG_TAG ="CallManager"; |
| private static final boolean DBG = true; |
| private static final boolean VDBG = false; |
| |
| private static final int EVENT_DISCONNECT = 100; |
| @VisibleForTesting 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; |
| @VisibleForTesting 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; |
| @VisibleForTesting 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; |
| private static final int EVENT_ONHOLD_TONE = 120; |
| // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| //private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 121; |
| private static final int EVENT_TTY_MODE_RECEIVED = 122; |
| |
| // Singleton instance |
| private static final CallManager INSTANCE = new CallManager(); |
| |
| // list of registered phones, which are Phone objs |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private final ArrayList<Phone> mPhones; |
| |
| // list of supported ringing calls |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private final ArrayList<Call> mRingingCalls; |
| |
| // list of supported background calls |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private final ArrayList<Call> mBackgroundCalls; |
| |
| // list of supported foreground calls |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private final ArrayList<Call> mForegroundCalls; |
| |
| // empty connection list |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private final ArrayList<Connection> mEmptyConnections = new ArrayList<Connection>(); |
| |
| // mapping of phones to registered handler instances used for callbacks from RIL |
| private final HashMap<Phone, CallManagerHandler> mHandlerMap = new HashMap<>(); |
| |
| // default phone as the first phone registered, which is Phone obj |
| private Phone mDefaultPhone; |
| |
| private boolean mSpeedUpAudioForMtCall = false; |
| // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| //private boolean mIsEccDialing = false; |
| |
| private Object mRegistrantidentifier = new Object(); |
| |
| // 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 mOnHoldToneRegistrants |
| = 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(); |
| |
| protected final RegistrantList mTtyModeReceivedRegistrants |
| = 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 |
| */ |
| @UnsupportedAppUsage |
| public static CallManager getInstance() { |
| return INSTANCE; |
| } |
| |
| /** |
| * get Phone object corresponds to subId |
| * @return Phone |
| */ |
| private Phone getPhone(int subId) { |
| Phone p = null; |
| for (Phone phone : mPhones) { |
| if (phone.getSubId() == subId && |
| phone.getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) { |
| p = phone; |
| break; |
| } |
| } |
| return p; |
| } |
| |
| /** |
| * 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 |
| * |
| */ |
| @UnsupportedAppUsage |
| public PhoneConstants.State getState() { |
| PhoneConstants.State s = PhoneConstants.State.IDLE; |
| |
| for (Phone phone : mPhones) { |
| if (phone.getState() == PhoneConstants.State.RINGING) { |
| s = PhoneConstants.State.RINGING; |
| } else if (phone.getState() == PhoneConstants.State.OFFHOOK) { |
| if (s == PhoneConstants.State.IDLE) s = PhoneConstants.State.OFFHOOK; |
| } |
| } |
| return s; |
| } |
| |
| /** |
| * Get current coarse-grained voice call state on a subId. |
| * If the Call Manager has an active call and call waiting occurs, |
| * then the phone state is RINGING not OFFHOOK |
| * |
| */ |
| @UnsupportedAppUsage |
| public PhoneConstants.State getState(int subId) { |
| PhoneConstants.State s = PhoneConstants.State.IDLE; |
| |
| for (Phone phone : mPhones) { |
| if (phone.getSubId() == subId) { |
| if (phone.getState() == PhoneConstants.State.RINGING) { |
| s = PhoneConstants.State.RINGING; |
| } else if (phone.getState() == PhoneConstants.State.OFFHOOK) { |
| if (s == PhoneConstants.State.IDLE) s = PhoneConstants.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; |
| } |
| |
| /** |
| * @return the Phone service state corresponds to subId |
| */ |
| public int getServiceState(int subId) { |
| int resultState = ServiceState.STATE_OUT_OF_SERVICE; |
| |
| for (Phone phone : mPhones) { |
| if (phone.getSubId() == subId) { |
| 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; |
| } |
| |
| /** |
| * @return the phone associated with any call |
| */ |
| @UnsupportedAppUsage |
| public Phone getPhoneInCall() { |
| Phone phone = null; |
| if (!getFirstActiveRingingCall().isIdle()) { |
| phone = getFirstActiveRingingCall().getPhone(); |
| } else if (!getActiveFgCall().isIdle()) { |
| phone = getActiveFgCall().getPhone(); |
| } else { |
| // If BG call is idle, we return default phone |
| phone = getFirstActiveBgCall().getPhone(); |
| } |
| return phone; |
| } |
| |
| /** |
| * Register phone to CallManager |
| * @param phone to be registered |
| * @return true if register successfully |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public boolean registerPhone(Phone phone) { |
| if (phone != null && !mPhones.contains(phone)) { |
| |
| if (DBG) { |
| Rlog.d(LOG_TAG, "registerPhone(" + |
| phone.getPhoneName() + " " + 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; |
| } |
| |
| /** |
| * unregister phone from CallManager |
| * @param phone to be unregistered |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void unregisterPhone(Phone phone) { |
| if (phone != null && mPhones.contains(phone)) { |
| |
| if (DBG) { |
| Rlog.d(LOG_TAG, "unregisterPhone(" + |
| phone.getPhoneName() + " " + phone + ")"); |
| } |
| |
| Phone imsPhone = phone.getImsPhone(); |
| if (imsPhone != null) { |
| unregisterPhone(imsPhone); |
| } |
| |
| 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); |
| } |
| } |
| } |
| } |
| |
| /** |
| * return the default phone or null if no phone available |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public Phone getDefaultPhone() { |
| return mDefaultPhone; |
| } |
| |
| /** |
| * @return the phone associated with the foreground call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public Phone getFgPhone() { |
| return getActiveFgCall().getPhone(); |
| } |
| |
| /** |
| * @return the phone associated with the foreground call |
| * of a particular subId |
| */ |
| @UnsupportedAppUsage |
| public Phone getFgPhone(int subId) { |
| return getActiveFgCall(subId).getPhone(); |
| } |
| |
| /** |
| * @return the phone associated with the background call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public Phone getBgPhone() { |
| return getFirstActiveBgCall().getPhone(); |
| } |
| |
| /** |
| * @return the phone associated with the ringing call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public Phone getRingingPhone() { |
| return getFirstActiveRingingCall().getPhone(); |
| } |
| |
| /** |
| * @return the phone associated with the ringing call |
| * of a particular subId |
| */ |
| public Phone getRingingPhone(int subId) { |
| return getFirstActiveRingingCall(subId).getPhone(); |
| } |
| |
| /* FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| public void setAudioMode() { |
| Context context = getContext(); |
| if (context == null) return; |
| AudioManager audioManager = (AudioManager) |
| context.getSystemService(Context.AUDIO_SERVICE); |
| |
| if (!isServiceStateInService() && !mIsEccDialing) { |
| if (audioManager.getMode() != AudioManager.MODE_NORMAL) { |
| if (VDBG) Rlog.d(LOG_TAG, "abandonAudioFocus"); |
| // abandon audio focus after the mode has been set back to normal |
| audioManager.abandonAudioFocusForCall(); |
| audioManager.setMode(AudioManager.MODE_NORMAL); |
| } |
| return; |
| } |
| |
| // change the audio mode and request/abandon audio focus according to phone state, |
| // but only on audio mode transitions |
| switch (getState()) { |
| case RINGING: |
| int curAudioMode = audioManager.getMode(); |
| if (curAudioMode != AudioManager.MODE_RINGTONE) { |
| if (VDBG) Rlog.d(LOG_TAG, "requestAudioFocus on STREAM_RING"); |
| audioManager.requestAudioFocusForCall(AudioManager.STREAM_RING, |
| AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); |
| if(!mSpeedUpAudioForMtCall) { |
| audioManager.setMode(AudioManager.MODE_RINGTONE); |
| } |
| } |
| |
| if (mSpeedUpAudioForMtCall && (curAudioMode != AudioManager.MODE_IN_CALL)) { |
| audioManager.setMode(AudioManager.MODE_IN_CALL); |
| } |
| break; |
| case OFFHOOK: |
| Phone offhookPhone = getFgPhone(); |
| if (getActiveFgCallState() == Call.State.IDLE) { |
| // There is no active Fg calls, the OFFHOOK state |
| // is set by the Bg call. So set the phone to bgPhone. |
| offhookPhone = getBgPhone(); |
| } |
| |
| int newAudioMode = AudioManager.MODE_IN_CALL; |
| int currMode = audioManager.getMode(); |
| if (currMode != newAudioMode || mSpeedUpAudioForMtCall) { |
| // request audio focus before setting the new mode |
| if (VDBG) Rlog.d(LOG_TAG, "requestAudioFocus on STREAM_VOICE_CALL"); |
| audioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, |
| AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); |
| Rlog.d(LOG_TAG, "setAudioMode Setting audio mode from " |
| + currMode + " to " + newAudioMode); |
| audioManager.setMode(newAudioMode); |
| } |
| mSpeedUpAudioForMtCall = false; |
| break; |
| case IDLE: |
| if (audioManager.getMode() != AudioManager.MODE_NORMAL) { |
| audioManager.setMode(AudioManager.MODE_NORMAL); |
| if (VDBG) Rlog.d(LOG_TAG, "abandonAudioFocus"); |
| // abandon audio focus after the mode has been set back to normal |
| audioManager.abandonAudioFocusForCall(); |
| } |
| mSpeedUpAudioForMtCall = false; |
| break; |
| } |
| Rlog.d(LOG_TAG, "setAudioMode state = " + getState()); |
| } |
| */ |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private Context getContext() { |
| Phone defaultPhone = getDefaultPhone(); |
| return ((defaultPhone == null) ? null : defaultPhone.getContext()); |
| } |
| |
| public Object getRegistrantIdentifier() { |
| return mRegistrantidentifier; |
| } |
| |
| private void registerForPhoneStates(Phone phone) { |
| // We need to keep a mapping of handler to Phone for proper unregistration. |
| // TODO: Clean up this solution as it is just a work around for each Phone instance |
| // using the same Handler to register with the RIL. When time permits, we should consider |
| // moving the handler (or the reference ot the handler) into the Phone object. |
| // See b/17414427. |
| CallManagerHandler handler = mHandlerMap.get(phone); |
| if (handler != null) { |
| Rlog.d(LOG_TAG, "This phone has already been registered."); |
| return; |
| } |
| |
| // New registration, create a new handler instance and register the phone. |
| handler = new CallManagerHandler(); |
| mHandlerMap.put(phone, handler); |
| |
| // for common events supported by all phones |
| // The mRegistrantIdentifier passed here, is to identify in the Phone |
| // that the registrants are coming from the CallManager. |
| phone.registerForPreciseCallStateChanged(handler, EVENT_PRECISE_CALL_STATE_CHANGED, |
| mRegistrantidentifier); |
| phone.registerForDisconnect(handler, EVENT_DISCONNECT, |
| mRegistrantidentifier); |
| phone.registerForNewRingingConnection(handler, EVENT_NEW_RINGING_CONNECTION, |
| mRegistrantidentifier); |
| phone.registerForUnknownConnection(handler, EVENT_UNKNOWN_CONNECTION, |
| mRegistrantidentifier); |
| phone.registerForIncomingRing(handler, EVENT_INCOMING_RING, |
| mRegistrantidentifier); |
| phone.registerForRingbackTone(handler, EVENT_RINGBACK_TONE, |
| mRegistrantidentifier); |
| phone.registerForInCallVoicePrivacyOn(handler, EVENT_IN_CALL_VOICE_PRIVACY_ON, |
| mRegistrantidentifier); |
| phone.registerForInCallVoicePrivacyOff(handler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, |
| mRegistrantidentifier); |
| phone.registerForDisplayInfo(handler, EVENT_DISPLAY_INFO, |
| mRegistrantidentifier); |
| phone.registerForSignalInfo(handler, EVENT_SIGNAL_INFO, |
| mRegistrantidentifier); |
| phone.registerForResendIncallMute(handler, EVENT_RESEND_INCALL_MUTE, |
| mRegistrantidentifier); |
| phone.registerForMmiInitiate(handler, EVENT_MMI_INITIATE, |
| mRegistrantidentifier); |
| phone.registerForMmiComplete(handler, EVENT_MMI_COMPLETE, |
| mRegistrantidentifier); |
| phone.registerForSuppServiceFailed(handler, EVENT_SUPP_SERVICE_FAILED, |
| mRegistrantidentifier); |
| phone.registerForServiceStateChanged(handler, EVENT_SERVICE_STATE_CHANGED, |
| mRegistrantidentifier); |
| |
| // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| //phone.registerForRadioOffOrNotAvailable(handler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); |
| |
| // for events supported only by GSM, CDMA and IMS phone |
| phone.setOnPostDialCharacter(handler, EVENT_POST_DIAL_CHARACTER, null); |
| |
| // for events supported only by CDMA phone |
| phone.registerForCdmaOtaStatusChange(handler, EVENT_CDMA_OTA_STATUS_CHANGE, null); |
| phone.registerForSubscriptionInfoReady(handler, EVENT_SUBSCRIPTION_INFO_READY, null); |
| phone.registerForCallWaiting(handler, EVENT_CALL_WAITING, null); |
| phone.registerForEcmTimerReset(handler, EVENT_ECM_TIMER_RESET, null); |
| |
| // for events supported only by IMS phone |
| phone.registerForOnHoldTone(handler, EVENT_ONHOLD_TONE, null); |
| phone.registerForSuppServiceFailed(handler, EVENT_SUPP_SERVICE_FAILED, null); |
| phone.registerForTtyModeReceived(handler, EVENT_TTY_MODE_RECEIVED, null); |
| } |
| |
| private void unregisterForPhoneStates(Phone phone) { |
| // Make sure that we clean up our map of handlers to Phones. |
| CallManagerHandler handler = mHandlerMap.get(phone); |
| if (handler == null) { |
| Rlog.e(LOG_TAG, "Could not find Phone handler for unregistration"); |
| return; |
| } |
| mHandlerMap.remove(phone); |
| |
| // for common events supported by all phones |
| phone.unregisterForPreciseCallStateChanged(handler); |
| phone.unregisterForDisconnect(handler); |
| phone.unregisterForNewRingingConnection(handler); |
| phone.unregisterForUnknownConnection(handler); |
| phone.unregisterForIncomingRing(handler); |
| phone.unregisterForRingbackTone(handler); |
| phone.unregisterForInCallVoicePrivacyOn(handler); |
| phone.unregisterForInCallVoicePrivacyOff(handler); |
| phone.unregisterForDisplayInfo(handler); |
| phone.unregisterForSignalInfo(handler); |
| phone.unregisterForResendIncallMute(handler); |
| phone.unregisterForMmiInitiate(handler); |
| phone.unregisterForMmiComplete(handler); |
| phone.unregisterForSuppServiceFailed(handler); |
| phone.unregisterForServiceStateChanged(handler); |
| phone.unregisterForTtyModeReceived(handler); |
| // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| //phone.unregisterForRadioOffOrNotAvailable(handler); |
| |
| // for events supported only by GSM, CDMA and IMS phone |
| phone.setOnPostDialCharacter(null, EVENT_POST_DIAL_CHARACTER, null); |
| |
| // for events supported only by CDMA phone |
| phone.unregisterForCdmaOtaStatusChange(handler); |
| phone.unregisterForSubscriptionInfoReady(handler); |
| phone.unregisterForCallWaiting(handler); |
| phone.unregisterForEcmTimerReset(handler); |
| |
| // for events supported only by IMS phone |
| phone.unregisterForOnHoldTone(handler); |
| phone.unregisterForSuppServiceFailed(handler); |
| } |
| |
| /** |
| * 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) { |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| Phone ringingPhone = ringingCall.getPhone(); |
| |
| ringingPhone.rejectCall(); |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End rejectCall(" +ringingCall + ")"); |
| Rlog.d(LOG_TAG, 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()); |
| } |
| |
| /** |
| * Whether or not the phone can conference in the current phone |
| * state--that is, one call holding and one call active. |
| * This method consider the phone object which is specific |
| * to the provided subId. |
| * @return true if the phone can conference; false otherwise. |
| */ |
| @UnsupportedAppUsage |
| public boolean canConference(Call heldCall, int subId) { |
| Phone activePhone = null; |
| Phone heldPhone = null; |
| |
| if (hasActiveFgCall(subId)) { |
| activePhone = getActiveFgCall(subId).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. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void conference(Call heldCall) throws CallStateException { |
| int subId = heldCall.getPhone().getSubId(); |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "conference(" +heldCall + ")"); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| Phone fgPhone = getFgPhone(subId); |
| if (fgPhone != null) { |
| if (canConference(heldCall)) { |
| fgPhone.conference(); |
| } else { |
| throw(new CallStateException("Can't conference foreground and selected background call")); |
| } |
| } else { |
| Rlog.d(LOG_TAG, "conference: fgPhone=null"); |
| } |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End conference(" +heldCall + ")"); |
| Rlog.d(LOG_TAG, 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, int videoState) |
| throws CallStateException { |
| int subId = phone.getSubId(); |
| Connection result; |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, " dial(" + phone + ", "+ dialString + ")" + |
| " subId = " + subId); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (!canDial(phone)) { |
| /* |
| * canDial function only checks whether the phone can make a new call. |
| * InCall MMI commmands are basically supplementary services |
| * within a call eg: call hold, call deflection, explicit call transfer etc. |
| */ |
| String newDialString = PhoneNumberUtils.stripSeparators(dialString); |
| if (phone.handleInCallMmiCommands(newDialString)) { |
| return null; |
| } else { |
| throw new CallStateException("cannot dial in current state"); |
| } |
| } |
| |
| if ( hasActiveFgCall(subId) ) { |
| Phone activePhone = getActiveFgCall(subId).getPhone(); |
| boolean hasBgCall = !(activePhone.getBackgroundCall().isIdle()); |
| |
| if (DBG) { |
| Rlog.d(LOG_TAG, "hasBgCall: "+ hasBgCall + " sameChannel:" + (activePhone == phone)); |
| } |
| |
| // Manipulation between IMS phone and its owner |
| // will be treated in GSM/CDMA phone. |
| Phone imsPhone = phone.getImsPhone(); |
| if (activePhone != phone |
| && (imsPhone == null || imsPhone != activePhone)) { |
| if (hasBgCall) { |
| Rlog.d(LOG_TAG, "Hangup"); |
| getActiveFgCall(subId).hangup(); |
| } else { |
| Rlog.d(LOG_TAG, "Switch"); |
| activePhone.switchHoldingAndActive(); |
| } |
| } |
| } |
| |
| result = phone.dial(dialString, new PhoneInternalInterface.DialArgs.Builder<>() |
| .setVideoState(videoState).build()); |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End dial(" + phone + ", "+ dialString + ")"); |
| Rlog.d(LOG_TAG, 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, int videoState) |
| throws CallStateException { |
| return phone.dial(dialString, |
| new PhoneInternalInterface.DialArgs.Builder<>() |
| .setUusInfo(uusInfo) |
| .setVideoState(videoState).build()); |
| } |
| |
| /** |
| * clear disconnect connection for each phone |
| */ |
| public void clearDisconnected() { |
| for(Phone phone : mPhones) { |
| phone.clearDisconnected(); |
| } |
| } |
| |
| /** |
| * clear disconnect connection for a phone specific |
| * to the provided subId |
| */ |
| public void clearDisconnected(int subId) { |
| for(Phone phone : mPhones) { |
| if (phone.getSubId() == subId) { |
| phone.clearDisconnected(); |
| } |
| } |
| } |
| |
| /** |
| * Phone can make a call only if ALL of the following are true: |
| * - Phone is not powered off |
| * - There's no incoming or waiting call |
| * - The foreground call is ACTIVE or IDLE or DISCONNECTED. |
| * (We mainly need to make sure it *isn't* DIALING or ALERTING.) |
| * @param phone |
| * @return true if the phone can make a new call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean canDial(Phone phone) { |
| int serviceState = phone.getServiceState().getState(); |
| int subId = phone.getSubId(); |
| boolean hasRingingCall = hasActiveRingingCall(); |
| Call.State fgCallState = getActiveFgCallState(subId); |
| |
| boolean result = (serviceState != ServiceState.STATE_POWER_OFF |
| && !hasRingingCall |
| && ((fgCallState == Call.State.ACTIVE) |
| || (fgCallState == Call.State.IDLE) |
| || (fgCallState == Call.State.DISCONNECTED) |
| /*As per 3GPP TS 51.010-1 section 31.13.1.4 |
| call should be alowed when the foreground |
| call is in ALERTING state*/ |
| || (fgCallState == Call.State.ALERTING))); |
| |
| if (result == false) { |
| Rlog.d(LOG_TAG, "canDial serviceState=" + serviceState |
| + " hasRingingCall=" + hasRingingCall |
| + " fgCallState=" + fgCallState); |
| } |
| return result; |
| } |
| |
| /** |
| * 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()); |
| } |
| |
| /** |
| * Whether or not the phone specific to subId 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, int subId) { |
| Phone activePhone = null; |
| Phone heldPhone = null; |
| |
| if (hasActiveFgCall(subId)) { |
| activePhone = getActiveFgCall(subId).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) { |
| Rlog.d(LOG_TAG, " explicitCallTransfer(" + heldCall + ")"); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (canTransfer(heldCall)) { |
| heldCall.getPhone().explicitCallTransfer(); |
| } |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End explicitCallTransfer(" + heldCall + ")"); |
| Rlog.d(LOG_TAG, 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) { |
| Rlog.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) { |
| Rlog.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) { |
| Rlog.d(LOG_TAG, " setMute(" + muted + ")"); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (hasActiveFgCall()) { |
| getActiveFgCall().getPhone().setMute(muted); |
| } |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End setMute(" + muted + ")"); |
| Rlog.d(LOG_TAG, 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(); |
| } else if (hasActiveBgCall()) { |
| return getFirstActiveBgCall().getPhone().getMute(); |
| } |
| return false; |
| } |
| |
| /** |
| * Enables or disables echo suppression. |
| */ |
| public void setEchoSuppressionEnabled() { |
| if (VDBG) { |
| Rlog.d(LOG_TAG, " setEchoSuppression()"); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (hasActiveFgCall()) { |
| getActiveFgCall().getPhone().setEchoSuppressionEnabled(); |
| } |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End setEchoSuppression()"); |
| Rlog.d(LOG_TAG, 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) { |
| Rlog.d(LOG_TAG, " sendDtmf(" + c + ")"); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (hasActiveFgCall()) { |
| getActiveFgCall().getPhone().sendDtmf(c); |
| result = true; |
| } |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End sendDtmf(" + c + ")"); |
| Rlog.d(LOG_TAG, 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) { |
| Rlog.d(LOG_TAG, " startDtmf(" + c + ")"); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (hasActiveFgCall()) { |
| getActiveFgCall().getPhone().startDtmf(c); |
| result = true; |
| } |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End startDtmf(" + c + ")"); |
| Rlog.d(LOG_TAG, 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) { |
| Rlog.d(LOG_TAG, " stopDtmf()" ); |
| Rlog.d(LOG_TAG, toString()); |
| } |
| |
| if (hasActiveFgCall()) getFgPhone().stopDtmf(); |
| |
| if (VDBG) { |
| Rlog.d(LOG_TAG, "End stopDtmf()"); |
| Rlog.d(LOG_TAG, 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> |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void registerForDisconnect(Handler h, int what, Object obj) { |
| mDisconnectRegistrants.addUnique(h, what, obj); |
| } |
| |
| /** |
| * Unregisters for voice disconnection notification. |
| * Extraneous calls are tolerated silently |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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 what |
| * 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. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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() |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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 |
| */ |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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); |
| } |
| |
| /** |
| * Notifies when out-band on-hold tone is needed.<p> |
| * |
| * Messages received from this: |
| * Message.obj will be an AsyncResult |
| * AsyncResult.userObj = obj |
| * AsyncResult.result = boolean, true to start play on-hold tone |
| * and false to stop. <p> |
| */ |
| public void registerForOnHoldTone(Handler h, int what, Object obj){ |
| mOnHoldToneRegistrants.addUnique(h, what, obj); |
| } |
| |
| /** |
| * Unregisters for on-hold tone notification. |
| */ |
| |
| public void unregisterForOnHoldTone(Handler h){ |
| mOnHoldToneRegistrants.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){ |
| Rlog.d(LOG_TAG, "registerForMmiComplete"); |
| 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); |
| } |
| |
| /** |
| * Register for TTY mode change notifications from the network. |
| * Message.obj will contain an AsyncResult. |
| * AsyncResult.result will be an Integer containing new mode. |
| * |
| * @param h Handler that receives the notification message. |
| * @param what User-defined message code. |
| * @param obj User object. |
| */ |
| public void registerForTtyModeReceived(Handler h, int what, Object obj){ |
| mTtyModeReceivedRegistrants.addUnique(h, what, obj); |
| } |
| |
| /** |
| * Unregisters for TTY mode change notifications. |
| * Extraneous calls are tolerated silently |
| * |
| * @param h Handler to be removed from the registrant list. |
| */ |
| public void unregisterForTtyModeReceived(Handler h) { |
| mTtyModeReceivedRegistrants.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 |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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 |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public List<Call> getBackgroundCalls() { |
| return Collections.unmodifiableList(mBackgroundCalls); |
| } |
| |
| /** |
| * Return true if there is at least one active foreground call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public boolean hasActiveFgCall() { |
| return (getFirstActiveCall(mForegroundCalls) != null); |
| } |
| |
| /** |
| * Return true if there is at least one active foreground call on a particular subId |
| */ |
| @UnsupportedAppUsage |
| public boolean hasActiveFgCall(int subId) { |
| return (getFirstActiveCall(mForegroundCalls, subId) != null); |
| } |
| |
| /** |
| * Return true if there is at least one active background call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| 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 background call on a particular subId |
| */ |
| @UnsupportedAppUsage |
| public boolean hasActiveBgCall(int subId) { |
| // TODO since hasActiveBgCall may get called often |
| // better to cache it to improve performance |
| return (getFirstActiveCall(mBackgroundCalls, subId) != null); |
| } |
| |
| /** |
| * Return true if there is at least one active ringing call |
| * |
| */ |
| public boolean hasActiveRingingCall() { |
| return (getFirstActiveCall(mRingingCalls) != null); |
| } |
| |
| /** |
| * Return true if there is at least one active ringing call |
| */ |
| @UnsupportedAppUsage |
| public boolean hasActiveRingingCall(int subId) { |
| return (getFirstActiveCall(mRingingCalls, subId) != 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; |
| } |
| |
| @UnsupportedAppUsage |
| public Call getActiveFgCall(int subId) { |
| Call call = getFirstNonIdleCall(mForegroundCalls, subId); |
| if (call == null) { |
| Phone phone = getPhone(subId); |
| call = (phone == null) |
| ? null |
| : phone.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; |
| } |
| |
| // 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, int subId) { |
| Call result = null; |
| for (Call call : calls) { |
| if (call.getPhone().getSubId() == subId) { |
| 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() |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public Call getFirstActiveBgCall() { |
| Call call = getFirstNonIdleCall(mBackgroundCalls); |
| if (call == null) { |
| call = (mDefaultPhone == null) |
| ? null |
| : mDefaultPhone.getBackgroundCall(); |
| } |
| return call; |
| } |
| |
| /** |
| * return one active background call from background calls of the |
| * requested subId. |
| * |
| * Active call means the call is NOT idle defined by Call.isIdle() |
| * |
| * 1. If there is only one active background call on given sub, return it |
| * 2. If there is more than one active background call, return the background call |
| * associated with the active sub. |
| * 3. If there is no background call at all, return null. |
| * |
| * Complete background calls list can be get by getBackgroundCalls() |
| */ |
| @UnsupportedAppUsage |
| public Call getFirstActiveBgCall(int subId) { |
| Phone phone = getPhone(subId); |
| if (hasMoreThanOneHoldingCall(subId)) { |
| return phone.getBackgroundCall(); |
| } else { |
| Call call = getFirstNonIdleCall(mBackgroundCalls, subId); |
| if (call == null) { |
| call = (phone == null) |
| ? null |
| : phone.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() |
| */ |
| @UnsupportedAppUsage |
| public Call getFirstActiveRingingCall() { |
| Call call = getFirstNonIdleCall(mRingingCalls); |
| if (call == null) { |
| call = (mDefaultPhone == null) |
| ? null |
| : mDefaultPhone.getRingingCall(); |
| } |
| return call; |
| } |
| |
| @UnsupportedAppUsage |
| public Call getFirstActiveRingingCall(int subId) { |
| Phone phone = getPhone(subId); |
| Call call = getFirstNonIdleCall(mRingingCalls, subId); |
| if (call == null) { |
| call = (phone == null) |
| ? null |
| : phone.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; |
| } |
| |
| @UnsupportedAppUsage |
| public Call.State getActiveFgCallState(int subId) { |
| Call fgCall = getActiveFgCall(subId); |
| |
| 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 |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public List<Connection> getFgCallConnections() { |
| Call fgCall = getActiveFgCall(); |
| if ( fgCall != null) { |
| return fgCall.getConnections(); |
| } |
| return mEmptyConnections; |
| } |
| |
| /** |
| * @return the connections of active foreground call |
| * return empty list if there is no active foreground call |
| */ |
| public List<Connection> getFgCallConnections(int subId) { |
| Call fgCall = getActiveFgCall(subId); |
| if ( fgCall != null) { |
| return fgCall.getConnections(); |
| } |
| return mEmptyConnections; |
| } |
| |
| /** |
| * @return the connections of active background call |
| * return empty list if there is no active background call |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public List<Connection> getBgCallConnections() { |
| Call bgCall = getFirstActiveBgCall(); |
| if ( bgCall != null) { |
| return bgCall.getConnections(); |
| } |
| return mEmptyConnections; |
| } |
| |
| /** |
| * @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 Foreground call in disconnected state |
| */ |
| public boolean hasDisconnectedFgCall(int subId) { |
| return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED, |
| subId) != 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 true if there is at least one background call in disconnected state |
| */ |
| public boolean hasDisconnectedBgCall(int subId) { |
| return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED, |
| subId) != 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 active call from a call list |
| */ |
| private Call getFirstActiveCall(ArrayList<Call> calls, int subId) { |
| for (Call call : calls) { |
| if ((!call.isIdle()) && (call.getPhone().getSubId() == subId)) { |
| 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; |
| } |
| |
| /** |
| * @return the first call in a the Call.state from a call list |
| */ |
| private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state, |
| int subId) { |
| for (Call call : calls) { |
| if ((call.getState() == state) || (call.getPhone().getSubId() == subId)) { |
| return call; |
| } |
| } |
| return null; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean hasMoreThanOneRingingCall() { |
| int count = 0; |
| for (Call call : mRingingCalls) { |
| if (call.getState().isRinging()) { |
| if (++count > 1) return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @return true if more than one active ringing call exists on |
| * the active subId. |
| * This checks for the active calls on provided subId. |
| * |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private boolean hasMoreThanOneRingingCall(int subId) { |
| int count = 0; |
| for (Call call : mRingingCalls) { |
| if ((call.getState().isRinging()) && (call.getPhone().getSubId() == subId)) { |
| if (++count > 1) return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @return true if more than one active background call exists on |
| * the provided subId. |
| * This checks for the background calls on provided subId. |
| * |
| */ |
| private boolean hasMoreThanOneHoldingCall(int subId) { |
| int count = 0; |
| for (Call call : mBackgroundCalls) { |
| if ((call.getState() == Call.State.HOLDING) && (call.getPhone().getSubId() == subId)) { |
| if (++count > 1) return true; |
| } |
| } |
| return false; |
| } |
| |
| private class CallManagerHandler extends Handler { |
| @Override |
| public void handleMessage(Message msg) { |
| |
| switch (msg.what) { |
| case EVENT_DISCONNECT: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)"); |
| mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_PRECISE_CALL_STATE_CHANGED: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)"); |
| mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_NEW_RINGING_CONNECTION: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)"); |
| Connection c = (Connection) ((AsyncResult) msg.obj).result; |
| int subId = c.getCall().getPhone().getSubId(); |
| boolean incomingRejected = false; |
| if ((c.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) |
| && ((ImsPhoneConnection) c).isIncomingCallAutoRejected()) { |
| incomingRejected = true; |
| } |
| if ((getActiveFgCallState(subId).isDialing() || hasMoreThanOneRingingCall()) |
| && (!incomingRejected)) { |
| try { |
| Rlog.d(LOG_TAG, "silently drop incoming call: " + c.getCall()); |
| c.getCall().hangup(); |
| } catch (CallStateException e) { |
| Rlog.w(LOG_TAG, "new ringing connection", e); |
| } |
| } else { |
| mNewRingingConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| } |
| break; |
| case EVENT_UNKNOWN_CONNECTION: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_UNKNOWN_CONNECTION)"); |
| mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_INCOMING_RING: |
| if (VDBG) Rlog.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) Rlog.d(LOG_TAG, " handleMessage (EVENT_RINGBACK_TONE)"); |
| mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_IN_CALL_VOICE_PRIVACY_ON: |
| if (VDBG) Rlog.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) Rlog.d(LOG_TAG, " handleMessage (EVENT_IN_CALL_VOICE_PRIVACY_OFF)"); |
| mInCallVoicePrivacyOffRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_CALL_WAITING: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_CALL_WAITING)"); |
| mCallWaitingRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_DISPLAY_INFO: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_DISPLAY_INFO)"); |
| mDisplayInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_SIGNAL_INFO: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_SIGNAL_INFO)"); |
| mSignalInfoRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_CDMA_OTA_STATUS_CHANGE: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_CDMA_OTA_STATUS_CHANGE)"); |
| mCdmaOtaStatusChangeRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_RESEND_INCALL_MUTE: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_RESEND_INCALL_MUTE)"); |
| mResendIncallMuteRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_MMI_INITIATE: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_MMI_INITIATE)"); |
| mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_MMI_COMPLETE: |
| Rlog.d(LOG_TAG, "CallManager: handleMessage (EVENT_MMI_COMPLETE)"); |
| mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_ECM_TIMER_RESET: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_ECM_TIMER_RESET)"); |
| mEcmTimerResetRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_SUBSCRIPTION_INFO_READY: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_SUBSCRIPTION_INFO_READY)"); |
| mSubscriptionInfoReadyRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_SUPP_SERVICE_FAILED: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_SUPP_SERVICE_FAILED)"); |
| mSuppServiceFailedRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_SERVICE_STATE_CHANGED: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_SERVICE_STATE_CHANGED)"); |
| mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| //setAudioMode(); |
| 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) Rlog.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; |
| case EVENT_ONHOLD_TONE: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_ONHOLD_TONE)"); |
| mOnHoldToneRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| case EVENT_TTY_MODE_RECEIVED: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_TTY_MODE_RECEIVED)"); |
| mTtyModeReceivedRegistrants.notifyRegistrants((AsyncResult) msg.obj); |
| break; |
| /* FIXME Taken from klp-sprout-dev but setAudioMode was removed in L. |
| case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: |
| if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_RADIO_OFF_OR_NOT_AVAILABLE)"); |
| setAudioMode(); |
| break; |
| */ |
| } |
| } |
| }; |
| } |