diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-04-28 20:26:09 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-04-28 20:26:09 +0000 |
commit | 87aa92ce455fa7ae05855bd5679019ce398b8477 (patch) | |
tree | ed7ef0d45f5f5115c5be92256019b79c953e2dd3 | |
parent | 148ef0ecb5c16dc0da6920fe112ab2362afaec42 (diff) | |
parent | 1a09d5bc572be7a5a6ef42e41e6ba5a606e369d2 (diff) | |
download | ims-87aa92ce455fa7ae05855bd5679019ce398b8477.tar.gz |
Snap for 6439596 from 1a09d5bc572be7a5a6ef42e41e6ba5a606e369d2 to qt-aml-tzdata-releaseq_tzdata_aml_297100400q_tzdata_aml_297100300q_tzdata_aml_297100000q_tzdata_aml_296200000q_tzdata_aml_295600118q_tzdata_aml_295600110q_tzdata_aml_295500002q_tzdata_aml_295500001q_tzdata_aml_294400310android-mainline-12.0.0_r54android-mainline-12.0.0_r111android-mainline-10.0.0_r13android-mainline-10.0.0_r12android-mainline-10.0.0_r11q_tzdata_aml_297100000android12-mainline-tzdata-releaseandroid10-mainline-tzdata-releaseandroid10-android13-mainline-tzdata-release
Change-Id: I5ce6a5e44c233654354733e6458844d23eb812b0
-rw-r--r-- | src/java/com/android/ims/FeatureConnection.java | 274 | ||||
-rw-r--r-- | src/java/com/android/ims/FeatureConnector.java | 289 | ||||
-rw-r--r-- | src/java/com/android/ims/HandlerExecutor.java | 46 | ||||
-rw-r--r-- | src/java/com/android/ims/IFeatureConnector.java | 24 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsCall.java | 42 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsConnectionStateListener.java | 7 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsEcbm.java | 2 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsManager.java | 552 | ||||
-rw-r--r-- | src/java/com/android/ims/ImsUt.java | 32 | ||||
-rw-r--r-- | src/java/com/android/ims/MmTelFeatureConnection.java | 374 | ||||
-rw-r--r-- | src/java/com/android/ims/RcsFeatureConnection.java | 220 | ||||
-rw-r--r-- | src/java/com/android/ims/RcsFeatureManager.java | 303 | ||||
-rw-r--r-- | src/java/com/android/ims/internal/ImsVideoCallProviderWrapper.java | 2 |
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 { |