aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp24
-rw-r--r--Android.mk28
-rw-r--r--OWNERS1
-rw-r--r--src/java/com/android/ims/ImsCall.java18
-rw-r--r--src/java/com/android/ims/ImsConnectionStateListener.java7
-rw-r--r--src/java/com/android/ims/ImsManager.java435
-rw-r--r--src/java/com/android/ims/MmTelFeatureConnection.java394
-rw-r--r--tests/Android.bp35
-rw-r--r--tests/Android.mk36
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))
diff --git a/OWNERS b/OWNERS
index 2f0bcec6..94409ef1 100644
--- a/OWNERS
+++ b/OWNERS
@@ -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