diff options
-rw-r--r-- | Android.bp | 24 | ||||
-rw-r--r-- | Android.mk | 28 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsCall.java | 18 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsConnectionStateListener.java | 7 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsManager.java | 435 | ||||
-rw-r--r-- | src/java/com/android/ims/MmTelFeatureConnection.java | 394 | ||||
-rw-r--r-- | tests/Android.bp | 35 | ||||
-rw-r--r-- | tests/Android.mk | 36 |
9 files changed, 658 insertions, 320 deletions
diff --git a/Android.bp b/Android.bp new file mode 100644 index 00000000..5a1d1e7e --- /dev/null +++ b/Android.bp @@ -0,0 +1,24 @@ +// Copyright 2013 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. + +java_library { + name: "ims-common", + installable: true, + + aidl: { + local_include_dirs: ["src/java"], + }, + srcs: ["src/java/**/*.java"], + +} diff --git a/Android.mk b/Android.mk deleted file mode 100644 index 85b734ad..00000000 --- a/Android.mk +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src/java -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src/java) - -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := ims-common - -include $(BUILD_JAVA_LIBRARY) - -include $(call all-makefiles-under,$(LOCAL_PATH)) @@ -1,3 +1,4 @@ breadley@google.com hallliu@google.com tgunn@google.com +paulye@google.com diff --git a/src/java/com/android/ims/ImsCall.java b/src/java/com/android/ims/ImsCall.java index 582a0269..667cb3d4 100644 --- a/src/java/com/android/ims/ImsCall.java +++ b/src/java/com/android/ims/ImsCall.java @@ -1575,7 +1575,7 @@ public class ImsCall implements ICall { * @param result the result message to send when done. */ public void sendDtmf(char c, Message result) { - logi("sendDtmf :: code=" + c); + logi("sendDtmf :: "); synchronized(mLockObj) { if (mSession != null) { @@ -1592,7 +1592,7 @@ public class ImsCall implements ICall { * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. */ public void startDtmf(char c) { - logi("startDtmf :: code=" + c); + logi("startDtmf :: "); synchronized(mLockObj) { if (mSession != null) { @@ -1648,16 +1648,20 @@ public class ImsCall implements ICall { /** * Sends a user-requested RTT upgrade request. + * @param rttOn true if the request is to turn on RTT, false to turn off. */ - public void sendRttModifyRequest() { + public void sendRttModifyRequest(boolean rttOn) { logi("sendRttModifyRequest"); synchronized(mLockObj) { if (mSession == null) { loge("sendRttModifyRequest::no session"); } - if (mCallProfile.mMediaProfile.isRttCall()) { - logi("sendRttModifyRequest::Already RTT call, ignoring."); + if (rttOn && mCallProfile.mMediaProfile.isRttCall()) { + logi("sendRttModifyRequest::Already RTT call, ignoring request to turn on."); + return; + } else if (!rttOn && !mCallProfile.mMediaProfile.isRttCall()) { + logi("sendRttModifyRequest::Not RTT call, ignoring request to turn off."); return; } // Make a copy of the current ImsCallProfile and modify it to enable RTT @@ -1665,7 +1669,9 @@ public class ImsCall implements ICall { mCallProfile.writeToParcel(p, 0); p.setDataPosition(0); ImsCallProfile requestedProfile = new ImsCallProfile(p); - requestedProfile.mMediaProfile.setRttMode(ImsStreamMediaProfile.RTT_MODE_FULL); + requestedProfile.mMediaProfile.setRttMode(rttOn + ? ImsStreamMediaProfile.RTT_MODE_FULL + : ImsStreamMediaProfile.RTT_MODE_DISABLED); mSession.sendRttModifyRequest(requestedProfile); } diff --git a/src/java/com/android/ims/ImsConnectionStateListener.java b/src/java/com/android/ims/ImsConnectionStateListener.java index 8ac473a5..8bfab8ce 100644 --- a/src/java/com/android/ims/ImsConnectionStateListener.java +++ b/src/java/com/android/ims/ImsConnectionStateListener.java @@ -17,6 +17,7 @@ package com.android.ims; import android.net.Uri; +import android.telephony.ims.ImsMmTelManager; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; @@ -28,10 +29,10 @@ import java.util.Arrays; * Listener for receiving notifications about changes to the IMS connection. * It provides a state of IMS registration between UE and IMS network, the service * availability of the local device during IMS registered. - * @Deprecated Use {@link ImsRegistrationImplBase.Callback} instead. + * @Deprecated Use {@link ImsMmTelManager.RegistrationCallback} instead. * @hide */ -public class ImsConnectionStateListener extends ImsRegistrationImplBase.Callback { +public class ImsConnectionStateListener extends ImsMmTelManager.RegistrationCallback { @Override public final void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) { @@ -69,7 +70,7 @@ public class ImsConnectionStateListener extends ImsRegistrationImplBase.Callback */ public void onFeatureCapabilityChangedAdapter( @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, - ImsFeature.Capabilities c) { + MmTelFeature.MmTelCapabilities c) { // Size of ImsConfig.FeatureConstants int[] enabledCapabilities = new int[6]; // UNKNOWN means disabled. diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java index 590ab5ac..1468da27 100644 --- a/src/java/com/android/ims/ImsManager.java +++ b/src/java/com/android/ims/ImsManager.java @@ -21,15 +21,19 @@ import android.app.PendingIntent; import android.content.Context; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SystemProperties; +import android.provider.Settings; import android.telecom.TelecomManager; import android.telephony.CarrierConfigManager; +import android.telephony.ims.ImsMmTelManager; +import android.telephony.ims.aidl.IImsCapabilityCallback; +import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.Rlog; import android.telephony.SubscriptionManager; @@ -55,15 +59,18 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Set; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; /** * Provides APIs for IMS services, such as initiating IMS calls, and provides access to * the operator's IMS network. This class is the starting point for any IMS actions. * You can acquire an instance of it with {@link #getInstance getInstance()}.</p> - * <p>The APIs in this class allows you to:</p> - * + * @deprecated use {@link ImsMmTelManager} instead. * @hide */ public class ImsManager { @@ -171,6 +178,8 @@ public class ImsManager { private static final String TAG = "ImsManager"; private static final boolean DBG = true; + private static final int RESPONSE_WAIT_TIME_MS = 3000; + /** * Helper class for managing a connection to the ImsManager when the ImsService is unavailable * or switches to another service. @@ -237,6 +246,7 @@ public class ImsManager { } } } catch (ImsException e) { + loge("notifyStateChanged(): ", e); // Could not get the ImsService, retry! notifyNotReady(); retryGetImsService(); @@ -433,7 +443,8 @@ public class ImsManager { /** * Returns the user configuration of Enhanced 4G LTE Mode setting for slot. If the option is - * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), or + * not editable ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), + * hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), or * the setting is not initialized, this method will return default value specified by * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. * @@ -447,8 +458,10 @@ public class ImsManager { boolean onByDefault = getBooleanCarrierConfig( CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL); - // If Enhanced 4G LTE Mode is uneditable or not initialized, we use the default value + // If Enhanced 4G LTE Mode is uneditable, hidden or not initialized, we use the default + // value if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) + || getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL) || setting == SUB_PROPERTY_NOT_INITIALIZED) { return onByDefault; } else { @@ -473,27 +486,35 @@ public class ImsManager { /** * Change persistent Enhanced 4G LTE Mode setting. If the option is not editable - * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false), this method will - * set the setting to the default value specified by + * ({@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL} is false) + * or hidden ({@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} is true), + * this method will set the setting to the default value specified by * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. - * */ public void setEnhanced4gLteModeSetting(boolean enabled) { - // If editable=false, we must keep default advanced 4G mode. - if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) { + int subId = getSubId(); + // If editable=false or hidden=true, we must keep default advanced 4G mode. + if (!getBooleanCarrierConfig(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL) || + getBooleanCarrierConfig(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)) { enabled = getBooleanCarrierConfig( CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL); } - int prevSetting = SubscriptionManager.getIntegerSubscriptionProperty( - getSubId(), SubscriptionManager.ENHANCED_4G_MODE_ENABLED, - SUB_PROPERTY_NOT_INITIALIZED, mContext); + int prevSetting = SubscriptionManager.getIntegerSubscriptionProperty(subId, + SubscriptionManager.ENHANCED_4G_MODE_ENABLED, SUB_PROPERTY_NOT_INITIALIZED, + mContext); if (prevSetting != (enabled ? ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF)) { - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.ENHANCED_4G_MODE_ENABLED, booleanToPropertyString(enabled)); + if (isSubIdValid(subId)) { + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.ENHANCED_4G_MODE_ENABLED, + booleanToPropertyString(enabled)); + } else { + loge("setEnhanced4gLteModeSetting: invalid sub id, can not set property in " + + " siminfo db; subId=" + subId); + } if (isNonTtyOrTtyOnVolteEnabled()) { try { setAdvanced4GMode(enabled); @@ -525,8 +546,7 @@ public class ImsManager { * supported on a per slot basis. */ public boolean isNonTtyOrTtyOnVolteEnabled() { - if (getBooleanCarrierConfig( - CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) { + if (isTtyOnVoLteCapable()) { return true; } @@ -538,6 +558,10 @@ public class ImsManager { return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF; } + public boolean isTtyOnVoLteCapable() { + return getBooleanCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL); + } + /** * Returns a platform configuration for VoLTE which may override the user setting. * @deprecated Does not support MSIM devices. Please use @@ -759,8 +783,14 @@ public class ImsManager { * Change persistent VT enabled setting for slot. */ public void setVtSetting(boolean enabled) { - SubscriptionManager.setSubscriptionProperty(getSubId(), SubscriptionManager.VT_IMS_ENABLED, - booleanToPropertyString(enabled)); + int subId = getSubId(); + if (isSubIdValid(subId)) { + SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.VT_IMS_ENABLED, + booleanToPropertyString(enabled)); + } else { + loge("setVtSetting: sub id invalid, skip modifying vt state in subinfo db; subId=" + + subId); + } try { changeMmTelCapability(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, @@ -870,12 +900,18 @@ public class ImsManager { * Change persistent WFC enabled setting for slot. */ public void setWfcSetting(boolean enabled) { - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_ENABLED, booleanToPropertyString(enabled)); + int subId = getSubId(); + if (isSubIdValid(subId)) { + SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_ENABLED, + booleanToPropertyString(enabled)); + } else { + loge("setWfcSetting: invalid sub id, can not set WFC setting in siminfo db; subId=" + + subId); + } TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - setWfcNonPersistent(enabled, getWfcMode(tm.isNetworkRoaming(getSubId()))); + setWfcNonPersistent(enabled, getWfcMode(tm.isNetworkRoaming(subId))); } /** @@ -951,8 +987,13 @@ public class ImsManager { public void setWfcMode(int wfcMode) { if (DBG) log("setWfcMode(i) - setting=" + wfcMode); - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_MODE, Integer.toString(wfcMode)); + int subId = getSubId(); + if (isSubIdValid(subId)) { + SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_MODE, + Integer.toString(wfcMode)); + } else { + loge("setWfcMode: invalid sub id, skip setting value in siminfo db; subId=" + subId); + } setWfcModeInternal(wfcMode); } @@ -1046,14 +1087,20 @@ public class ImsManager { * @param roaming {@code false} for home network setting, {@code true} for roaming setting */ public void setWfcMode(int wfcMode, boolean roaming) { - if (!roaming) { - if (DBG) log("setWfcMode(i,b) - setting=" + wfcMode); - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_MODE, Integer.toString(wfcMode)); + int subId = getSubId(); + if (isSubIdValid(subId)) { + if (!roaming) { + if (DBG) log("setWfcMode(i,b) - setting=" + wfcMode); + SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.WFC_IMS_MODE, + Integer.toString(wfcMode)); + } else { + if (DBG) log("setWfcMode(i,b) (roaming) - setting=" + wfcMode); + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.WFC_IMS_ROAMING_MODE, Integer.toString(wfcMode)); + } } else { - if (DBG) log("setWfcMode(i,b) (roaming) - setting=" + wfcMode); - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_ROAMING_MODE, Integer.toString(wfcMode)); + loge("setWfcMode(i,b): invalid sub id, skip setting setting in siminfo db; subId=" + + subId); } TelephonyManager tm = (TelephonyManager) @@ -1287,6 +1334,7 @@ public class ImsManager { boolean isImsUsed = updateVolteFeatureValue(); isImsUsed |= updateWfcFeatureAndProvisionedValues(); isImsUsed |= updateVideoCallFeatureValue(); + isImsUsed |= updateRttConfigValue(); if (isImsUsed || !isTurnOffImsAllowedByPlatform()) { // Turn on IMS if it is used. @@ -1416,14 +1464,6 @@ public class ImsManager { * busy, it will try to connect before reporting failure. */ public boolean isServiceAvailable() { - // If we are busy resolving dynamic IMS bindings, we are not available yet. - TelephonyManager tm = (TelephonyManager) - mContext.getSystemService(Context.TELEPHONY_SERVICE); - if (tm.isResolvingImsBinding()) { - Log.d(TAG, "isServiceAvailable: resolving IMS binding, returning false"); - return false; - } - connectIfServiceIsAvailable(); // mImsServiceProxy will always create an ImsServiceProxy. return mMmTelFeatureConnection.isBinderAlive(); @@ -1525,8 +1565,8 @@ public class ImsManager { * @param listener To listen to IMS registration events; It cannot be null * @throws NullPointerException if {@code listener} is null * @throws ImsException if calling the IMS service results in an error - * @deprecated use {@link #addRegistrationCallback(ImsRegistrationImplBase.Callback)} and - * {@link #addCapabilitiesCallback(ImsFeature.CapabilityCallback)} instead. + * @deprecated use {@link #addRegistrationCallback(ImsMmTelManager.RegistrationCallback)} + * instead. */ public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException { if (listener == null) { @@ -1534,29 +1574,32 @@ public class ImsManager { } addRegistrationCallback(listener); // connect the ImsConnectionStateListener to the new CapabilityCallback. - addCapabilitiesCallback(new ImsFeature.CapabilityCallback() { + addCapabilitiesCallback(new ImsMmTelManager.CapabilityCallback() { @Override - public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) { - listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), config); + public void onCapabilitiesStatusChanged( + MmTelFeature.MmTelCapabilities capabilities) { + listener.onFeatureCapabilityChangedAdapter(getRegistrationTech(), capabilities); } }); log("Registration Callback registered."); } /** - * Adds a callback that gets called when IMS registration has changed. - * @param callback A {@link ImsRegistrationImplBase.Callback} that will notify the caller when - * IMS registration status has changed. + * Adds a callback that gets called when IMS registration has changed for the slot ID + * associated with this ImsManager. + * @param callback A {@link ImsMmTelManager.RegistrationCallback} that will notify the caller + * when IMS registration status has changed. * @throws ImsException when the ImsService connection is not available. */ - public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback) + public void addRegistrationCallback(ImsMmTelManager.RegistrationCallback callback) throws ImsException { if (callback == null) { throw new NullPointerException("registration callback can't be null"); } try { - mMmTelFeatureConnection.addRegistrationCallback(callback); + callback.setExecutor(getThreadExecutor()); + mMmTelFeatureConnection.addRegistrationCallback(callback.getBinder()); log("Registration Callback registered."); // Only record if there isn't a RemoteException. } catch (RemoteException e) { @@ -1567,33 +1610,58 @@ public class ImsManager { /** * Removes a previously added registration callback that was added via - * {@link #addRegistrationCallback(ImsRegistrationImplBase.Callback)} . - * @param callback A {@link ImsRegistrationImplBase.Callback} that was previously added. - * @throws ImsException when the ImsService connection is not available. + * {@link #addRegistrationCallback(ImsMmTelManager.RegistrationCallback)} . + * @param callback A {@link ImsMmTelManager.RegistrationCallback} that was previously added. */ - public void removeRegistrationListener(ImsRegistrationImplBase.Callback callback) - throws ImsException { + public void removeRegistrationListener(ImsMmTelManager.RegistrationCallback callback) { if (callback == null) { throw new NullPointerException("registration callback can't be null"); } - try { - mMmTelFeatureConnection.removeRegistrationCallback(callback); - log("Registration callback removed."); - } catch (RemoteException e) { - throw new ImsException("removeRegistrationCallback(IRIB)", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + mMmTelFeatureConnection.removeRegistrationCallback(callback.getBinder()); + log("Registration callback removed."); + } + + /** + * Adds a callback that gets called when IMS registration has changed for a specific + * subscription. + * + * @param callback A {@link ImsMmTelManager.RegistrationCallback} that will notify the caller + * when IMS registration status has changed. + * @param subId The subscription ID to register this registration callback for. + * @throws RemoteException when the ImsService connection is not available. + */ + public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId) + throws RemoteException { + if (callback == null) { + throw new NullPointerException("registration callback can't be null"); + } + mMmTelFeatureConnection.addRegistrationCallbackForSubscription(callback, subId); + log("Registration Callback registered."); + // Only record if there isn't a RemoteException. + } + + /** + * Removes a previously registered {@link ImsMmTelManager.RegistrationCallback} callback that is + * associated with a specific subscription. + */ + public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, + int subId) { + if (callback == null) { + throw new NullPointerException("registration callback can't be null"); } + + mMmTelFeatureConnection.removeRegistrationCallbackForSubscription(callback, subId); } /** * Adds a callback that gets called when MMTel capability status has changed, for example when * Voice over IMS or VT over IMS is not available currently. - * @param callback A {@link ImsFeature.CapabilityCallback} that will notify the caller when - * MMTel capability status has changed. + * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller when + * MMTel capability status has changed. * @throws ImsException when the ImsService connection is not available. */ - public void addCapabilitiesCallback(ImsFeature.CapabilityCallback callback) + public void addCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback) throws ImsException { if (callback == null) { throw new NullPointerException("capabilities callback can't be null"); @@ -1601,7 +1669,8 @@ public class ImsManager { checkAndThrowExceptionIfServiceUnavailable(); try { - mMmTelFeatureConnection.addCapabilityCallback(callback); + callback.setExecutor(getThreadExecutor()); + mMmTelFeatureConnection.addCapabilityCallback(callback.getBinder()); log("Capability Callback registered."); // Only record if there isn't a RemoteException. } catch (RemoteException e) { @@ -1611,6 +1680,51 @@ public class ImsManager { } /** + * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} callback. + * @throws ImsException when the ImsService connection is not available. + */ + public void removeCapabilitiesCallback(ImsMmTelManager.CapabilityCallback callback) + throws ImsException { + if (callback == null) { + throw new NullPointerException("capabilities callback can't be null"); + } + + checkAndThrowExceptionIfServiceUnavailable(); + mMmTelFeatureConnection.removeCapabilityCallback(callback.getBinder()); + } + + /** + * Adds a callback that gets called when IMS capabilities have changed for a specified + * subscription. + * @param callback A {@link ImsMmTelManager.CapabilityCallback} that will notify the caller + * when the IMS Capabilities have changed. + * @param subId The subscription that is associated with the callback. + * @throws RemoteException when the ImsService connection is not available. + */ + public void addCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, int subId) + throws RemoteException { + if (callback == null) { + throw new NullPointerException("registration callback can't be null"); + } + + mMmTelFeatureConnection.addCapabilityCallbackForSubscription(callback, subId); + log("Capability Callback registered for subscription."); + } + + /** + * Removes a previously registered {@link ImsMmTelManager.CapabilityCallback} that was + * associated with a specific subscription. + */ + public void removeCapabilitiesCallbackForSubscription(IImsCapabilityCallback callback, + int subId) { + if (callback == null) { + throw new NullPointerException("capabilities callback can't be null"); + } + + mMmTelFeatureConnection.removeCapabilityCallbackForSubscription(callback, subId); + } + + /** * Removes the registration listener from the IMS service. * * @param listener Previously registered listener that will be removed. Can not be null. @@ -1625,14 +1739,9 @@ public class ImsManager { } checkAndThrowExceptionIfServiceUnavailable(); - try { - mMmTelFeatureConnection.removeRegistrationCallback(listener); - log("Registration Callback/Listener registered."); - // Only record if there isn't a RemoteException. - } catch (RemoteException e) { - throw new ImsException("addRegistrationCallback()", e, - ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); - } + mMmTelFeatureConnection.removeRegistrationCallback(listener.getBinder()); + log("Registration Callback/Listener registered."); + // Only record if there isn't a RemoteException. } public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() { @@ -1842,22 +1951,84 @@ public class ImsManager { } } + public boolean updateRttConfigValue() { + boolean isCarrierSupported = + getBooleanCarrierConfig(CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL); + boolean isRttEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.RTT_CALLING_MODE, 0) != 0; + Log.i(ImsManager.class.getSimpleName(), "update RTT value " + isRttEnabled); + if (isCarrierSupported == true) { + setRttConfig(isRttEnabled); + } + return isCarrierSupported && isRttEnabled; + } + + private void setRttConfig(boolean enabled) { + final int value = enabled ? ImsConfig.FeatureValueConstants.ON : + ImsConfig.FeatureValueConstants.OFF; + Thread thread = new Thread(() -> { + try { + Log.i(ImsManager.class.getSimpleName(), "Setting RTT enabled to " + enabled); + getConfigInterface().setProvisionedValue( + ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value); + } catch (ImsException e) { + Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT value enabled to " + + enabled + ": " + e); + } + }); + thread.start(); + } + + public boolean queryMmTelCapability( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { + checkAndThrowExceptionIfServiceUnavailable(); + + BlockingQueue<Boolean> result = new LinkedBlockingDeque<>(1); + + try { + mMmTelFeatureConnection.queryEnabledCapabilities(capability, radioTech, + new IImsCapabilityCallback.Stub() { + @Override + public void onQueryCapabilityConfiguration(int resCap, int resTech, + boolean enabled) { + if (resCap == capability && resTech == radioTech) { + result.offer(enabled); + } + } + + @Override + public void onChangeCapabilityConfigurationError(int capability, + int radioTech, int reason) { + + } + + @Override + public void onCapabilitiesStatusChanged(int config) { + + } + }); + } catch (RemoteException e) { + throw new ImsException("queryMmTelCapability()", e, + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN); + } + + try { + return result.poll(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Log.w(TAG, "queryMmTelCapability: interrupted while waiting for response"); + } + return false; + } + public void setRttEnabled(boolean enabled) { try { - setAdvanced4GMode(enabled || isEnhanced4gLteModeSettingEnabledByUser()); - final int value = enabled ? ImsConfig.FeatureValueConstants.ON : - ImsConfig.FeatureValueConstants.OFF; - Thread thread = new Thread(() -> { - try { - Log.i(ImsManager.class.getSimpleName(), "Setting RTT enabled to " + enabled); - getConfigInterface().setProvisionedValue( - ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value); - } catch (ImsException e) { - Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT enabled to " - + enabled + ": " + e); - } - }); - thread.start(); + if (enabled) { + setEnhanced4gLteModeSetting(enabled); + } else { + setAdvanced4GMode(enabled || isEnhanced4gLteModeSettingEnabledByUser()); + } + setRttConfig(enabled); } catch (ImsException e) { Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT enabled to " + enabled + ": " + e); @@ -1930,6 +2101,13 @@ public class ImsManager { return mMmTelFeatureConnection.getFeatureState(); } + private Executor getThreadExecutor() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + return new HandlerExecutor(new Handler(Looper.myLooper())); + } + /** * Get the boolean config from carrier config manager. * @@ -1937,15 +2115,10 @@ public class ImsManager { * @return boolean value of corresponding key. */ private boolean getBooleanCarrierConfig(String key) { - int[] subIds = SubscriptionManager.getSubId(mPhoneId); - int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - if (subIds != null && subIds.length >= 1) { - subId = subIds[0]; - } PersistableBundle b = null; if (mConfigManager != null) { // If an invalid subId is used, this bundle will contain default values. - b = mConfigManager.getConfigForSubId(subId); + b = mConfigManager.getConfigForSubId(getSubId()); } if (b != null) { return b.getBoolean(key); @@ -1962,15 +2135,10 @@ public class ImsManager { * @return integer value of corresponding key. */ private int getIntCarrierConfig(String key) { - int[] subIds = SubscriptionManager.getSubId(mPhoneId); - int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - if (subIds != null && subIds.length >= 1) { - subId = subIds[0]; - } PersistableBundle b = null; if (mConfigManager != null) { // If an invalid subId is used, this bundle will contain default values. - b = mConfigManager.getConfigForSubId(subId); + b = mConfigManager.getConfigForSubId(getSubId()); } if (b != null) { return b.getInt(key); @@ -2305,33 +2473,39 @@ public class ImsManager { * @hide */ public void factoryReset() { - // Set VoLTE to default - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.ENHANCED_4G_MODE_ENABLED, - booleanToPropertyString(getBooleanCarrierConfig( - CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL))); - - // Set VoWiFi to default - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_ENABLED, - booleanToPropertyString(getBooleanCarrierConfig( - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL))); - - // Set VoWiFi mode to default - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_MODE, - Integer.toString(getIntCarrierConfig( - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT))); - - // Set VoWiFi roaming to default - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.WFC_IMS_ROAMING_ENABLED, - booleanToPropertyString(getBooleanCarrierConfig( - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL))); - - // Set VT to default - SubscriptionManager.setSubscriptionProperty(getSubId(), - SubscriptionManager.VT_IMS_ENABLED, booleanToPropertyString(true)); + int subId = getSubId(); + if (isSubIdValid(subId)) { + // Set VoLTE to default + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.ENHANCED_4G_MODE_ENABLED, + booleanToPropertyString(getBooleanCarrierConfig( + CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL))); + + // Set VoWiFi to default + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.WFC_IMS_ENABLED, + booleanToPropertyString(getBooleanCarrierConfig( + CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL))); + + // Set VoWiFi mode to default + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.WFC_IMS_MODE, + Integer.toString(getIntCarrierConfig( + CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT))); + + // Set VoWiFi roaming to default + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.WFC_IMS_ROAMING_ENABLED, + booleanToPropertyString(getBooleanCarrierConfig( + CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL))); + + + // Set VT to default + SubscriptionManager.setSubscriptionProperty(subId, + SubscriptionManager.VT_IMS_ENABLED, booleanToPropertyString(true)); + } else { + loge("factoryReset: invalid sub id, can not reset siminfo db settings; subId=" + subId); + } // Push settings to ImsConfig updateImsServiceConfig(true); @@ -2390,4 +2564,15 @@ public class ImsManager { pw.println(" isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice()); pw.flush(); } + + /** + * Determines if a sub id is valid. + * Mimics the logic in SubscriptionController.validateSubId. + * @param subId The sub id to check. + * @return {@code true} if valid, {@code false} otherwise. + */ + private boolean isSubIdValid(int subId) { + return SubscriptionManager.isValidSubscriptionId(subId) && + subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + } } diff --git a/src/java/com/android/ims/MmTelFeatureConnection.java b/src/java/com/android/ims/MmTelFeatureConnection.java index d6954f3f..16c4a493 100644 --- a/src/java/com/android/ims/MmTelFeatureConnection.java +++ b/src/java/com/android/ims/MmTelFeatureConnection.java @@ -18,14 +18,18 @@ package com.android.ims; import android.annotation.Nullable; import android.content.Context; -import android.net.Uri; import android.os.IBinder; +import android.os.IInterface; +import android.os.Looper; import android.os.Message; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.Rlog; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.ims.ImsCallProfile; -import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsMmTelFeature; import android.telephony.ims.aidl.IImsRegistration; @@ -36,18 +40,22 @@ import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.ImsSmsImplBase; +import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.ims.internal.IImsUt; +import com.android.internal.annotations.VisibleForTesting; +import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * A container of the IImsServiceController binder, which implements all of the ImsFeatures that @@ -56,137 +64,239 @@ import java.util.concurrent.ConcurrentHashMap; */ public class MmTelFeatureConnection { - protected static final String TAG = "MmTelFeatureConnection"; - protected final int mSlotId; - protected IBinder mBinder; - private Context mContext; - - private volatile boolean mIsAvailable = false; - // ImsFeature Status from the ImsService. Cached. - private Integer mFeatureStateCached = null; - private IFeatureUpdate mStatusCallback; - private final Object mLock = new Object(); - // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent. - private boolean mSupportsEmergencyCalling = false; - // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If - // it becomes disconnected, invalidate. - private IImsRegistration mRegistrationBinder; - private IImsConfig mConfigBinder; - - private IBinder.DeathRecipient mDeathRecipient = () -> { - Log.w(TAG, "DeathRecipient triggered, binder died."); - onRemovedOrDied(); - }; - - private abstract class CallbackAdapterManager<T> { + // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection. + @VisibleForTesting + public static abstract class CallbackAdapterManager<T extends IInterface> { private static final String TAG = "CallbackAdapterManager"; - protected final Set<T> mLocalCallbacks = - Collections.newSetFromMap(new ConcurrentHashMap<>()); - private boolean mHasConnected = false; + private final Context mContext; + private final Object mLock; + // Map of sub id -> List<callbacks> for sub id linked callbacks. + private final SparseArray<Set<T>> mCallbackSubscriptionMap = new SparseArray<>(); + // List of all active callbacks to ImsService + private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>(); + @VisibleForTesting + public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener; + + public CallbackAdapterManager(Context context, Object lock) { + mContext = context; + mLock = lock; + if (Looper.myLooper() == null) { + Looper.prepare(); + } + // Must be created after Looper.prepare() is called, or else we will get an exception. + mSubChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + SubscriptionManager manager = mContext.getSystemService( + SubscriptionManager.class); + if (manager == null) { + Log.w(TAG, "onSubscriptionsChanged: could not find SubscriptionManager."); + return; + } + List<SubscriptionInfo> subInfos = manager.getActiveSubscriptionInfoList(); + if (subInfos == null) { + subInfos = Collections.emptyList(); + } + Set<Integer> newSubIds = subInfos.stream() + .map(SubscriptionInfo::getSubscriptionId) + .collect(Collectors.toSet()); + synchronized (mLock) { + Set<Integer> storedSubIds = new ArraySet<>(mCallbackSubscriptionMap.size()); + for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); + keyIndex++) { + storedSubIds.add(mCallbackSubscriptionMap.keyAt(keyIndex)); + } + // Get the set of sub ids that are in storedSubIds that are not in newSubIds. + // This is the set of sub ids that need to be removed. + storedSubIds.removeAll(newSubIds); + for (Integer subId : storedSubIds) { + removeCallbacksForSubscription(subId); + } + } + } + }; + + } - public void addCallback(T localCallback) throws RemoteException { - // We only one one binding to the ImsService per process. - // Store any more locally. + // Add a callback to the MmTelFeature associated with this manager (independent of the) + // current subscription. + public final void addCallback(T localCallback) throws RemoteException { synchronized (mLock) { - if (!mHasConnected) { + // Skip registering to callback subscription map here, because we are registering + // for the slot, independent of subscription (deprecated behavior). + if (!registerCallback(localCallback)) { // throws a RemoteException if a connection can not be established. - if (createConnection()) { - mHasConnected = true; - } else { - throw new RemoteException("Can not create connection!"); - } + throw new RemoteException("Can not create connection!"); } + Log.i(TAG, "Local callback added: " + localCallback); + mRemoteCallbacks.register(localCallback); } - Log.i(TAG, "Local callback added: " + localCallback); - mLocalCallbacks.add(localCallback); } - public void removeCallback(T localCallback) { - // We only maintain one binding to the ImsService per process. - Log.i(TAG, "Local callback removed: " + localCallback); - mLocalCallbacks.remove(localCallback); + // Add a callback to be associated with a subscription. If that subscription is removed, + // remove the callback and notify the callback that the subscription has been removed. + public final void addCallbackForSubscription(T localCallback, int subId) + throws RemoteException { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + return; + } synchronized (mLock) { - // If we have removed all local callbacks, remove callback to ImsService. - if(mHasConnected) { - if (mLocalCallbacks.isEmpty()) { - removeConnection(); - mHasConnected = false; - } - } + addCallback(localCallback); + linkCallbackToSubscription(localCallback, subId); } } - public void close() { + // Removes a callback associated with the MmTelFeature. + public final void removeCallback(T localCallback) { + Log.i(TAG, "Local callback removed: " + localCallback); synchronized (mLock) { - if (mHasConnected) { - removeConnection(); - // Still mark the connection as disconnected, even if this fails. - mHasConnected = false; + if (mRemoteCallbacks.unregister(localCallback)) { + // Will only occur if we have record of this callback in mRemoteCallbacks. + unregisterCallback(localCallback); } } - Log.i(TAG, "Closing connection and clearing callbacks"); - mLocalCallbacks.clear(); } - abstract boolean createConnection() throws RemoteException; - - abstract void removeConnection(); - } - private ImsRegistrationCallbackAdapter mRegistrationCallbackManager - = new ImsRegistrationCallbackAdapter(); - private class ImsRegistrationCallbackAdapter - extends CallbackAdapterManager<ImsRegistrationImplBase.Callback> { - private final RegistrationCallbackAdapter mRegistrationCallbackAdapter - = new RegistrationCallbackAdapter(); + // Remove an existing callback that has been linked to a subscription. + public final void removeCallbackForSubscription(T localCallback, int subId) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + return; + } + synchronized (mLock) { + removeCallback(localCallback); + unlinkCallbackFromSubscription(localCallback, subId); + } + } - private class RegistrationCallbackAdapter extends IImsRegistrationCallback.Stub { + // Links a callback to be tracked by a subscription. If it goes away, emove. + private void linkCallbackToSubscription(T callback, int subId) { + synchronized (mLock) { + if (mCallbackSubscriptionMap.size() == 0) { + // we are about to add the first entry to the map, register for subscriptions + //changed listener. + registerForSubscriptionsChanged(); + } + Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId); + if (callbacksPerSub == null) { + // the callback list has not been created yet for this subscription. + callbacksPerSub = new ArraySet<>(); + mCallbackSubscriptionMap.put(subId, callbacksPerSub); + } + callbacksPerSub.add(callback); + } + } - @Override - public void onRegistered(int imsRadioTech) { - Log.i(TAG, "onRegistered ::"); + // Unlink the callback from the associated subscription. + private void unlinkCallbackFromSubscription(T callback, int subId) { + synchronized (mLock) { + Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId); + if (callbacksPerSub != null) { + callbacksPerSub.remove(callback); + if (callbacksPerSub.isEmpty()) { + mCallbackSubscriptionMap.remove(subId); + } + } + if (mCallbackSubscriptionMap.size() == 0) { + unregisterForSubscriptionsChanged(); + } + } + } - mLocalCallbacks.forEach(l -> l.onRegistered(imsRadioTech)); + // Removes all of the callbacks that have been registered to the subscription specified. + // This happens when Telephony sends an indication that the subscriptions have changed. + private void removeCallbacksForSubscription(int subId) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + return; } + synchronized (mLock) { + Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId); + if (callbacksPerSub == null) { + // no callbacks registered for this subscription. + return; + } + // clear all registered callbacks in the subscription map for this subscription. + mCallbackSubscriptionMap.remove(subId); + for (T callback : callbacksPerSub) { + removeCallback(callback); + } + // If there are no more callbacks being tracked, remove subscriptions changed + // listener. + if (mCallbackSubscriptionMap.size() == 0) { + unregisterForSubscriptionsChanged(); + } + } + } - @Override - public void onRegistering(int imsRadioTech) { - Log.i(TAG, "onRegistering ::"); + // Clear the Subscription -> Callback map because the ImsService connection is no longer + // current. + private void clearCallbacksForAllSubscriptions() { + synchronized (mLock) { + List<Integer> keys = new ArrayList<>(); + for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); keyIndex++) { + keys.add(mCallbackSubscriptionMap.keyAt(keyIndex)); + } + keys.forEach(this::removeCallbacksForSubscription); + } + } - mLocalCallbacks.forEach(l -> l.onRegistering(imsRadioTech)); + private void registerForSubscriptionsChanged() { + SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class); + if (manager != null) { + manager.addOnSubscriptionsChangedListener(mSubChangedListener); + } else { + Log.w(TAG, "registerForSubscriptionsChanged: could not find SubscriptionManager."); } + } - @Override - public void onDeregistered(ImsReasonInfo imsReasonInfo) { - Log.i(TAG, "onDeregistered ::"); + private void unregisterForSubscriptionsChanged() { + SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class); + if (manager != null) { + manager.removeOnSubscriptionsChangedListener(mSubChangedListener); + } else { + Log.w(TAG, "unregisterForSubscriptionsChanged: could not find" + + " SubscriptionManager."); + } + } - mLocalCallbacks.forEach(l -> l.onDeregistered(imsReasonInfo)); + // The ImsService these callbacks are registered to has become unavailable or crashed, or + // the ImsResolver has switched to a new ImsService. In these cases, clean up all existing + // callbacks. + public void close() { + synchronized (mLock) { + final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1; + for(int ii = lastCallbackIndex; ii >= 0; ii --) { + T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii); + unregisterCallback(callbackItem); + mRemoteCallbacks.unregister(callbackItem); + } + clearCallbacksForAllSubscriptions(); + Log.i(TAG, "Closing connection and clearing callbacks"); } + } - @Override - public void onTechnologyChangeFailed(int targetRadioTech, ImsReasonInfo imsReasonInfo) { - Log.i(TAG, "onTechnologyChangeFailed :: targetAccessTech=" + targetRadioTech + - ", imsReasonInfo=" + imsReasonInfo); + // A callback has been registered. Register that callback with the MmTelFeature. + public abstract boolean registerCallback(T localCallback) throws RemoteException; - mLocalCallbacks.forEach(l -> l.onTechnologyChangeFailed(targetRadioTech, - imsReasonInfo)); - } + // A callback has been removed, unregister that callback with the MmTelFeature. + public abstract void unregisterCallback(T localCallback); + } - @Override - public void onSubscriberAssociatedUriChanged(Uri[] uris) { - Log.i(TAG, "onSubscriberAssociatedUriChanged"); + private class ImsRegistrationCallbackAdapter extends + CallbackAdapterManager<IImsRegistrationCallback> { - mLocalCallbacks.forEach(l -> l.onSubscriberAssociatedUriChanged(uris)); - } + public ImsRegistrationCallbackAdapter(Context context, Object lock) { + super(context, lock); } @Override - boolean createConnection() throws RemoteException { + public boolean registerCallback(IImsRegistrationCallback localCallback) + throws RemoteException { IImsRegistration imsRegistration = getRegistration(); if (imsRegistration != null) { - getRegistration().addRegistrationCallback(mRegistrationCallbackAdapter); + getRegistration().addRegistrationCallback(localCallback); return true; } else { Log.e(TAG, "ImsRegistration is null"); @@ -195,13 +305,13 @@ public class MmTelFeatureConnection { } @Override - void removeConnection() { + public void unregisterCallback(IImsRegistrationCallback localCallback) { IImsRegistration imsRegistration = getRegistration(); if (imsRegistration != null) { try { - getRegistration().removeRegistrationCallback(mRegistrationCallbackAdapter); + getRegistration().removeRegistrationCallback(localCallback); } catch (RemoteException e) { - Log.w(TAG, "removeConnection: couldn't remove registration callback"); + Log.w(TAG, "unregisterCallback: couldn't remove registration callback"); } } else { Log.e(TAG, "ImsRegistration is null"); @@ -209,30 +319,22 @@ public class MmTelFeatureConnection { } } - private final CapabilityCallbackManager mCapabilityCallbackManager - = new CapabilityCallbackManager(); - private class CapabilityCallbackManager - extends CallbackAdapterManager<ImsFeature.CapabilityCallback> { - private final CapabilityCallbackAdapter mCallbackAdapter = new CapabilityCallbackAdapter(); + private class CapabilityCallbackManager extends CallbackAdapterManager<IImsCapabilityCallback> { - private class CapabilityCallbackAdapter extends ImsFeature.CapabilityCallback { - // Called when the Capabilities Status on this connection have changed. - @Override - public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) { - mLocalCallbacks.forEach( - callback -> callback.onCapabilitiesStatusChanged(config)); - } + public CapabilityCallbackManager(Context context, Object lock) { + super(context, lock); } @Override - boolean createConnection() throws RemoteException { + public boolean registerCallback(IImsCapabilityCallback localCallback) + throws RemoteException { IImsMmTelFeature binder; synchronized (mLock) { checkServiceIsReady(); binder = getServiceInterface(mBinder); } if (binder != null) { - binder.addCapabilityCallback(mCallbackAdapter); + binder.addCapabilityCallback(localCallback); return true; } else { Log.w(TAG, "create: Couldn't get IImsMmTelFeature binder"); @@ -241,7 +343,7 @@ public class MmTelFeatureConnection { } @Override - void removeConnection() { + public void unregisterCallback(IImsCapabilityCallback localCallback) { IImsMmTelFeature binder = null; synchronized (mLock) { try { @@ -249,11 +351,12 @@ public class MmTelFeatureConnection { binder = getServiceInterface(mBinder); } catch (RemoteException e) { // binder is null + Log.w(TAG, "remove: Binder is null"); } } if (binder != null) { try { - binder.removeCapabilityCallback(mCallbackAdapter); + binder.removeCapabilityCallback(localCallback); } catch (RemoteException e) { Log.w(TAG, "remove: IImsMmTelFeature binder is dead"); } @@ -263,6 +366,31 @@ public class MmTelFeatureConnection { } } + protected final int mSlotId; + protected IBinder mBinder; + private Context mContext; + + private volatile boolean mIsAvailable = false; + // ImsFeature Status from the ImsService. Cached. + private Integer mFeatureStateCached = null; + private IFeatureUpdate mStatusCallback; + private final Object mLock = new Object(); + // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent. + private boolean mSupportsEmergencyCalling = false; + + // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If + // it becomes disconnected, invalidate. + private IImsRegistration mRegistrationBinder; + private IImsConfig mConfigBinder; + + private IBinder.DeathRecipient mDeathRecipient = () -> { + Log.w(TAG, "DeathRecipient triggered, binder died."); + onRemovedOrDied(); + }; + + private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager; + private final CapabilityCallbackManager mCapabilityCallbackManager; + public static MmTelFeatureConnection create(Context context , int slotId) { MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId); @@ -373,6 +501,8 @@ public class MmTelFeatureConnection { public MmTelFeatureConnection(Context context, int slotId) { mSlotId = slotId; mContext = context; + mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock); + mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock); } /** @@ -380,6 +510,8 @@ public class MmTelFeatureConnection { */ private void onRemovedOrDied() { synchronized (mLock) { + mRegistrationCallbackManager.close(); + mCapabilityCallbackManager.close(); if (mIsAvailable) { mIsAvailable = false; // invalidate caches. @@ -484,28 +616,46 @@ public class MmTelFeatureConnection { } } - public void addRegistrationCallback(ImsRegistrationImplBase.Callback callback) + public void addRegistrationCallback(IImsRegistrationCallback callback) throws RemoteException { mRegistrationCallbackManager.addCallback(callback); } - public void removeRegistrationCallback(ImsRegistrationImplBase.Callback callback) + public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId) throws RemoteException { + mRegistrationCallbackManager.addCallbackForSubscription(callback , subId); + } + + public void removeRegistrationCallback(IImsRegistrationCallback callback) { mRegistrationCallbackManager.removeCallback(callback); } - public void addCapabilityCallback(ImsFeature.CapabilityCallback callback) + public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, + int subId) { + mRegistrationCallbackManager.removeCallbackForSubscription(callback, subId); + } + + public void addCapabilityCallback(IImsCapabilityCallback callback) throws RemoteException { mCapabilityCallbackManager.addCallback(callback); } - public void removeCapabilityCallback(ImsFeature.CapabilityCallback callback) - throws RemoteException { + public void addCapabilityCallbackForSubscription(IImsCapabilityCallback callback, + int subId) throws RemoteException { + mCapabilityCallbackManager.addCallbackForSubscription(callback, subId); + } + + public void removeCapabilityCallback(IImsCapabilityCallback callback) { mCapabilityCallbackManager.removeCallback(callback); } + public void removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback, + int subId) { + mCapabilityCallbackManager.removeCallbackForSubscription(callback , subId); + } + public void changeEnabledCapabilities(CapabilityChangeRequest request, - ImsFeature.CapabilityCallback callback) throws RemoteException { + IImsCapabilityCallback callback) throws RemoteException { synchronized (mLock) { checkServiceIsReady(); getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback); @@ -513,7 +663,7 @@ public class MmTelFeatureConnection { } public void queryEnabledCapabilities(int capability, int radioTech, - ImsFeature.CapabilityCallback callback) throws RemoteException { + IImsCapabilityCallback callback) throws RemoteException { synchronized (mLock) { checkServiceIsReady(); getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech, diff --git a/tests/Android.bp b/tests/Android.bp new file mode 100644 index 00000000..977b5141 --- /dev/null +++ b/tests/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2016 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. +// + +android_test { + name: "ImsCommonTests", + + srcs: ["src/**/*.java"], + + platform_apis: true, + certificate: "platform", + + libs: [ + "ims-common", + "android.test.runner", + "android.test.base", + ], + + static_libs: [ + "android-support-test", + "mockito-target-minus-junit4", + ], +} diff --git a/tests/Android.mk b/tests/Android.mk deleted file mode 100644 index a0b998b8..00000000 --- a/tests/Android.mk +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (C) 2016 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. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := ImsCommonTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -LOCAL_MODULE_TAGS := tests - -LOCAL_JAVA_LIBRARIES := ims-common \ - android.test.runner \ - android.test.base - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ - mockito-target-minus-junit4 - -include $(BUILD_PACKAGE)
\ No newline at end of file |