aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2020-04-28 20:26:09 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2020-04-28 20:26:09 +0000
commit87aa92ce455fa7ae05855bd5679019ce398b8477 (patch)
treeed7ef0d45f5f5115c5be92256019b79c953e2dd3
parent148ef0ecb5c16dc0da6920fe112ab2362afaec42 (diff)
parent1a09d5bc572be7a5a6ef42e41e6ba5a606e369d2 (diff)
downloadims-87aa92ce455fa7ae05855bd5679019ce398b8477.tar.gz
Change-Id: I5ce6a5e44c233654354733e6458844d23eb812b0
-rw-r--r--src/java/com/android/ims/FeatureConnection.java274
-rw-r--r--src/java/com/android/ims/FeatureConnector.java289
-rw-r--r--src/java/com/android/ims/HandlerExecutor.java46
-rw-r--r--src/java/com/android/ims/IFeatureConnector.java24
-rw-r--r--src/java/com/android/ims/ImsCall.java42
-rw-r--r--src/java/com/android/ims/ImsConnectionStateListener.java7
-rw-r--r--src/java/com/android/ims/ImsEcbm.java2
-rw-r--r--src/java/com/android/ims/ImsManager.java552
-rw-r--r--src/java/com/android/ims/ImsUt.java32
-rw-r--r--src/java/com/android/ims/MmTelFeatureConnection.java374
-rw-r--r--src/java/com/android/ims/RcsFeatureConnection.java220
-rw-r--r--src/java/com/android/ims/RcsFeatureManager.java303
-rw-r--r--src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java2
13 files changed, 616 insertions, 1551 deletions
diff --git a/src/java/com/android/ims/FeatureConnection.java b/src/java/com/android/ims/FeatureConnection.java
deleted file mode 100644
index 5e7dcdda..00000000
--- a/src/java/com/android/ims/FeatureConnection.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ims;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
-import android.telephony.ims.aidl.IImsRegistration;
-import android.telephony.ims.feature.ImsFeature;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.Rlog;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.ims.internal.IImsServiceFeatureCallback;
-
-import java.util.concurrent.Executor;
-
-/**
- * Base class of MmTelFeatureConnection and RcsFeatureConnection.
- */
-public abstract class FeatureConnection {
- protected static final String TAG = "FeatureConnection";
-
- public interface IFeatureUpdate {
- /**
- * Called when the ImsFeature has changed its state. Use
- * {@link ImsFeature#getFeatureState()} to get the new state.
- */
- void notifyStateChanged();
-
- /**
- * Called when the ImsFeature has become unavailable due to the binder switching or app
- * crashing. A new ImsServiceProxy should be requested for that feature.
- */
- void notifyUnavailable();
- }
-
- protected static boolean sImsSupportedOnDevice = true;
-
- protected final int mSlotId;
- protected final int mFeatureType;
- protected Context mContext;
- protected IBinder mBinder;
- @VisibleForTesting
- public Executor mExecutor;
-
- // We are assuming the feature is available when started.
- protected volatile boolean mIsAvailable = true;
- // ImsFeature Status from the ImsService. Cached.
- protected Integer mFeatureStateCached = null;
- protected IFeatureUpdate mStatusCallback;
- protected IImsRegistration mRegistrationBinder;
- protected final Object mLock = new Object();
-
- public FeatureConnection(Context context, int slotId, int featureType) {
- mSlotId = slotId;
- mContext = context;
- mFeatureType = featureType;
-
- // Callbacks should be scheduled on the main thread.
- if (context.getMainLooper() != null) {
- mExecutor = context.getMainExecutor();
- } else {
- // Fallback to the current thread.
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- mExecutor = new HandlerExecutor(new Handler(Looper.myLooper()));
- }
- }
-
- protected TelephonyManager getTelephonyManager() {
- return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- /**
- * Set the binder which type is IImsMmTelFeature or IImsRcsFeature to connect to MmTelFeature
- * or RcsFeature.
- */
- public void setBinder(IBinder binder) {
- synchronized (mLock) {
- mBinder = binder;
- try {
- if (mBinder != null) {
- mBinder.linkToDeath(mDeathRecipient, 0);
- }
- } catch (RemoteException e) {
- // No need to do anything if the binder is already dead.
- }
- }
- }
-
- protected final IBinder.DeathRecipient mDeathRecipient = () -> {
- Log.w(TAG, "DeathRecipient triggered, binder died.");
- if (mContext != null && Looper.getMainLooper() != null) {
- // Move this signal to the main thread, notifying ImsManager of the Binder
- // death on another thread may lead to deadlocks.
- mContext.getMainExecutor().execute(this::onRemovedOrDied);
- return;
- }
- // No choice - execute on the current Binder thread.
- onRemovedOrDied();
- };
-
- /**
- * Called when the MmTelFeature/RcsFeature has either been removed by Telephony or crashed.
- */
- protected void onRemovedOrDied() {
- synchronized (mLock) {
- if (mIsAvailable) {
- mIsAvailable = false;
- mRegistrationBinder = null;
- if (mBinder != null) {
- mBinder.unlinkToDeath(mDeathRecipient, 0);
- }
- if (mStatusCallback != null) {
- mStatusCallback.notifyUnavailable();
- }
- }
- }
- }
-
- /**
- * The listener for ImsManger and RcsFeatureManager to receive IMS feature status changed.
- * @param callback Callback that will fire when the feature status has changed.
- */
- public void setStatusCallback(IFeatureUpdate callback) {
- mStatusCallback = callback;
- }
-
- @VisibleForTesting
- public IImsServiceFeatureCallback getListener() {
- return mListenerBinder;
- }
-
- /**
- * The callback to receive ImsFeature status changed.
- */
- private final IImsServiceFeatureCallback mListenerBinder =
- new IImsServiceFeatureCallback.Stub() {
- @Override
- public void imsFeatureCreated(int slotId, int feature) {
- mExecutor.execute(() -> {
- handleImsFeatureCreatedCallback(slotId, feature);
- });
- }
- @Override
- public void imsFeatureRemoved(int slotId, int feature) {
- mExecutor.execute(() -> {
- handleImsFeatureRemovedCallback(slotId, feature);
- });
- }
- @Override
- public void imsStatusChanged(int slotId, int feature, int status) {
- mExecutor.execute(() -> {
- handleImsStatusChangedCallback(slotId, feature, status);
- });
- }
- };
-
- protected abstract void handleImsFeatureCreatedCallback(int slotId, int feature);
- protected abstract void handleImsFeatureRemovedCallback(int slotId, int feature);
- protected abstract void handleImsStatusChangedCallback(int slotId, int feature, int status);
-
- public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
- throws RemoteException {
- IImsRegistration registration = getRegistration();
- if (registration != null) {
- return registration.getRegistrationTechnology();
- } else {
- return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
- }
- }
-
- protected @Nullable IImsRegistration getRegistration() {
- synchronized (mLock) {
- // null if cache is invalid;
- if (mRegistrationBinder != null) {
- return mRegistrationBinder;
- }
- }
- TelephonyManager tm = getTelephonyManager();
- // We don't want to synchronize on a binder call to another process.
- IImsRegistration regBinder = tm != null
- ? tm.getImsRegistration(mSlotId, mFeatureType) : null;
- synchronized (mLock) {
- // mRegistrationBinder may have changed while we tried to get the registration
- // interface.
- if (mRegistrationBinder == null) {
- mRegistrationBinder = regBinder;
- }
- }
- return mRegistrationBinder;
- }
-
- protected void checkServiceIsReady() throws RemoteException {
- if (!sImsSupportedOnDevice) {
- throw new RemoteException("IMS is not supported on this device.");
- }
- if (!isBinderReady()) {
- throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
- }
- }
-
- /**
- * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
- * method returns false, it doesn't mean that the Binder connection is not available (use
- * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
- * at this time.
- *
- * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
- * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
- */
- public boolean isBinderReady() {
- return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
- }
-
- /**
- * @return false if the binder connection is no longer alive.
- */
- public boolean isBinderAlive() {
- return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
- }
-
- /**
- * @return an integer describing the current Feature Status, defined in
- * {@link ImsFeature.ImsState}.
- */
- public int getFeatureState() {
- synchronized (mLock) {
- if (isBinderAlive() && mFeatureStateCached != null) {
- return mFeatureStateCached;
- }
- }
- // Don't synchronize on Binder call.
- Integer state = retrieveFeatureState();
- synchronized (mLock) {
- if (state == null) {
- return ImsFeature.STATE_UNAVAILABLE;
- }
- // Cache only non-null value for feature status.
- mFeatureStateCached = state;
- }
- Log.i(TAG + " [" + mSlotId + "]", "getFeatureState - returning "
- + ImsFeature.STATE_LOG_MAP.get(state));
- return state;
- }
-
- /**
- * Internal method used to retrieve the feature status from the corresponding ImsService.
- */
- protected abstract Integer retrieveFeatureState();
-}
diff --git a/src/java/com/android/ims/FeatureConnector.java b/src/java/com/android/ims/FeatureConnector.java
deleted file mode 100644
index 8000a350..00000000
--- a/src/java/com/android/ims/FeatureConnector.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (c) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ims;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.telephony.ims.feature.ImsFeature;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.concurrent.Executor;
-
-/**
- * Helper class for managing a connection to the ImsFeature manager.
- */
-public class FeatureConnector<T extends IFeatureConnector> extends Handler {
- private static final String TAG = "FeatureConnector";
-
- // Initial condition for ims connection retry.
- private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
-
- // Ceiling bitshift amount for service query timeout, calculated as:
- // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
- // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
- private static final int CEILING_SERVICE_RETRY_COUNT = 6;
-
- public interface Listener<T> {
- /**
- * Check if ImsFeature supported
- */
- boolean isSupported();
-
- /**
- * Get ImsFeature manager instance
- */
- T getFeatureManager();
-
- /**
- * ImsFeature manager is connected to the underlying IMS implementation.
- */
- void connectionReady(T manager) throws ImsException;
-
- /**
- * The underlying IMS implementation is unavailable and can not be used to communicate.
- */
- void connectionUnavailable();
- }
-
- public interface RetryTimeout {
- int get();
- }
-
- protected final int mPhoneId;
- protected final Context mContext;
- protected final Executor mExecutor;
- protected final Object mLock = new Object();
- protected final String mLogPrefix;
-
- @VisibleForTesting
- public Listener<T> mListener;
-
- // The IMS feature manager which interacts with ImsService
- @VisibleForTesting
- public T mManager;
-
- protected int mRetryCount = 0;
-
- @VisibleForTesting
- public RetryTimeout mRetryTimeout = () -> {
- synchronized (mLock) {
- int timeout = (1 << mRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
- if (mRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
- mRetryCount++;
- }
- return timeout;
- }
- };
-
- public FeatureConnector(Context context, int phoneId, Listener<T> listener) {
- mContext = context;
- mPhoneId = phoneId;
- mListener = listener;
- mExecutor = new HandlerExecutor(this);
- mLogPrefix = "?";
- }
-
- public FeatureConnector(Context context, int phoneId, Listener<T> listener,
- String logPrefix) {
- mContext = context;
- mPhoneId = phoneId;
- mListener = listener;
- mExecutor = new HandlerExecutor(this);
- mLogPrefix = logPrefix;
- }
-
- @VisibleForTesting
- public FeatureConnector(Context context, int phoneId, Listener<T> listener,
- Executor executor, String logPrefix) {
- mContext = context;
- mPhoneId = phoneId;
- mListener= listener;
- mExecutor = executor;
- mLogPrefix = logPrefix;
- }
-
- @VisibleForTesting
- public FeatureConnector(Context context, int phoneId, Listener<T> listener,
- Executor executor, Looper looper) {
- super(looper);
- mContext = context;
- mPhoneId = phoneId;
- mListener= listener;
- mExecutor = executor;
- mLogPrefix = "?";
- }
-
- /**
- * Start the creation of a connection to the underlying ImsService implementation. When the
- * service is connected, {@link FeatureConnector.Listener#connectionReady(Object)} will be
- * called with an active instance.
- *
- * If this device does not support an ImsStack (i.e. doesn't support
- * {@link PackageManager#FEATURE_TELEPHONY_IMS} feature), this method will do nothing.
- */
- public void connect() {
- Log.i(TAG, getLogMessage("connect"));
- if (!isSupported()) {
- Log.i(TAG, getLogMessage("connect: not supported."));
- return;
- }
- mRetryCount = 0;
- // Send a message to connect to the Ims Service and open a connection through
- // getImsService().
- post(mGetServiceRunnable);
- }
-
- // Check if this ImsFeature is supported or not.
- private boolean isSupported() {
- return mListener.isSupported();
- }
-
- /**
- * Disconnect from the ImsService Implementation and clean up. When this is complete,
- * {@link FeatureConnector.Listener#connectionUnavailable()} will be called one last time.
- */
- public void disconnect() {
- Log.i(TAG, getLogMessage("disconnect"));
- removeCallbacks(mGetServiceRunnable);
- synchronized (mLock) {
- if (mManager != null) {
- mManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
- }
- }
- notifyNotReady();
- }
-
- private final Runnable mGetServiceRunnable = () -> {
- try {
- createImsService();
- } catch (ImsException e) {
- retryGetImsService();
- }
- };
-
- @VisibleForTesting
- public void createImsService() throws ImsException {
- synchronized (mLock) {
- Log.d(TAG, getLogMessage("createImsService"));
- mManager = mListener.getFeatureManager();
- // Adding to set, will be safe adding multiple times. If the ImsService is not
- // active yet, this method will throw an ImsException.
- mManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
- }
- // Wait for ImsService.STATE_READY to start listening for calls.
- // Call the callback right away for compatibility with older devices that do not use
- // states.
- mNotifyStatusChangedCallback.notifyStateChanged();
- }
-
- /**
- * Remove callback and re-running mGetServiceRunnable
- */
- public void retryGetImsService() {
- if (mManager != null) {
- // remove callback so we do not receive updates from old ImsServiceProxy when
- // switching between ImsServices.
- mManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
- //Leave mImsManager as null, then CallStateException will be thrown when dialing
- mManager = null;
- }
-
- // Exponential backoff during retry, limited to 32 seconds.
- removeCallbacks(mGetServiceRunnable);
- int timeout = mRetryTimeout.get();
- postDelayed(mGetServiceRunnable, timeout);
- Log.i(TAG, getLogMessage("retryGetImsService: unavailable, retrying in " + timeout
- + " seconds"));
- }
-
- // Callback fires when IMS Feature changes state
- public FeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
- new FeatureConnection.IFeatureUpdate() {
- @Override
- public void notifyStateChanged() {
- mExecutor.execute(() -> {
- try {
- int status = ImsFeature.STATE_UNAVAILABLE;
- synchronized (mLock) {
- if (mManager != null) {
- status = mManager.getImsServiceState();
- }
- }
- switch (status) {
- case ImsFeature.STATE_READY: {
- notifyReady();
- break;
- }
- case ImsFeature.STATE_INITIALIZING:
- // fall through
- case ImsFeature.STATE_UNAVAILABLE: {
- notifyNotReady();
- break;
- }
- default: {
- Log.w(TAG, getLogMessage("Unexpected State!"));
- }
- }
- } catch (ImsException e) {
- // Could not get the ImsService, retry!
- notifyNotReady();
- retryGetImsService();
- }
- });
- }
-
- @Override
- public void notifyUnavailable() {
- mExecutor.execute(() -> {
- notifyNotReady();
- retryGetImsService();
- });
- }
- };
-
- private void notifyReady() throws ImsException {
- T manager;
- synchronized (mLock) {
- manager = mManager;
- }
- try {
- Log.i(TAG, getLogMessage("notifyReady"));
- mListener.connectionReady(manager);
- }
- catch (ImsException e) {
- Log.w(TAG, getLogMessage("notifyReady exception: " + e.getMessage()));
- throw e;
- }
- // Only reset retry count if connectionReady does not generate an ImsException/
- synchronized (mLock) {
- mRetryCount = 0;
- }
- }
-
- protected void notifyNotReady() {
- Log.i(TAG, getLogMessage("notifyNotReady"));
- mListener.connectionUnavailable();
- }
-
- protected String getLogMessage(String message) {
- return "Connector-[" + mLogPrefix + ", " + mPhoneId + "] " + message;
- }
-}
diff --git a/src/java/com/android/ims/HandlerExecutor.java b/src/java/com/android/ims/HandlerExecutor.java
deleted file mode 100644
index 57971163..00000000
--- a/src/java/com/android/ims/HandlerExecutor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ims;
-
-import android.annotation.NonNull;
-
-import android.os.Handler;
-import com.android.internal.util.Preconditions;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
-
-/**
- * An adapter {@link Executor} that posts all executed tasks onto the given
- * {@link Handler}.
- *
- * @hide
- */
-public class HandlerExecutor implements Executor {
- private final Handler mHandler;
-
- public HandlerExecutor(@NonNull Handler handler) {
- mHandler = Preconditions.checkNotNull(handler);
- }
-
- @Override
- public void execute(Runnable command) {
- if (!mHandler.post(command)) {
- throw new RejectedExecutionException(mHandler + " is shutting down");
- }
- }
-} \ No newline at end of file
diff --git a/src/java/com/android/ims/IFeatureConnector.java b/src/java/com/android/ims/IFeatureConnector.java
deleted file mode 100644
index a169bb23..00000000
--- a/src/java/com/android/ims/IFeatureConnector.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ims;
-
-public interface IFeatureConnector {
- int getImsServiceState() throws ImsException;
- void addNotifyStatusChangedCallbackIfAvailable(FeatureConnection.IFeatureUpdate callback)
- throws ImsException;
- void removeNotifyStatusChangedCallback(FeatureConnection.IFeatureUpdate callback);
-} \ No newline at end of file
diff --git a/src/java/com/android/ims/ImsCall.java b/src/java/com/android/ims/ImsCall.java
index 83e710d7..2ce718d9 100644
--- a/src/java/com/android/ims/ImsCall.java
+++ b/src/java/com/android/ims/ImsCall.java
@@ -16,7 +16,12 @@
package com.android.ims;
-import android.annotation.UnsupportedAppUsage;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
@@ -25,11 +30,13 @@ import android.os.Parcel;
import android.telecom.Call;
import android.telecom.ConferenceParticipant;
import android.telecom.Connection;
-import android.telephony.CallQuality;
import android.telephony.Rlog;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import android.telephony.CallQuality;
import android.telephony.ServiceState;
import android.telephony.ims.ImsCallProfile;
-import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsConferenceState;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
@@ -37,17 +44,10 @@ import android.telephony.ims.ImsSuppServiceNotification;
import android.util.Log;
import com.android.ims.internal.ICall;
+import android.telephony.ims.ImsCallSession;
import com.android.ims.internal.ImsStreamMediaSession;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-
/**
* Handles an IMS voice / video call over LTE. You can instantiate this class with
* {@link ImsManager}.
@@ -937,7 +937,6 @@ public class ImsCall implements ICall {
*
* @return {@code True} if the call is a multiparty call.
*/
- @UnsupportedAppUsage
public boolean isMultiparty() {
synchronized(mLockObj) {
if (mSession == null) {
@@ -1201,7 +1200,6 @@ public class ImsCall implements ICall {
* @param number number to be deflected to.
* @throws ImsException if the IMS service fails to deflect the call
*/
- @UnsupportedAppUsage
public void deflect(String number) throws ImsException {
logi("deflect :: session=" + mSession + ", number=" + Rlog.pii(TAG, number));
@@ -1227,7 +1225,6 @@ public class ImsCall implements ICall {
* @see Listener#onCallStartFailed
* @throws ImsException if the IMS service fails to reject the call
*/
- @UnsupportedAppUsage
public void reject(int reason) throws ImsException {
logi("reject :: reason=" + reason);
@@ -1262,7 +1259,6 @@ public class ImsCall implements ICall {
*
* @param reason reason code to terminate a call
*/
- @UnsupportedAppUsage
public void terminate(int reason) {
logi("terminate :: reason=" + reason);
@@ -1756,14 +1752,6 @@ public class ImsCall implements ICall {
return mImsCallSessionListenerProxy;
}
- /**
- * @return the current Listener. NOTE: ONLY FOR USE WITH TESTING.
- */
- @VisibleForTesting
- public Listener getListener() {
- return mListener;
- }
-
private ImsCall createNewCall(ImsCallSession session, ImsCallProfile profile) {
ImsCall call = new ImsCall(mContext, profile);
@@ -2458,7 +2446,6 @@ public class ImsCall implements ICall {
return;
}
- mHold = true;
mUpdateRequest = UPDATE_NONE;
listener = mListener;
}
@@ -3500,11 +3487,10 @@ public class ImsCall implements ICall {
sb.append(isOnHold() ? "Y" : "N");
sb.append(" mute:");
sb.append(isMuted() ? "Y" : "N");
- ImsCallProfile imsCallProfile = mCallProfile;
- if (imsCallProfile != null) {
- sb.append(" mCallProfile:" + imsCallProfile);
+ if (mCallProfile != null) {
+ sb.append(" mCallProfile:" + mCallProfile);
sb.append(" tech:");
- sb.append(imsCallProfile.getCallExtra(ImsCallProfile.EXTRA_CALL_RAT_TYPE));
+ sb.append(mCallProfile.getCallExtra(ImsCallProfile.EXTRA_CALL_RAT_TYPE));
}
sb.append(" updateRequest:");
sb.append(updateRequestToString(mUpdateRequest));
diff --git a/src/java/com/android/ims/ImsConnectionStateListener.java b/src/java/com/android/ims/ImsConnectionStateListener.java
index 1594c805..6f6107cb 100644
--- a/src/java/com/android/ims/ImsConnectionStateListener.java
+++ b/src/java/com/android/ims/ImsConnectionStateListener.java
@@ -17,8 +17,9 @@
package com.android.ims;
import android.net.Uri;
-import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -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 RegistrationManager.RegistrationCallback} instead.
+ * @Deprecated Use {@link ImsMmTelManager.RegistrationCallback} instead.
* @hide
*/
-public class ImsConnectionStateListener extends RegistrationManager.RegistrationCallback {
+public class ImsConnectionStateListener extends ImsMmTelManager.RegistrationCallback {
@Override
public final void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
diff --git a/src/java/com/android/ims/ImsEcbm.java b/src/java/com/android/ims/ImsEcbm.java
index 0abfebe7..99d99f17 100644
--- a/src/java/com/android/ims/ImsEcbm.java
+++ b/src/java/com/android/ims/ImsEcbm.java
@@ -29,7 +29,6 @@
package com.android.ims;
-import android.annotation.UnsupportedAppUsage;
import android.os.RemoteException;
import android.telephony.Rlog;
import android.telephony.ims.ImsReasonInfo;
@@ -64,7 +63,6 @@ public class ImsEcbm {
}
}
- @UnsupportedAppUsage
public void exitEmergencyCallbackMode() throws ImsException {
try {
miEcbm.exitEmergencyCallbackMode();
diff --git a/src/java/com/android/ims/ImsManager.java b/src/java/com/android/ims/ImsManager.java
index 9ac3f076..7c24c7dc 100644
--- a/src/java/com/android/ims/ImsManager.java
+++ b/src/java/com/android/ims/ImsManager.java
@@ -17,12 +17,12 @@
package com.android.ims;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -32,34 +32,32 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telecom.TelecomManager;
-import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsCallProfile;
-import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsService;
import android.telephony.ims.ProvisioningManager;
-import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
-import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.stub.ImsCallSessionImplBase;
-import android.telephony.ims.stub.ImsConfigImplBase;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
+import android.telephony.ims.ImsCallSession;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.ITelephony;
@@ -74,7 +72,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
/**
* Provides APIs for IMS services, such as initiating IMS calls, and provides access to
@@ -83,7 +80,7 @@ import java.util.function.Consumer;
* For internal use ONLY! Use {@link ImsMmTelManager} instead.
* @hide
*/
-public class ImsManager implements IFeatureConnector {
+public class ImsManager {
/*
* Debug flag to override configuration flag
@@ -104,12 +101,9 @@ public class ImsManager implements IFeatureConnector {
public static final int INCOMING_CALL_RESULT_CODE = 101;
/**
- * Key to retrieve the call ID from an incoming call intent. No longer used, see
- * {@link ImsCallSessionImplBase#getCallId()}.
- * @deprecated Not used in the framework, keeping around symbol to not break old vendor
- * components.
+ * Key to retrieve the call ID from an incoming call intent.
+ * @see #open(MmTelFeature.Listener)
*/
- @Deprecated
public static final String EXTRA_CALL_ID = "android:imsCallID";
/**
@@ -159,9 +153,7 @@ public class ImsManager implements IFeatureConnector {
* An integer value; service identifier obtained from {@link ImsManager#open}.
* Internal use only.
* @hide
- * @deprecated Not used in the system, keeping around to not break old vendor components.
*/
- @Deprecated
public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
/**
@@ -169,8 +161,6 @@ public class ImsManager implements IFeatureConnector {
* An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
* The value "true" indicates that the incoming call is for USSD.
* Internal use only.
- * @deprecated Keeping around to not break old vendor components. Use
- * {@link MmTelFeature#EXTRA_USSD} instead.
* @hide
*/
public static final String EXTRA_USSD = "android:ussd";
@@ -183,8 +173,6 @@ public class ImsManager implements IFeatureConnector {
* Even though they are not incoming calls, they are propagated
* to Phone app using same ACTION_IMS_INCOMING_CALL intent.
* Internal use only.
- * @deprecated Keeping around to not break old vendor components. Use
- * {@link MmTelFeature#EXTRA_IS_UNKNOWN_CALL} instead.
* @hide
*/
public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
@@ -199,29 +187,217 @@ public class ImsManager implements IFeatureConnector {
private static final int RESPONSE_WAIT_TIME_MS = 3000;
- @VisibleForTesting
- public interface ExecutorFactory {
- void executeRunnable(Runnable runnable);
- }
+ /**
+ * Helper class for managing a connection to the ImsManager when the ImsService is unavailable
+ * or switches to another service.
+ */
+ public static class Connector extends Handler {
+ // Initial condition for ims connection retry.
+ private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
+ // Ceiling bitshift amount for service query timeout, calculated as:
+ // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
+ // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
+ private static final int CEILING_SERVICE_RETRY_COUNT = 6;
+
+ private final Runnable mGetServiceRunnable = () -> {
+ try {
+ getImsService();
+ } catch (ImsException e) {
+ retryGetImsService();
+ }
+ };
+
+ public interface Listener {
+ /**
+ * ImsManager is connected to the underlying IMS implementation.
+ */
+ void connectionReady(ImsManager manager) throws ImsException;
+
+ /**
+ * The underlying IMS implementation is unavailable and can not be used to communicate.
+ */
+ void connectionUnavailable();
+ }
+
+ @VisibleForTesting
+ public interface RetryTimeout {
+ int get();
+ }
+
+ // Callback fires when ImsManager MMTel Feature changes state
+ private MmTelFeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
+ new MmTelFeatureConnection.IFeatureUpdate() {
+ @Override
+ public void notifyStateChanged() {
+ mExecutor.execute(() -> {
+ try {
+ int status = ImsFeature.STATE_UNAVAILABLE;
+ synchronized (mLock) {
+ if (mImsManager != null) {
+ status = mImsManager.getImsServiceState();
+ }
+ }
+ switch (status) {
+ case ImsFeature.STATE_READY: {
+ notifyReady();
+ break;
+ }
+ case ImsFeature.STATE_INITIALIZING:
+ // fall through
+ case ImsFeature.STATE_UNAVAILABLE: {
+ notifyNotReady();
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unexpected State!");
+ }
+ }
+ } catch (ImsException e) {
+ // Could not get the ImsService, retry!
+ notifyNotReady();
+ retryGetImsService();
+ }
+ });
+ }
- private static class ImsExecutorFactory implements ExecutorFactory {
+ @Override
+ public void notifyUnavailable() {
+ mExecutor.execute(() -> {
+ notifyNotReady();
+ retryGetImsService();
+ });
+ }
+ };
+
+ private final Context mContext;
+ private final int mPhoneId;
+ private final Listener mListener;
+ private final Executor mExecutor;
+ private final Object mLock = new Object();
+
+ private int mRetryCount = 0;
+ private ImsManager mImsManager;
+
+ @VisibleForTesting
+ public RetryTimeout mRetryTimeout = () -> {
+ synchronized (mLock) {
+ int timeout = (1 << mRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
+ if (mRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
+ mRetryCount++;
+ }
+ return timeout;
+ }
+ };
+
+ public Connector(Context context, int phoneId, Listener listener) {
+ mContext = context;
+ mPhoneId = phoneId;
+ mListener = listener;
+ mExecutor = new HandlerExecutor(this);
+ }
+
+ @VisibleForTesting
+ public Connector(Context context, int phoneId, Listener listener, Executor executor) {
+ mContext = context;
+ mPhoneId = phoneId;
+ mListener= listener;
+ mExecutor = executor;
+ }
+
+
+ /**
+ * Start the creation of a connection to the underlying ImsService implementation. When the
+ * service is connected, {@link Listener#connectionReady(ImsManager)} will be called with
+ * an active ImsManager instance.
+ *
+ * If this device does not support an ImsStack (i.e. doesn't support
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS} feature), this method will do nothing.
+ */
+ public void connect() {
+ if (!ImsManager.isImsSupportedOnDevice(mContext)) {
+ return;
+ }
+ mRetryCount = 0;
+ // Send a message to connect to the Ims Service and open a connection through
+ // getImsService().
+ post(mGetServiceRunnable);
+ }
+
+ /**
+ * Disconnect from the ImsService Implementation and clean up. When this is complete,
+ * {@link Listener#connectionUnavailable()} will be called one last time.
+ */
+ public void disconnect() {
+ removeCallbacks(mGetServiceRunnable);
+ synchronized (mLock) {
+ if (mImsManager != null) {
+ mImsManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
+ }
+ }
+ notifyNotReady();
+ }
- private final HandlerThread mThreadHandler;
+ private void retryGetImsService() {
+ synchronized (mLock) {
+ if (mImsManager != null) {
+ // remove callback so we do not receive updates from old ImsServiceProxy when
+ // switching between ImsServices.
+ mImsManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
+ //Leave mImsManager as null, then CallStateException will be thrown when dialing
+ mImsManager = null;
+ }
- public ImsExecutorFactory() {
- mThreadHandler = new HandlerThread("ImsHandlerThread");
- mThreadHandler.start();
+ // Exponential backoff during retry, limited to 32 seconds.
+ removeCallbacks(mGetServiceRunnable);
+ postDelayed(mGetServiceRunnable, mRetryTimeout.get());
+ }
}
- @Override
- public void executeRunnable(Runnable runnable) {
- mThreadHandler.getThreadHandler().post(runnable);
+ private void getImsService() throws ImsException {
+ synchronized (mLock) {
+ mImsManager = ImsManager.getInstance(mContext, mPhoneId);
+ // Adding to set, will be safe adding multiple times. If the ImsService is not
+ // active yet, this method will throw an ImsException.
+ mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
+ }
+ // Wait for ImsService.STATE_READY to start listening for calls.
+ // Call the callback right away for compatibility with older devices that do not use
+ // states.
+ mNotifyStatusChangedCallback.notifyStateChanged();
+ }
+
+ private void notifyReady() throws ImsException {
+ ImsManager manager;
+ synchronized (mLock) {
+ manager = mImsManager;
+ }
+ try {
+ mListener.connectionReady(manager);
+ }
+ catch (ImsException e) {
+ Log.w(TAG, "Connector: notifyReady exception: " + e.getMessage());
+ throw e;
+ }
+ // Only reset retry count if connectionReady does not generate an ImsException/
+ synchronized (mLock) {
+ mRetryCount = 0;
+ }
+ }
+
+ private void notifyNotReady() {
+ mListener.connectionUnavailable();
}
}
+
+ @VisibleForTesting
+ public interface ExecutorFactory {
+ void executeRunnable(Runnable runnable);
+ }
+
// Replaced with single-threaded executor for testing.
@VisibleForTesting
- public ExecutorFactory mExecutorFactory = new ImsExecutorFactory();
+ public ExecutorFactory mExecutorFactory = runnable -> new Thread(runnable).start();
private static HashMap<Integer, ImsManager> sImsManagerInstances =
new HashMap<Integer, ImsManager>();
@@ -243,7 +419,8 @@ public class ImsManager implements IFeatureConnector {
private ImsEcbm mEcbm = null;
private ImsMultiEndpoint mMultiEndpoint = null;
- private Set<FeatureConnection.IFeatureUpdate> mStatusCallbacks = new CopyOnWriteArraySet<>();
+ private Set<MmTelFeatureConnection.IFeatureUpdate> mStatusCallbacks =
+ new CopyOnWriteArraySet<>();
public static final String TRUE = "true";
public static final String FALSE = "false";
@@ -260,7 +437,6 @@ public class ImsManager implements IFeatureConnector {
* @param phoneId the phone ID for the IMS Service
* @return the manager instance corresponding to the phoneId
*/
- @UnsupportedAppUsage
public static ImsManager getInstance(Context context, int phoneId) {
synchronized (sImsManagerInstances) {
if (sImsManagerInstances.containsKey(phoneId)) {
@@ -289,15 +465,13 @@ public class ImsManager implements IFeatureConnector {
* @deprecated Doesn't support MSIM devices. Use
* {@link #isEnhanced4gLteModeSettingEnabledByUser()} instead.
*/
- @UnsupportedAppUsage
public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
ImsManager mgr = ImsManager.getInstance(context,
SubscriptionManager.getDefaultVoicePhoneId());
if (mgr != null) {
return mgr.isEnhanced4gLteModeSettingEnabledByUser();
}
- Rlog.e(TAG, "isEnhanced4gLteModeSettingEnabledByUser: ImsManager null, returning default"
- + " value.");
+ loge("isEnhanced4gLteModeSettingEnabledByUser: ImsManager null, returning default value.");
return false;
}
@@ -341,7 +515,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.setEnhanced4gLteModeSetting(enabled);
}
- Rlog.e(TAG, "setEnhanced4gLteModeSetting: ImsManager null, value not set.");
+ loge("setEnhanced4gLteModeSetting: ImsManager null, value not set.");
}
/**
@@ -395,14 +569,13 @@ public class ImsManager implements IFeatureConnector {
* @deprecated Does not support MSIM devices. Please use
* {@link #isNonTtyOrTtyOnVolteEnabled()} instead.
*/
- @UnsupportedAppUsage
public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
ImsManager mgr = ImsManager.getInstance(context,
SubscriptionManager.getDefaultVoicePhoneId());
if (mgr != null) {
return mgr.isNonTtyOrTtyOnVolteEnabled();
}
- Rlog.e(TAG, "isNonTtyOrTtyOnVolteEnabled: ImsManager null, returning default value.");
+ loge("isNonTtyOrTtyOnVolteEnabled: ImsManager null, returning default value.");
return false;
}
@@ -417,7 +590,7 @@ public class ImsManager implements IFeatureConnector {
TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
if (tm == null) {
- logw("isNonTtyOrTtyOnVolteEnabled: telecom not available");
+ Log.w(TAG, "isNonTtyOrTtyOnVolteEnabled: telecom not available");
return true;
}
return tm.getCurrentTtyMode() == TelecomManager.TTY_MODE_OFF;
@@ -432,71 +605,17 @@ public class ImsManager implements IFeatureConnector {
* @deprecated Does not support MSIM devices. Please use
* {@link #isVolteEnabledByPlatform()} instead.
*/
- @UnsupportedAppUsage
public static boolean isVolteEnabledByPlatform(Context context) {
ImsManager mgr = ImsManager.getInstance(context,
SubscriptionManager.getDefaultVoicePhoneId());
if (mgr != null) {
return mgr.isVolteEnabledByPlatform();
}
- Rlog.e(TAG, "isVolteEnabledByPlatform: ImsManager null, returning default value.");
+ loge("isVolteEnabledByPlatform: ImsManager null, returning default value.");
return false;
}
/**
- * Asynchronous call to ImsService to determine whether or not a specific MmTel capability is
- * supported.
- */
- public void isSupported(int capability, int transportType, Consumer<Boolean> result) {
- mExecutorFactory.executeRunnable(() -> {
- switch(transportType) {
- case (AccessNetworkConstants.TRANSPORT_TYPE_WWAN): {
- switch (capability) {
- case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE): {
- result.accept(isVolteEnabledByPlatform());
- return;
- } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO): {
- result.accept(isVtEnabledByPlatform());
- return;
- }case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT): {
- result.accept(isSuppServicesOverUtEnabledByPlatform());
- return;
- } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS): {
- // There is currently no carrier config defined for this.
- result.accept(true);
- return;
- }
- }
- break;
- } case (AccessNetworkConstants.TRANSPORT_TYPE_WLAN): {
- switch (capability) {
- case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE) : {
- result.accept(isWfcEnabledByPlatform());
- return;
- } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO) : {
- // This is not transport dependent at this time.
- result.accept(isVtEnabledByPlatform());
- return;
- } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT) : {
- // This is not transport dependent at this time.
- result.accept(isSuppServicesOverUtEnabledByPlatform());
- return;
- } case (MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS) : {
- // There is currently no carrier config defined for this.
- result.accept(true);
- return;
- }
- }
- break;
- }
- }
- // false for unknown capability/transport types.
- result.accept(false);
- });
-
- }
-
- /**
* Returns a platform configuration for VoLTE which may override the user setting on a per Slot
* basis.
*/
@@ -529,7 +648,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isVolteProvisionedOnDevice();
}
- Rlog.e(TAG, "isVolteProvisionedOnDevice: ImsManager null, returning default value.");
+ loge("isVolteProvisionedOnDevice: ImsManager null, returning default value.");
return true;
}
@@ -560,7 +679,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isWfcProvisionedOnDevice();
}
- Rlog.e(TAG, "isWfcProvisionedOnDevice: ImsManager null, returning default value.");
+ loge("isWfcProvisionedOnDevice: ImsManager null, returning default value.");
return true;
}
@@ -598,7 +717,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isVtProvisionedOnDevice();
}
- Rlog.e(TAG, "isVtProvisionedOnDevice: ImsManager null, returning default value.");
+ loge("isVtProvisionedOnDevice: ImsManager null, returning default value.");
return true;
}
@@ -629,7 +748,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isVtEnabledByPlatform();
}
- Rlog.e(TAG, "isVtEnabledByPlatform: ImsManager null, returning default value.");
+ loge("isVtEnabledByPlatform: ImsManager null, returning default value.");
return false;
}
@@ -666,7 +785,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isVtEnabledByUser();
}
- Rlog.e(TAG, "isVtEnabledByUser: ImsManager null, returning default value.");
+ loge("isVtEnabledByUser: ImsManager null, returning default value.");
return false;
}
@@ -695,7 +814,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.setVtSetting(enabled);
}
- Rlog.e(TAG, "setVtSetting: ImsManager null, can not set value.");
+ loge("setVtSetting: ImsManager null, can not set value.");
}
/**
@@ -750,7 +869,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isTurnOffImsAllowedByPlatform();
}
- Rlog.e(TAG, "isTurnOffImsAllowedByPlatform: ImsManager null, returning default value.");
+ loge("isTurnOffImsAllowedByPlatform: ImsManager null, returning default value.");
return true;
}
@@ -784,7 +903,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isWfcEnabledByUser();
}
- Rlog.e(TAG, "isWfcEnabledByUser: ImsManager null, returning default value.");
+ loge("isWfcEnabledByUser: ImsManager null, returning default value.");
return true;
}
@@ -817,7 +936,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.setWfcSetting(enabled);
}
- Rlog.e(TAG, "setWfcSetting: ImsManager null, can not set value.");
+ loge("setWfcSetting: ImsManager null, can not set value.");
}
/**
@@ -892,7 +1011,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.getWfcMode();
}
- Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value.");
+ loge("getWfcMode: ImsManager null, returning default value.");
return ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
}
@@ -915,7 +1034,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.setWfcMode(wfcMode);
}
- Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value.");
+ loge("setWfcMode: ImsManager null, can not set value.");
}
/**
@@ -939,7 +1058,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.getWfcMode(roaming);
}
- Rlog.e(TAG, "getWfcMode: ImsManager null, returning default value.");
+ loge("getWfcMode: ImsManager null, returning default value.");
return ImsMmTelManager.WIFI_MODE_WIFI_ONLY;
}
@@ -1011,7 +1130,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.setWfcMode(wfcMode, roaming);
}
- Rlog.e(TAG, "setWfcMode: ImsManager null, can not set value.");
+ loge("setWfcMode: ImsManager null, can not set value.");
}
/**
@@ -1079,7 +1198,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isWfcRoamingEnabledByUser();
}
- Rlog.e(TAG, "isWfcRoamingEnabledByUser: ImsManager null, returning default value.");
+ loge("isWfcRoamingEnabledByUser: ImsManager null, returning default value.");
return false;
}
@@ -1108,7 +1227,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.setWfcRoamingSetting(enabled);
}
- Rlog.e(TAG, "setWfcRoamingSetting: ImsManager null, value not set.");
+ loge("setWfcRoamingSetting: ImsManager null, value not set.");
}
/**
@@ -1150,7 +1269,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
return mgr.isWfcEnabledByPlatform();
}
- Rlog.e(TAG, "isWfcEnabledByPlatform: ImsManager null, returning default value.");
+ loge("isWfcEnabledByPlatform: ImsManager null, returning default value.");
return false;
}
@@ -1245,7 +1364,7 @@ public class ImsManager implements IFeatureConnector {
ImsConfig config = getConfigInterface();
return getProvisionedBool(config, item);
} catch (ImsException ex) {
- logw("getProvisionedBoolNoException: operation failed for item=" + item
+ Log.w(TAG, "getProvisionedBoolNoException: operation failed for item=" + item
+ ". Exception:" + ex.getMessage() + ". Returning false.");
return false;
}
@@ -1260,7 +1379,7 @@ public class ImsManager implements IFeatureConnector {
ImsConfig config = getConfigInterface();
setProvisionedBool(config, item, value);
} catch (ImsException ex) {
- logw("setProvisionedBoolNoException: operation failed for item=" + item
+ Log.w(TAG, "setProvisionedBoolNoException: operation failed for item=" + item
+ ", value=" + value + ". Exception:" + ex.getMessage());
return false;
}
@@ -1282,7 +1401,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.updateImsServiceConfig(force);
}
- Rlog.e(TAG, "updateImsServiceConfig: ImsManager null, returning without update.");
+ loge("updateImsServiceConfig: ImsManager null, returning without update.");
}
/**
@@ -1302,12 +1421,6 @@ public class ImsManager implements IFeatureConnector {
if (!mConfigUpdated || force) {
try {
- PersistableBundle imsCarrierConfigs =
- mConfigManager.getConfigByComponentForSubId(
- CarrierConfigManager.Ims.KEY_PREFIX, getSubId());
-
- updateImsCarrierConfigs(imsCarrierConfigs);
-
// Note: currently the order of updates is set to produce different order of
// changeEnabledCapabilities() function calls from setAdvanced4GMode(). This is done
// to differentiate this code path from vendor code perspective.
@@ -1459,7 +1572,7 @@ public class ImsManager implements IFeatureConnector {
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
}
} catch (RemoteException e) {
- loge("updateUtFeatureValue: couldn't reach telephony! returning provisioned");
+ Log.e(TAG, "updateUtFeatureValue: couldn't reach telephony! returning provisioned");
}
}
boolean isFeatureOn = isCarrierSupported && isProvisioned;
@@ -1537,9 +1650,8 @@ public class ImsManager implements IFeatureConnector {
* Adds a callback for status changed events if the binder is already available. If it is not,
* this method will throw an ImsException.
*/
- @Override
@VisibleForTesting
- public void addNotifyStatusChangedCallbackIfAvailable(FeatureConnection.IFeatureUpdate c)
+ public void addNotifyStatusChangedCallbackIfAvailable(MmTelFeatureConnection.IFeatureUpdate c)
throws ImsException {
if (!mMmTelFeatureConnection.isBinderAlive()) {
throw new ImsException("Binder is not active!",
@@ -1550,12 +1662,11 @@ public class ImsManager implements IFeatureConnector {
}
}
- @Override
- public void removeNotifyStatusChangedCallback(FeatureConnection.IFeatureUpdate c) {
+ void removeNotifyStatusChangedCallback(MmTelFeatureConnection.IFeatureUpdate c) {
if (c != null) {
mStatusCallbacks.remove(c);
} else {
- logw("removeNotifyStatusChangedCallback: callback is null!");
+ Log.w(TAG, "removeNotifyStatusChangedCallback: callback is null!");
}
}
@@ -1566,10 +1677,12 @@ public class ImsManager implements IFeatureConnector {
* (from ISIM) periodically in order to receive calls from the operator's network.
* When the IMS service receives a new call, it will call
* {@link MmTelFeature.Listener#onIncomingCall}
+ * The listener contains a call ID extra {@link #getCallId} and it can be used to take a call.
* @param listener A {@link MmTelFeature.Listener}, which is the interface the
* {@link MmTelFeature} uses to notify the framework of updates
* @throws NullPointerException if {@code listener} is null
* @throws ImsException if calling the IMS service results in an error
+ * @see #getCallId
*/
public void open(MmTelFeature.Listener listener) throws ImsException {
checkAndThrowExceptionIfServiceUnavailable();
@@ -1607,7 +1720,7 @@ public class ImsManager implements IFeatureConnector {
* @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(RegistrationManager.RegistrationCallback)}
+ * @deprecated use {@link #addRegistrationCallback(ImsMmTelManager.RegistrationCallback)}
* instead.
*/
public void addRegistrationListener(ImsConnectionStateListener listener) throws ImsException {
@@ -1629,11 +1742,11 @@ public class ImsManager implements IFeatureConnector {
/**
* Adds a callback that gets called when IMS registration has changed for the slot ID
* associated with this ImsManager.
- * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the
- * caller when IMS registration status has changed.
+ * @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(RegistrationManager.RegistrationCallback callback)
+ public void addRegistrationCallback(ImsMmTelManager.RegistrationCallback callback)
throws ImsException {
if (callback == null) {
throw new NullPointerException("registration callback can't be null");
@@ -1652,10 +1765,10 @@ public class ImsManager implements IFeatureConnector {
/**
* Removes a previously added registration callback that was added via
- * {@link #addRegistrationCallback(RegistrationManager.RegistrationCallback)} .
- * @param callback A {@link RegistrationManager.RegistrationCallback} that was previously added.
+ * {@link #addRegistrationCallback(ImsMmTelManager.RegistrationCallback)} .
+ * @param callback A {@link ImsMmTelManager.RegistrationCallback} that was previously added.
*/
- public void removeRegistrationListener(RegistrationManager.RegistrationCallback callback) {
+ public void removeRegistrationListener(ImsMmTelManager.RegistrationCallback callback) {
if (callback == null) {
throw new NullPointerException("registration callback can't be null");
}
@@ -1668,8 +1781,8 @@ public class ImsManager implements IFeatureConnector {
* Adds a callback that gets called when IMS registration has changed for a specific
* subscription.
*
- * @param callback A {@link RegistrationManager.RegistrationCallback} that will notify the
- * caller when IMS registration status has changed.
+ * @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.
*/
@@ -1684,8 +1797,8 @@ public class ImsManager implements IFeatureConnector {
}
/**
- * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback
- * that is associated with a specific subscription.
+ * Removes a previously registered {@link ImsMmTelManager.RegistrationCallback} callback that is
+ * associated with a specific subscription.
*/
public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback,
int subId) {
@@ -1821,23 +1934,11 @@ public class ImsManager implements IFeatureConnector {
try {
return mMmTelFeatureConnection.getRegistrationTech();
} catch (RemoteException e) {
- logw("getRegistrationTech: no connection to ImsService.");
+ Log.w(TAG, "getRegistrationTech: no connection to ImsService.");
return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
}
}
- public void getRegistrationTech(Consumer<Integer> callback) {
- mExecutorFactory.executeRunnable(() -> {
- try {
- int tech = mMmTelFeatureConnection.getRegistrationTech();
- callback.accept(tech);
- } catch (RemoteException e) {
- logw("getRegistrationTech(C): no connection to ImsService.");
- callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
- }
- });
- }
-
/**
* Closes the connection and removes all active callbacks.
* All the resources that were allocated to the service are also released.
@@ -1945,13 +2046,32 @@ public class ImsManager implements IFeatureConnector {
/**
* Creates a {@link ImsCall} to take an incoming call.
*
+ * @param sessionId a session id which is obtained from {@link ImsManager#open}
+ * @param incomingCallExtras the incoming call broadcast intent
* @param listener to listen to the call events from {@link ImsCall}
* @return a {@link ImsCall} object
* @throws ImsException if calling the IMS service results in an error
*/
- public ImsCall takeCall(IImsCallSession session, ImsCall.Listener listener)
- throws ImsException {
+ public ImsCall takeCall(IImsCallSession session, Bundle incomingCallExtras,
+ ImsCall.Listener listener) throws ImsException {
+ if (DBG) {
+ log("takeCall :: incomingCall=" + incomingCallExtras);
+ }
+
checkAndThrowExceptionIfServiceUnavailable();
+
+ if (incomingCallExtras == null) {
+ throw new ImsException("Can't retrieve session with null intent",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
+ }
+
+ String callId = getCallId(incomingCallExtras);
+
+ if (callId == null) {
+ throw new ImsException("Call ID missing in the incoming call intent",
+ ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
+ }
+
try {
if (session == null) {
throw new ImsException("No pending session for the call",
@@ -1975,7 +2095,6 @@ public class ImsManager implements IFeatureConnector {
* @return the ImsConfig instance.
* @throws ImsException if getting the setting interface results in an error.
*/
- @UnsupportedAppUsage
public ImsConfig getConfigInterface() throws ImsException {
checkAndThrowExceptionIfServiceUnavailable();
@@ -2004,7 +2123,7 @@ public class ImsManager implements IFeatureConnector {
public void changeMmTelCapability(CapabilityChangeRequest r) throws ImsException {
checkAndThrowExceptionIfServiceUnavailable();
try {
- logi("changeMmTelCapability: changing capabilities for sub: " + getSubId()
+ Log.i(TAG, "changeMmTelCapability: changing capabilities for sub: " + getSubId()
+ ", request: " + r);
mMmTelFeatureConnection.changeEnabledCapabilities(r, null);
if (mImsConfigListener == null) {
@@ -2036,7 +2155,8 @@ public class ImsManager implements IFeatureConnector {
CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
boolean shouldImsRttBeOn = isRttUiSettingEnabled || isRttAlwaysOnCarrierConfig;
- logi("update RTT: settings value: " + isRttUiSettingEnabled + " always-on carrierconfig: "
+ Log.i(ImsManager.class.getSimpleName(), "update RTT: settings value: "
+ + isRttUiSettingEnabled + " always-on carrierconfig: "
+ isRttAlwaysOnCarrierConfig);
if (isCarrierSupported) {
@@ -2050,11 +2170,12 @@ public class ImsManager implements IFeatureConnector {
ProvisioningManager.PROVISIONING_VALUE_DISABLED;
mExecutorFactory.executeRunnable(() -> {
try {
- logi("Setting RTT enabled to " + enabled);
+ Log.i(ImsManager.class.getSimpleName(), "Setting RTT enabled to " + enabled);
getConfigInterface().setProvisionedValue(
ImsConfig.ConfigConstants.RTT_SETTING_ENABLED, value);
} catch (ImsException e) {
- loge("Unable to set RTT value enabled to " + enabled + ": " + e);
+ Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT value enabled to "
+ + enabled + ": " + e);
}
});
}
@@ -2096,7 +2217,7 @@ public class ImsManager implements IFeatureConnector {
try {
return result.poll(RESPONSE_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
- logw("queryMmTelCapability: interrupted while waiting for response");
+ Log.w(TAG, "queryMmTelCapability: interrupted while waiting for response");
}
return false;
}
@@ -2110,7 +2231,8 @@ public class ImsManager implements IFeatureConnector {
}
setRttConfig(enabled);
} catch (ImsException e) {
- loge("Unable to set RTT enabled to " + enabled + ": " + e);
+ Log.e(ImsManager.class.getSimpleName(), "Unable to set RTT enabled to " + enabled
+ + ": " + e);
}
}
@@ -2176,22 +2298,10 @@ public class ImsManager implements IFeatureConnector {
return disconnectReasons;
}
- @Override
public int getImsServiceState() throws ImsException {
return mMmTelFeatureConnection.getFeatureState();
}
- public void getImsServiceState(Consumer<Integer> result) {
- mExecutorFactory.executeRunnable(() -> {
- try {
- result.accept(getImsServiceState());
- } catch (ImsException e) {
- // In the case that the ImsService is not available, report unavailable.
- result.accept(ImsFeature.STATE_UNAVAILABLE);
- }
- });
- }
-
private Executor getThreadExecutor() {
if (Looper.myLooper() == null) {
Looper.prepare();
@@ -2240,6 +2350,20 @@ public class ImsManager implements IFeatureConnector {
}
/**
+ * Gets the call ID from the specified incoming call broadcast intent.
+ *
+ * @param incomingCallExtras the incoming call broadcast intent
+ * @return the call ID or null if the intent does not contain it
+ */
+ private static String getCallId(Bundle incomingCallExtras) {
+ if (incomingCallExtras == null) {
+ return null;
+ }
+
+ return incomingCallExtras.getString(EXTRA_CALL_ID);
+ }
+
+ /**
* Checks to see if the ImsService Binder is connected. If it is not, we try to create the
* connection again.
*/
@@ -2269,15 +2393,15 @@ public class ImsManager implements IFeatureConnector {
mMmTelFeatureConnection = MmTelFeatureConnection.create(mContext, mPhoneId);
// Forwarding interface to tell mStatusCallbacks that the Proxy is unavailable.
- mMmTelFeatureConnection.setStatusCallback(new FeatureConnection.IFeatureUpdate() {
+ mMmTelFeatureConnection.setStatusCallback(new MmTelFeatureConnection.IFeatureUpdate() {
@Override
public void notifyStateChanged() {
- mStatusCallbacks.forEach(FeatureConnection.IFeatureUpdate::notifyStateChanged);
+ mStatusCallbacks.forEach(MmTelFeatureConnection.IFeatureUpdate::notifyStateChanged);
}
@Override
public void notifyUnavailable() {
- mStatusCallbacks.forEach(FeatureConnection.IFeatureUpdate::notifyUnavailable);
+ mStatusCallbacks.forEach(MmTelFeatureConnection.IFeatureUpdate::notifyUnavailable);
}
});
}
@@ -2294,31 +2418,23 @@ public class ImsManager implements IFeatureConnector {
// Throws an exception if the ImsService Feature is not ready to accept commands.
return new ImsCallSession(mMmTelFeatureConnection.createCallSession(profile));
} catch (RemoteException e) {
- logw("CreateCallSession: Error, remote exception: " + e.getMessage());
+ Rlog.w(TAG, "CreateCallSession: Error, remote exception: " + e.getMessage());
throw new ImsException("createCallSession()", e,
ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
}
}
- private void log(String s) {
- Rlog.d(TAG + " [" + mPhoneId + "]", s);
- }
-
- private void logi(String s) {
- Rlog.i(TAG + " [" + mPhoneId + "]", s);
- }
-
- private void logw(String s) {
- Rlog.w(TAG + " [" + mPhoneId + "]", s);
+ private static void log(String s) {
+ Rlog.d(TAG, s);
}
- private void loge(String s) {
- Rlog.e(TAG + " [" + mPhoneId + "]", s);
+ private static void loge(String s) {
+ Rlog.e(TAG, s);
}
- private void loge(String s, Throwable t) {
- Rlog.e(TAG + " [" + mPhoneId + "]", s, t);
+ private static void loge(String s, Throwable t) {
+ Rlog.e(TAG, s, t);
}
/**
@@ -2367,7 +2483,7 @@ public class ImsManager implements IFeatureConnector {
try {
mMmTelFeatureConnection.changeEnabledCapabilities(request, null);
} catch (RemoteException e) {
- loge("setLteFeatureValues: Exception: " + e.getMessage());
+ Log.e(TAG, "setLteFeatureValues: Exception: " + e.getMessage());
}
}
@@ -2552,7 +2668,7 @@ public class ImsManager implements IFeatureConnector {
if (mgr != null) {
mgr.factoryReset();
}
- Rlog.e(TAG, "factoryReset: ImsManager null.");
+ loge("factoryReset: ImsManager null.");
}
/**
@@ -2566,33 +2682,31 @@ public class ImsManager implements IFeatureConnector {
// Set VoLTE to default
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
- Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
+ booleanToPropertyString(getBooleanCarrierConfig(
+ CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL)));
// Set VoWiFi to default
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.WFC_IMS_ENABLED,
- Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
+ booleanToPropertyString(getBooleanCarrierConfig(
+ CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL)));
// Set VoWiFi mode to default
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.WFC_IMS_MODE,
- Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
+ Integer.toString(getIntCarrierConfig(
+ CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT)));
// Set VoWiFi roaming to default
SubscriptionManager.setSubscriptionProperty(subId,
SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
- Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
-
- // Set VoWiFi roaming mode to default
- SubscriptionManager.setSubscriptionProperty(subId,
- SubscriptionManager.WFC_IMS_ROAMING_MODE,
- Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
+ booleanToPropertyString(getBooleanCarrierConfig(
+ CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL)));
// Set VT to default
SubscriptionManager.setSubscriptionProperty(subId,
- SubscriptionManager.VT_IMS_ENABLED,
- Integer.toString(SUB_PROPERTY_NOT_INITIALIZED));
+ SubscriptionManager.VT_IMS_ENABLED, booleanToPropertyString(true));
} else {
loge("factoryReset: invalid sub id, can not reset siminfo db settings; subId=" + subId);
}
@@ -2687,20 +2801,4 @@ public class ImsManager implements IFeatureConnector {
return SubscriptionManager.isValidSubscriptionId(subId) &&
subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
}
-
- private void updateImsCarrierConfigs(PersistableBundle configs) throws ImsException {
- checkAndThrowExceptionIfServiceUnavailable();
-
- IImsConfig config = mMmTelFeatureConnection.getConfigInterface();
- if (config == null) {
- throw new ImsException("getConfigInterface()",
- ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
- }
- try {
- config.updateImsCarrierConfigs(configs);
- } catch (RemoteException e) {
- throw new ImsException("updateImsCarrierConfigs()", e,
- ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
- }
- }
}
diff --git a/src/java/com/android/ims/ImsUt.java b/src/java/com/android/ims/ImsUt.java
index 6634a705..b3d4c8af 100644
--- a/src/java/com/android/ims/ImsUt.java
+++ b/src/java/com/android/ims/ImsUt.java
@@ -31,11 +31,9 @@ import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsSsData;
import android.telephony.ims.ImsSsInfo;
-import android.telephony.ims.ImsUtListener;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
-import com.android.internal.annotations.VisibleForTesting;
/**
* Provides APIs for the supplementary service settings using IMS (Ut interface).
@@ -647,8 +645,7 @@ public class ImsUt implements ImsUtInterface {
/**
* A listener type for the result of the supplementary service configuration.
*/
- @VisibleForTesting
- public class IImsUtListenerProxy extends IImsUtListener.Stub {
+ private class IImsUtListenerProxy extends IImsUtListener.Stub {
/**
* Notifies the result of the supplementary service configuration udpate.
*/
@@ -675,34 +672,13 @@ public class ImsUt implements ImsUtInterface {
/**
* Notifies the result of the supplementary service configuration query.
*/
- // API Deprecated, internally use new API to process query result.
@Override
public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) {
- int[] clirResponse = ssInfo.getIntArray(ImsUtListener.BUNDLE_KEY_CLIR);
- if (clirResponse != null && clirResponse.length == 2) {
- // Deprecated functionality does not use status, set as NOT_REGISTERED.
- ImsSsInfo info = new ImsSsInfo.Builder(ImsSsInfo.NOT_REGISTERED)
- .setClirOutgoingState(clirResponse[0])
- .setClirInterrogationStatus(clirResponse[1]).build();
- lineIdentificationSupplementaryServiceResponse(id, info);
- return;
- }
- ImsSsInfo info = ssInfo.getParcelable(ImsUtListener.BUNDLE_KEY_SSINFO);
- if (info != null) {
- lineIdentificationSupplementaryServiceResponse(id, info);
- return;
- }
- Rlog.w(TAG, "Invalid utConfigurationQueried response received for Bundle " + ssInfo);
- }
+ Integer key = Integer.valueOf(id);
- /**
- * Notifies the result of a line identification supplementary service query.
- */
- @Override
- public void lineIdentificationSupplementaryServiceResponse(int id, ImsSsInfo config) {
synchronized(mLockObj) {
- sendSuccessReport(mPendingCmds.get(id), config);
- mPendingCmds.remove(id);
+ sendSuccessReport(mPendingCmds.get(key), ssInfo);
+ mPendingCmds.remove(key);
}
}
diff --git a/src/java/com/android/ims/MmTelFeatureConnection.java b/src/java/com/android/ims/MmTelFeatureConnection.java
index 48f160d5..96c7a4f7 100644
--- a/src/java/com/android/ims/MmTelFeatureConnection.java
+++ b/src/java/com/android/ims/MmTelFeatureConnection.java
@@ -19,6 +19,8 @@ package com.android.ims;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
@@ -57,14 +59,16 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.stream.Collectors;
/**
* A container of the IImsServiceController binder, which implements all of the ImsFeatures that
- * the platform currently supports: MMTel
+ * the platform currently supports: MMTel and RCS.
+ * @hide
*/
-public class MmTelFeatureConnection extends FeatureConnection {
+public class MmTelFeatureConnection {
protected static final String TAG = "MmTelFeatureConnection";
// Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection.
@@ -74,7 +78,6 @@ public class MmTelFeatureConnection extends FeatureConnection {
private final Context mContext;
private final Object mLock;
- private final int mSlotId;
// 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
@@ -82,10 +85,9 @@ public class MmTelFeatureConnection extends FeatureConnection {
@VisibleForTesting
public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
- public CallbackAdapterManager(Context context, Object lock, int slotId) {
+ public CallbackAdapterManager(Context context, Object lock) {
mContext = context;
mLock = lock;
- mSlotId = slotId;
if (Looper.myLooper() == null) {
Looper.prepare();
}
@@ -96,8 +98,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
SubscriptionManager manager = mContext.getSystemService(
SubscriptionManager.class);
if (manager == null) {
- Log.w(TAG + " [" + mSlotId + "]", "onSubscriptionsChanged: could not find "
- + "SubscriptionManager.");
+ Log.w(TAG, "onSubscriptionsChanged: could not find SubscriptionManager.");
return;
}
List<SubscriptionInfo> subInfos = manager.getActiveSubscriptionInfoList(false);
@@ -133,7 +134,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
// for the slot, independent of subscription (deprecated behavior).
// Throws a IllegalStateException if this registration fails.
registerCallback(localCallback);
- Log.i(TAG + " [" + mSlotId + "]", "Local callback added: " + localCallback);
+ Log.i(TAG, "Local callback added: " + localCallback);
mRemoteCallbacks.register(localCallback);
}
}
@@ -152,7 +153,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
// Removes a callback associated with the MmTelFeature.
public final void removeCallback(T localCallback) {
- Log.i(TAG + " [" + mSlotId + "]", "Local callback removed: " + localCallback);
+ Log.i(TAG, "Local callback removed: " + localCallback);
synchronized (mLock) {
if (mRemoteCallbacks.unregister(localCallback)) {
// Will only occur if we have record of this callback in mRemoteCallbacks.
@@ -248,8 +249,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
if (manager != null) {
manager.addOnSubscriptionsChangedListener(mSubChangedListener);
} else {
- Log.w(TAG + " [" + mSlotId + "]", "registerForSubscriptionsChanged: could not find"
- + " SubscriptionManager.");
+ Log.w(TAG, "registerForSubscriptionsChanged: could not find SubscriptionManager.");
}
}
@@ -258,8 +258,8 @@ public class MmTelFeatureConnection extends FeatureConnection {
if (manager != null) {
manager.removeOnSubscriptionsChangedListener(mSubChangedListener);
} else {
- Log.w(TAG + " [" + mSlotId + "]", "unregisterForSubscriptionsChanged: could not"
- + " find SubscriptionManager.");
+ Log.w(TAG, "unregisterForSubscriptionsChanged: could not find"
+ + " SubscriptionManager.");
}
}
@@ -275,7 +275,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
mRemoteCallbacks.unregister(callbackItem);
}
clearCallbacksForAllSubscriptions();
- Log.i(TAG + " [" + mSlotId + "]", "Closing connection and clearing callbacks");
+ Log.i(TAG, "Closing connection and clearing callbacks");
}
}
@@ -290,7 +290,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
CallbackAdapterManager<IImsRegistrationCallback> {
public ImsRegistrationCallbackAdapter(Context context, Object lock) {
- super(context, lock, mSlotId);
+ super(context, lock);
}
@Override
@@ -304,8 +304,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
+ " binder is dead.");
}
} else {
- Log.e(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter: ImsRegistration"
- + " is null");
+ Log.e(TAG, "ImsRegistrationCallbackAdapter: ImsRegistration is null");
throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature is"
+ "not available!");
}
@@ -318,12 +317,11 @@ public class MmTelFeatureConnection extends FeatureConnection {
try {
imsRegistration.removeRegistrationCallback(localCallback);
} catch (RemoteException e) {
- Log.w(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter -"
- + " unregisterCallback: couldn't remove registration callback");
+ Log.w(TAG, "ImsRegistrationCallbackAdapter - unregisterCallback: couldn't"
+ + " remove registration callback");
}
} else {
- Log.e(TAG + " [" + mSlotId + "]", "ImsRegistrationCallbackAdapter: ImsRegistration"
- + " is null");
+ Log.e(TAG, "ImsRegistrationCallbackAdapter: ImsRegistration is null");
}
}
}
@@ -331,7 +329,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
private class CapabilityCallbackManager extends CallbackAdapterManager<IImsCapabilityCallback> {
public CapabilityCallbackManager(Context context, Object lock) {
- super(context, lock, mSlotId);
+ super(context, lock);
}
@Override
@@ -354,8 +352,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
+ " binder is null.");
}
} else {
- Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, register: Couldn't"
- + " get binder");
+ Log.w(TAG, "CapabilityCallbackManager, register: Couldn't get binder");
throw new IllegalStateException("CapabilityCallbackManager: MmTelFeature is"
+ " not available!");
}
@@ -370,8 +367,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
binder = getServiceInterface(mBinder);
} catch (RemoteException e) {
// binder is null
- Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
- + " couldn't get binder.");
+ Log.w(TAG, "CapabilityCallbackManager, unregister: couldn't get binder.");
return;
}
}
@@ -379,19 +375,17 @@ public class MmTelFeatureConnection extends FeatureConnection {
try {
binder.removeCapabilityCallback(localCallback);
} catch (RemoteException e) {
- Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
- + " Binder is dead.");
+ Log.w(TAG, "CapabilityCallbackManager, unregister: Binder is dead.");
}
} else {
- Log.w(TAG + " [" + mSlotId + "]", "CapabilityCallbackManager, unregister:"
- + " binder is null.");
+ Log.w(TAG, "CapabilityCallbackManager, unregister: binder is null.");
}
}
}
private class ProvisioningCallbackManager extends CallbackAdapterManager<IImsConfigCallback> {
public ProvisioningCallbackManager (Context context, Object lock) {
- super(context, lock, mSlotId);
+ super(context, lock);
}
@Override
@@ -399,8 +393,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
IImsConfig binder = getConfigInterface();
if (binder == null) {
// Config interface is not currently available.
- Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't register,"
- + " binder is null.");
+ Log.w(TAG, "ProvisioningCallbackManager - couldn't register, binder is null.");
throw new IllegalStateException("ImsConfig is not available!");
}
try {
@@ -414,25 +407,49 @@ public class MmTelFeatureConnection extends FeatureConnection {
public void unregisterCallback(IImsConfigCallback localCallback) {
IImsConfig binder = getConfigInterface();
if (binder == null) {
- Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't"
- + " unregister, binder is null.");
+ Log.w(TAG, "ProvisioningCallbackManager - couldn't unregister, binder is null.");
return;
}
try {
binder.removeImsConfigCallback(localCallback);
} catch (RemoteException e) {
- Log.w(TAG + " [" + mSlotId + "]", "ProvisioningCallbackManager - couldn't"
- + " unregister, binder is dead.");
+ Log.w(TAG, "ProvisioningCallbackManager - couldn't unregister, binder is dead.");
}
}
}
+ protected final int mSlotId;
+ protected IBinder mBinder;
+ private Context mContext;
+ private Executor mExecutor;
+
+ // We are assuming the feature is available when started.
+ private volatile boolean mIsAvailable = true;
+ // 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;
+ private static boolean sImsSupportedOnDevice = true;
// 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 final IBinder.DeathRecipient mDeathRecipient = () -> {
+ Log.w(TAG, "DeathRecipient triggered, binder died.");
+ if (mContext != null && Looper.getMainLooper() != null) {
+ // Move this signal to the main thread, notifying ImsManager of the Binder
+ // death on another thread may lead to deadlocks.
+ mContext.getMainExecutor().execute(this::onRemovedOrDied);
+ return;
+ }
+ // No choice - execute on the current Binder thread.
+ onRemovedOrDied();
+ };
+
private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
private final CapabilityCallbackManager mCapabilityCallbackManager;
private final ProvisioningCallbackManager mProvisioningCallbackManager;
@@ -445,9 +462,9 @@ public class MmTelFeatureConnection extends FeatureConnection {
return serviceProxy;
}
- TelephonyManager tm = serviceProxy.getTelephonyManager();
+ TelephonyManager tm = getTelephonyManager(context);
if (tm == null) {
- Rlog.w(TAG + " [" + slotId + "]", "create: TelephonyManager is null!");
+ Rlog.w(TAG, "create: TelephonyManager is null!");
// Binder can be unset in this case because it will be torn down/recreated as part of
// a retry mechanism until the serviceProxy binder is set successfully.
return serviceProxy;
@@ -460,21 +477,121 @@ public class MmTelFeatureConnection extends FeatureConnection {
// Trigger the cache to be updated for feature status.
serviceProxy.getFeatureState();
} else {
- Rlog.w(TAG + " [" + slotId + "]", "create: binder is null!");
+ Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId);
}
return serviceProxy;
}
- public MmTelFeatureConnection(Context context, int slotId) {
- super(context, slotId, ImsFeature.FEATURE_MMTEL);
+ public static TelephonyManager getTelephonyManager(Context context) {
+ return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ public interface IFeatureUpdate {
+ /**
+ * Called when the ImsFeature has changed its state. Use
+ * {@link ImsFeature#getFeatureState()} to get the new state.
+ */
+ void notifyStateChanged();
+
+ /**
+ * Called when the ImsFeature has become unavailable due to the binder switching or app
+ * crashing. A new ImsServiceProxy should be requested for that feature.
+ */
+ void notifyUnavailable();
+ }
+
+ private final IImsServiceFeatureCallback mListenerBinder =
+ new IImsServiceFeatureCallback.Stub() {
+
+ @Override
+ public void imsFeatureCreated(int slotId, int feature) {
+ mExecutor.execute(() -> {
+ // The feature has been enabled. This happens when the feature is first created and
+ // may happen when the feature is re-enabled.
+ synchronized (mLock) {
+ if(mSlotId != slotId) {
+ return;
+ }
+ switch (feature) {
+ case ImsFeature.FEATURE_MMTEL: {
+ if (!mIsAvailable) {
+ Log.i(TAG, "MmTel enabled on slotId: " + slotId);
+ mIsAvailable = true;
+ }
+ break;
+ }
+ case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
+ mSupportsEmergencyCalling = true;
+ Log.i(TAG, "Emergency calling enabled on slotId: " + slotId);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void imsFeatureRemoved(int slotId, int feature) {
+ mExecutor.execute(() -> {
+ synchronized (mLock) {
+ if (mSlotId != slotId) {
+ return;
+ }
+ switch (feature) {
+ case ImsFeature.FEATURE_MMTEL: {
+ Log.i(TAG, "MmTel removed on slotId: " + slotId);
+ onRemovedOrDied();
+ break;
+ }
+ case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
+ mSupportsEmergencyCalling = false;
+ Log.i(TAG, "Emergency calling disabled on slotId: " + slotId);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void imsStatusChanged(int slotId, int feature, int status) {
+ mExecutor.execute(() -> {
+ synchronized (mLock) {
+ Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
+ " status: " + status);
+ if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
+ mFeatureStateCached = status;
+ if (mStatusCallback != null) {
+ mStatusCallback.notifyStateChanged();
+ }
+ }
+ }
+ });
+ }
+ };
+ public MmTelFeatureConnection(Context context, int slotId) {
+ mSlotId = slotId;
+ mContext = context;
+ // Callbacks should be scheduled on the main thread.
+ if (context.getMainLooper() != null) {
+ mExecutor = context.getMainExecutor();
+ } else {
+ // Fallback to the current thread.
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ mExecutor = new HandlerExecutor(new Handler(Looper.myLooper()));
+ }
mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock);
mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock);
mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock);
}
- @Override
- protected void onRemovedOrDied() {
+ /**
+ * Called when the MmTelFeature has either been removed by Telephony or crashed.
+ */
+ private void onRemovedOrDied() {
synchronized (mLock) {
mRegistrationCallbackManager.close();
mCapabilityCallbackManager.close();
@@ -494,6 +611,27 @@ public class MmTelFeatureConnection extends FeatureConnection {
}
}
+ private @Nullable IImsRegistration getRegistration() {
+ synchronized (mLock) {
+ // null if cache is invalid;
+ if (mRegistrationBinder != null) {
+ return mRegistrationBinder;
+ }
+ }
+ TelephonyManager tm = getTelephonyManager(mContext);
+ // We don't want to synchronize on a binder call to another process.
+ IImsRegistration regBinder = tm != null
+ ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
+ synchronized (mLock) {
+ // mRegistrationBinder may have changed while we tried to get the registration
+ // interface.
+ if (mRegistrationBinder == null) {
+ mRegistrationBinder = regBinder;
+ }
+ }
+ return mRegistrationBinder;
+ }
+
private IImsConfig getConfig() {
synchronized (mLock) {
// null if cache is invalid;
@@ -501,7 +639,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
return mConfigBinder;
}
}
- TelephonyManager tm = getTelephonyManager();
+ TelephonyManager tm = getTelephonyManager(mContext);
IImsConfig configBinder = tm != null
? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
synchronized (mLock) {
@@ -513,71 +651,27 @@ public class MmTelFeatureConnection extends FeatureConnection {
return mConfigBinder;
}
- @Override
- protected void handleImsFeatureCreatedCallback(int slotId, int feature) {
- // The feature has been enabled. This happens when the feature is first created and
- // may happen when the feature is re-enabled.
- synchronized (mLock) {
- if(mSlotId != slotId) {
- return;
- }
- switch (feature) {
- case ImsFeature.FEATURE_MMTEL: {
- if (!mIsAvailable) {
- Log.i(TAG + " [" + mSlotId + "]", "MmTel enabled");
- mIsAvailable = true;
- }
- break;
- }
- case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
- mSupportsEmergencyCalling = true;
- Log.i(TAG + " [" + mSlotId + "]", "Emergency calling enabled");
- break;
- }
- }
- }
+ public boolean isEmergencyMmTelAvailable() {
+ return mSupportsEmergencyCalling;
}
- @Override
- protected void handleImsFeatureRemovedCallback(int slotId, int feature) {
- synchronized (mLock) {
- if (mSlotId != slotId) {
- return;
- }
- switch (feature) {
- case ImsFeature.FEATURE_MMTEL: {
- Log.i(TAG + " [" + mSlotId + "]", "MmTel removed");
- onRemovedOrDied();
- break;
- }
- case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
- mSupportsEmergencyCalling = false;
- Log.i(TAG + " [" + mSlotId + "]", "Emergency calling disabled");
- break;
- }
- }
- }
+ public IImsServiceFeatureCallback getListener() {
+ return mListenerBinder;
}
- @Override
- protected void handleImsStatusChangedCallback(int slotId, int feature, int status) {
+ public void setBinder(IBinder binder) {
synchronized (mLock) {
- Log.i(TAG + " [" + mSlotId + "]", "imsStatusChanged: slot: " + slotId + " feature: "
- + ImsFeature.FEATURE_LOG_MAP.get(feature) +
- " status: " + ImsFeature.STATE_LOG_MAP.get(status));
- if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
- mFeatureStateCached = status;
- if (mStatusCallback != null) {
- mStatusCallback.notifyStateChanged();
+ mBinder = binder;
+ try {
+ if (mBinder != null) {
+ mBinder.linkToDeath(mDeathRecipient, 0);
}
+ } catch (RemoteException e) {
+ // No need to do anything if the binder is already dead.
}
}
}
- public boolean isEmergencyMmTelAvailable() {
- return mSupportsEmergencyCalling;
- }
-
/**
* Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
* framework. Calling this method multiple times will reset the listener attached to the
@@ -603,7 +697,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
}
}
} catch (RemoteException e) {
- Log.w(TAG + " [" + mSlotId + "]", "closeConnection: couldn't remove listener!");
+ Log.w(TAG, "closeConnection: couldn't remove listener!");
}
}
@@ -705,6 +799,16 @@ public class MmTelFeatureConnection extends FeatureConnection {
return getConfig();
}
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
+ throws RemoteException {
+ IImsRegistration registration = getRegistration();
+ if (registration != null) {
+ return registration.getRegistrationTechnology();
+ } else {
+ return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+ }
+ }
+
public IImsEcbm getEcbmInterface() throws RemoteException {
synchronized (mLock) {
checkServiceIsReady();
@@ -777,8 +881,7 @@ public class MmTelFeatureConnection extends FeatureConnection {
String[] numbers) throws RemoteException {
if (isEmergency && !isEmergencyMmTelAvailable()) {
// Don't query the ImsService if emergency calling is not available on the ImsService.
- Log.i(TAG + " [" + mSlotId + "]", "MmTel does not support emergency over IMS, fallback"
- + " to CS.");
+ Log.i(TAG, "MmTel does not support emergency over IMS, fallback to CS.");
return MmTelFeature.PROCESS_CALL_CSFB;
}
synchronized (mLock) {
@@ -787,8 +890,33 @@ public class MmTelFeatureConnection extends FeatureConnection {
}
}
- @Override
- protected Integer retrieveFeatureState() {
+ /**
+ * @return an integer describing the current Feature Status, defined in
+ * {@link ImsFeature.ImsState}.
+ */
+ public int getFeatureState() {
+ synchronized (mLock) {
+ if (isBinderAlive() && mFeatureStateCached != null) {
+ return mFeatureStateCached;
+ }
+ }
+ // Don't synchronize on Binder call.
+ Integer status = retrieveFeatureState();
+ synchronized (mLock) {
+ if (status == null) {
+ return ImsFeature.STATE_UNAVAILABLE;
+ }
+ // Cache only non-null value for feature status.
+ mFeatureStateCached = status;
+ }
+ Log.i(TAG, "getFeatureState - returning " + status);
+ return status;
+ }
+
+ /**
+ * Internal method used to retrieve the feature status from the corresponding ImsService.
+ */
+ private Integer retrieveFeatureState() {
if (mBinder != null) {
try {
return getServiceInterface(mBinder).getFeatureState();
@@ -799,6 +927,42 @@ public class MmTelFeatureConnection extends FeatureConnection {
return null;
}
+ /**
+ * @param c Callback that will fire when the feature status has changed.
+ */
+ public void setStatusCallback(IFeatureUpdate c) {
+ mStatusCallback = c;
+ }
+
+ /**
+ * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
+ * method returns false, it doesn't mean that the Binder connection is not available (use
+ * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
+ * at this time.
+ *
+ * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
+ * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
+ */
+ public boolean isBinderReady() {
+ return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
+ }
+
+ /**
+ * @return false if the binder connection is no longer alive.
+ */
+ public boolean isBinderAlive() {
+ return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
+ }
+
+ private void checkServiceIsReady() throws RemoteException {
+ if (!sImsSupportedOnDevice) {
+ throw new RemoteException("IMS is not supported on this device.");
+ }
+ if (!isBinderReady()) {
+ throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
+ }
+ }
+
private IImsMmTelFeature getServiceInterface(IBinder b) {
return IImsMmTelFeature.Stub.asInterface(b);
}
diff --git a/src/java/com/android/ims/RcsFeatureConnection.java b/src/java/com/android/ims/RcsFeatureConnection.java
deleted file mode 100644
index 6f423b23..00000000
--- a/src/java/com/android/ims/RcsFeatureConnection.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.ims;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.ims.aidl.IImsCapabilityCallback;
-import android.telephony.ims.aidl.IRcsFeatureListener;
-import android.telephony.ims.feature.CapabilityChangeRequest;
-import android.telephony.ims.feature.ImsFeature;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.telephony.ims.aidl.IImsRcsFeature;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * A container of the IImsServiceController binder, which implements all of the RcsFeatures that
- * the platform currently supports: RCS
- */
-public class RcsFeatureConnection extends FeatureConnection {
- private static final String TAG = "RcsFeatureConnection";
-
- public interface IRcsFeatureUpdate extends IFeatureUpdate {
- /**
- * Called when the ImsFeature has been created.
- */
- void notifyFeatureCreated();
- }
-
- public static @NonNull RcsFeatureConnection create(Context context , int slotId,
- IFeatureUpdate callback) {
- RcsFeatureConnection serviceProxy = new RcsFeatureConnection(context, slotId, callback);
- if (!ImsManager.isImsSupportedOnDevice(context)) {
- // Return empty service proxy in the case that IMS is not supported.
- sImsSupportedOnDevice = false;
- return serviceProxy;
- }
-
- if (!sRcsFeatureManagerProxy.isRcsUceSupportedByCarrier(context, slotId)) {
- // Return empty service proxy in the case that RCS feature is not supported.
- Rlog.w(TAG, "create: RCS UCE feature is not supported");
- return serviceProxy;
- }
-
- TelephonyManager tm =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (tm == null) {
- Rlog.w(TAG, "create: TelephonyManager is null");
- return serviceProxy;
- }
-
- IImsRcsFeature binder = tm.getImsRcsFeatureAndListen(slotId, serviceProxy.getListener());
- if (binder != null) {
- serviceProxy.setBinder(binder.asBinder());
- // Trigger the cache to be updated for feature status.
- serviceProxy.getFeatureState();
- } else {
- Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId);
- }
-
- Rlog.d(TAG, "create: RcsFeatureConnection");
- return serviceProxy;
- }
-
- @VisibleForTesting
- public IRcsFeatureUpdate mRcsFeatureStatusCallback;
-
- private RcsFeatureConnection(Context context, int slotId, IFeatureUpdate callback) {
- super(context, slotId, ImsFeature.FEATURE_RCS);
- setStatusCallback(callback);
- }
-
- @Override
- public void setStatusCallback(IFeatureUpdate callback) {
- super.setStatusCallback(callback);
- mRcsFeatureStatusCallback = (IRcsFeatureUpdate) mStatusCallback;
- }
-
- /**
- * Testing interface used to mock RcsFeatureManager in testing
- * @hide
- */
- @VisibleForTesting
- public interface RcsFeatureManagerProxy {
- /**
- * Mock-able interface for
- * {@link RcsFeatureManager#isRcsUceSupportedByCarrier(Context, int)} used for testing.
- */
- boolean isRcsUceSupportedByCarrier(Context context, int slotId);
- }
-
- private static RcsFeatureManagerProxy sRcsFeatureManagerProxy = new RcsFeatureManagerProxy() {
- @Override
- public boolean isRcsUceSupportedByCarrier(Context context, int slotId) {
- return RcsFeatureManager.isRcsUceSupportedByCarrier(context, slotId);
- }
- };
-
- /**
- * Testing function used to mock RcsFeatureManager in testing
- * @hide
- */
- @VisibleForTesting
- public static void setRcsFeatureManagerProxy(RcsFeatureManagerProxy proxy) {
- sRcsFeatureManagerProxy = proxy;
- }
-
- @Override
- @VisibleForTesting
- public void handleImsFeatureCreatedCallback(int slotId, int feature) {
- Log.i(TAG, "IMS feature created: slotId= " + slotId + ", feature=" + feature);
- if (!isUpdateForThisFeatureAndSlot(slotId, feature)) {
- return;
- }
- synchronized(mLock) {
- if (!mIsAvailable) {
- Log.i(TAG, "RCS enabled on slotId: " + slotId);
- mIsAvailable = true;
- }
- // Notify RcsFeatureManager that RCS feature has already been created
- if (mRcsFeatureStatusCallback != null) {
- mRcsFeatureStatusCallback.notifyFeatureCreated();
- }
- }
- }
-
- @Override
- @VisibleForTesting
- public void handleImsFeatureRemovedCallback(int slotId, int feature) {
- Log.i(TAG, "IMS feature removed: slotId= " + slotId + ", feature=" + feature);
- if (!isUpdateForThisFeatureAndSlot(slotId, feature)) {
- return;
- }
- synchronized(mLock) {
- Log.i(TAG, "Rcs UCE removed on slotId: " + slotId);
- onRemovedOrDied();
- }
- }
-
- @Override
- @VisibleForTesting
- public void handleImsStatusChangedCallback(int slotId, int feature, int status) {
- Log.i(TAG, "IMS status changed: slotId=" + slotId
- + ", feature=" + feature + ", status=" + status);
- if (!isUpdateForThisFeatureAndSlot(slotId, feature)) {
- return;
- }
- synchronized(mLock) {
- mFeatureStateCached = status;
- // notify RCS feature status changed
- if (mRcsFeatureStatusCallback != null) {
- mRcsFeatureStatusCallback.notifyStateChanged();
- }
- }
- }
-
- private boolean isUpdateForThisFeatureAndSlot(int slotId, int feature) {
- if (mSlotId == slotId && feature == ImsFeature.FEATURE_RCS) {
- return true;
- }
- return false;
- }
-
- public void setRcsFeatureListener(IRcsFeatureListener listener) throws RemoteException {
- checkServiceIsReady();
- getServiceInterface(mBinder).setListener(listener);
- }
-
- public void changeEnabledCapabilities(CapabilityChangeRequest request,
- IImsCapabilityCallback callback) throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback);
- }
- }
-
- @Override
- @VisibleForTesting
- public void checkServiceIsReady() throws RemoteException {
- super.checkServiceIsReady();
- if (!sRcsFeatureManagerProxy.isRcsUceSupportedByCarrier(mContext, mSlotId)) {
- throw new RemoteException("RCS UCE feature is not supported");
- }
- }
-
- @Override
- @VisibleForTesting
- public Integer retrieveFeatureState() {
- if (mBinder != null) {
- try {
- return getServiceInterface(mBinder).getFeatureState();
- } catch (RemoteException e) {
- // Status check failed, don't update cache
- }
- }
- return null;
- }
-
- private IImsRcsFeature getServiceInterface(IBinder b) {
- return IImsRcsFeature.Stub.asInterface(b);
- }
-}
diff --git a/src/java/com/android/ims/RcsFeatureManager.java b/src/java/com/android/ims/RcsFeatureManager.java
deleted file mode 100644
index be891531..00000000
--- a/src/java/com/android/ims/RcsFeatureManager.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (c) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ims;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.RcsContactUceCapability;
-import android.telephony.ims.aidl.IRcsFeatureListener;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.feature.CapabilityChangeRequest;
-import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
-import android.util.Log;
-
-import com.android.ims.FeatureConnection.IFeatureUpdate;
-import com.android.ims.RcsFeatureConnection.IRcsFeatureUpdate;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.Executor;
-
-public class RcsFeatureManager implements IFeatureConnector {
- private static final String TAG = "RcsFeatureManager";
-
- private final int mSlotId;
- private final Context mContext;
- @VisibleForTesting
- public RcsFeatureConnection mRcsFeatureConnection;
- @VisibleForTesting
- public Set<IFeatureUpdate> mStatusCallbacks = new CopyOnWriteArraySet<>();
-
- public RcsFeatureManager(Context context, int slotId) {
- Log.d(TAG, "RcsFeatureManager slotId: " + slotId);
- mContext = context;
- mSlotId = slotId;
- createImsService();
- }
-
- /**
- * Binds the IMS service to make/receive the call.
- */
- private void createImsService() {
- mRcsFeatureConnection = RcsFeatureConnection.create(mContext, mSlotId,
- new IRcsFeatureUpdate() {
- @Override
- public void notifyFeatureCreated() {
- Log.d(TAG, "Feature created");
- setRcsFeatureListener();
- changeEnabledCapabilitiesAfterRcsFeatureCreated();
- }
- @Override
- public void notifyStateChanged() {
- mStatusCallbacks.forEach(
- FeatureConnection.IFeatureUpdate::notifyStateChanged);
- }
- @Override
- public void notifyUnavailable() {
- mStatusCallbacks.forEach(
- FeatureConnection.IFeatureUpdate::notifyUnavailable);
- }
- });
- }
-
- /**
- * Set RcsFeature listener and it will also trigger onFeatureReady in RcsFeature.
- */
- private void setRcsFeatureListener() {
- try {
- mRcsFeatureConnection.setRcsFeatureListener(mRcsFeatureListener);
- } catch (RemoteException e) {
- Log.e(TAG, "setRcsFeatureListener " + e);
- }
- }
-
- /**
- * The callback to receive updated from RcsFeature
- */
- protected IRcsFeatureListener mRcsFeatureListener = new IRcsFeatureListener.Stub() {
- @Override
- public void onCommandUpdate(int commandCode, int operationToken) {
- }
-
- @Override
- public void onNetworkResponse(int code, String reason, int operationToken) {
- }
-
- @Override
- public void onCapabilityRequestResponsePresence(
- List<RcsContactUceCapability> infos, int operationToken) {
- }
-
- @Override
- public void onNotifyUpdateCapabilities() {
- }
-
- @Override
- public void onUnpublish() {
- }
-
- @Override
- public void onCapabilityRequestResponseOptions(
- int code, String reason, RcsContactUceCapability info, int operationToken) {
- }
-
- @Override
- public void onRemoteCapabilityRequest(
- Uri contactUri, RcsContactUceCapability remoteInfo, int operationToken) {
- }
- };
-
- /**
- * Adds a callback for status changed events if the binder is already available. If it is not,
- * this method will throw an ImsException.
- */
- @Override
- public void addNotifyStatusChangedCallbackIfAvailable(FeatureConnection.IFeatureUpdate c)
- throws ImsException {
- if (!mRcsFeatureConnection.isBinderAlive()) {
- throw new ImsException("Binder is not active!",
- ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
- }
- if (c != null) {
- mStatusCallbacks.add(c);
- }
- }
-
- @Override
- public void removeNotifyStatusChangedCallback(FeatureConnection.IFeatureUpdate c) {
- if (c != null) {
- mStatusCallbacks.remove(c);
- } else {
- Log.w(TAG, "removeNotifyStatusChangedCallback: callback is null!");
- }
- }
-
- /**
- * Enable/Disable UCE capabilities after RcsFeature has already been created.
- */
- @VisibleForTesting
- public void changeEnabledCapabilitiesAfterRcsFeatureCreated() {
- if (isOptionsSupported()) {
- enableRcsUceCapability(RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE);
- } else if (isPresenceSupported()) {
- enableRcsUceCapability(RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
- } else {
- disableRcsUceCapabilities();
- }
- }
-
- /**
- * Enable UCE capabilities with given type.
- * @param capabilityType the
- */
- public void enableRcsUceCapability(
- @RcsImsCapabilities.RcsImsCapabilityFlag int capabilityType) {
-
- CapabilityChangeRequest request = new CapabilityChangeRequest();
- request.addCapabilitiesToEnableForTech(capabilityType,
- ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
- request.addCapabilitiesToEnableForTech(capabilityType,
- ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
- try {
- Log.i(TAG, "enableRcsUceCapability: " + capabilityType);
- mRcsFeatureConnection.changeEnabledCapabilities(request, null);
- } catch (RemoteException e) {
- Log.e(TAG, "enableRcsUceCapability " + e);
- }
- }
-
- /**
- * Disable UCE capabilities.
- */
- public void disableRcsUceCapabilities() {
- final int techLte = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
- final int techIWlan = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
- CapabilityChangeRequest request = new CapabilityChangeRequest();
- request.addCapabilitiesToDisableForTech(
- RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE, techLte);
- request.addCapabilitiesToDisableForTech(
- RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE, techIWlan);
- request.addCapabilitiesToDisableForTech(
- RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, techLte);
- request.addCapabilitiesToDisableForTech(
- RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE, techIWlan);
- try {
- Log.i(TAG, "disableRcsUceCapabilities");
- mRcsFeatureConnection.changeEnabledCapabilities(request, null);
- } catch (RemoteException e) {
- Log.e(TAG, "disableRcsUceCapabilities " + e);
- }
- }
-
- private boolean isOptionsSupported() {
- return isCapabilityTypeSupported(mContext, mSlotId,
- RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE);
- }
-
- private boolean isPresenceSupported() {
- return isCapabilityTypeSupported(mContext, mSlotId,
- RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
- }
-
- /**
- * Check if RCS UCE feature is supported by carrier.
- */
- public static boolean isRcsUceSupportedByCarrier(Context context, int slotId) {
- boolean isOptionsSupported = isCapabilityTypeSupported(
- context, slotId, RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE);
- boolean isPresenceSupported = isCapabilityTypeSupported(
- context, slotId, RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
-
- Log.i(TAG, "isRcsUceSupported: options=" + isOptionsSupported
- + ", Presence=" + isPresenceSupported);
-
- return isOptionsSupported | isPresenceSupported;
- }
-
- /*
- * Check if the given type of capability is supported.
- */
- private static boolean isCapabilityTypeSupported(
- Context context, int slotId, int capabilityType) {
- int subId = sSubscriptionManagerProxy.getSubId(slotId);
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- Log.e(TAG, "isCapabilityTypeSupported: Getting subIds is failure! slotId=" + slotId);
- return false;
- }
-
- CarrierConfigManager configManager =
- (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) {
- return false;
- }
-
- PersistableBundle b = configManager.getConfigForSubId(subId);
- if (b == null) {
- return false;
- }
-
- if (capabilityType == RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE) {
- return b.getBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, false);
- } else if (capabilityType == RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE) {
- return b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false);
- }
- return false;
- }
-
- @Override
- public int getImsServiceState() throws ImsException {
- return mRcsFeatureConnection.getFeatureState();
- }
-
- /**
- * Testing interface used to mock SubscriptionManager in testing
- * @hide
- */
- @VisibleForTesting
- public interface SubscriptionManagerProxy {
- /**
- * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing.
- */
- int getSubId(int slotId);
- }
-
- private static SubscriptionManagerProxy sSubscriptionManagerProxy
- = slotId -> {
- int[] subIds = SubscriptionManager.getSubId(slotId);
- if (subIds != null) {
- Log.d(TAG, "getSubId : " + subIds[0]);
- return subIds[0];
- }
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- };
-
- /**
- * Testing function used to mock SubscriptionManager in testing
- * @hide
- */
- @VisibleForTesting
- public static void setSubscriptionManager(SubscriptionManagerProxy proxy) {
- sSubscriptionManagerProxy = proxy;
- }
-}
diff --git a/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java b/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
index 05daa35b..a77000db 100644
--- a/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
+++ b/src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java
@@ -16,7 +16,6 @@
package com.android.ims.internal;
-import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
@@ -228,7 +227,6 @@ public class ImsVideoCallProviderWrapper extends Connection.VideoProvider {
*
* @param VideoProvider
*/
- @UnsupportedAppUsage
public ImsVideoCallProviderWrapper(IImsVideoCallProvider videoProvider)
throws RemoteException {