Loading src/java/com/android/ims/FeatureConnection.java +67 −4 Original line number Diff line number Diff line Loading @@ -11,11 +11,12 @@ * 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 * limitations under the License. */ package com.android.ims; import android.annotation.Nullable; import android.content.Context; import android.os.Handler; import android.os.IBinder; Loading @@ -24,14 +25,14 @@ 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.telephony.SubscriptionManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.ims.MmTelFeatureConnection.IFeatureUpdate; import java.util.concurrent.Executor; Loading @@ -41,6 +42,20 @@ import java.util.concurrent.Executor; 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; Loading @@ -55,6 +70,7 @@ public abstract class FeatureConnection { // 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) { Loading @@ -78,6 +94,10 @@ public abstract class FeatureConnection { 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; Loading Loading @@ -110,6 +130,7 @@ public abstract class FeatureConnection { synchronized (mLock) { if (mIsAvailable) { mIsAvailable = false; mRegistrationBinder = null; if (mBinder != null) { mBinder.unlinkToDeath(mDeathRecipient, 0); } Loading @@ -120,11 +141,22 @@ public abstract class FeatureConnection { } } /** * 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 Loading @@ -151,6 +183,37 @@ public abstract class FeatureConnection { 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."); Loading src/java/com/android/ims/FeatureConnector.java 0 → 100644 +288 −0 Original line number Diff line number Diff line /* * 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.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 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 listener) { mContext = context; mPhoneId = phoneId; mListener = listener; mExecutor = new HandlerExecutor(this); mLogPrefix = "?"; } public FeatureConnector(Context context, int phoneId, Listener listener, String logPrefix) { mContext = context; mPhoneId = phoneId; mListener = listener; mExecutor = new HandlerExecutor(this); mLogPrefix = logPrefix; } @VisibleForTesting public FeatureConnector(Context context, int phoneId, Listener listener, Executor executor, String logPrefix) { mContext = context; mPhoneId = phoneId; mListener= listener; mExecutor = executor; mLogPrefix = logPrefix; } @VisibleForTesting public FeatureConnector(Context context, int phoneId, Listener 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(T manager)} 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, "connect"); if (!isSupported()) { Log.i(TAG, "connect: NOT support!"); 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, "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, "createImsService"); mManager = (T) 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, "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, "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, "notifyNotReady"); mListener.connectionUnavailable(); } protected String getLogMessage(String message) { return "Connector-[" + mLogPrefix + "] " + message; } } src/java/com/android/ims/IFeatureConnector.java 0 → 100644 +24 −0 Original line number Diff line number Diff line /* * 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 src/java/com/android/ims/ImsManager.java +10 −230 File changed.Preview size limit exceeded, changes collapsed. Show changes src/java/com/android/ims/MmTelFeatureConnection.java +0 −54 Original line number Diff line number Diff line Loading @@ -414,13 +414,11 @@ public class MmTelFeatureConnection extends FeatureConnection { } } private IFeatureUpdate mStatusCallback; // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent. private boolean mSupportsEmergencyCalling = false; // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If // it becomes disconnected, invalidate. private IImsRegistration mRegistrationBinder; private IImsConfig mConfigBinder; private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager; private final CapabilityCallbackManager mCapabilityCallbackManager; Loading Loading @@ -454,20 +452,6 @@ public class MmTelFeatureConnection extends FeatureConnection { return serviceProxy; } 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(); } public MmTelFeatureConnection(Context context, int slotId) { super(context, slotId, ImsFeature.FEATURE_MMTEL); Loading Loading @@ -497,27 +481,6 @@ public class MmTelFeatureConnection extends FeatureConnection { } } private @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, 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; Loading Loading @@ -729,16 +692,6 @@ 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(); Loading Loading @@ -820,13 +773,6 @@ public class MmTelFeatureConnection extends FeatureConnection { } } /** * @param c Callback that will fire when the feature status has changed. */ public void setStatusCallback(IFeatureUpdate c) { mStatusCallback = c; } @Override protected Integer retrieveFeatureState() { if (mBinder != null) { Loading Loading
src/java/com/android/ims/FeatureConnection.java +67 −4 Original line number Diff line number Diff line Loading @@ -11,11 +11,12 @@ * 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 * limitations under the License. */ package com.android.ims; import android.annotation.Nullable; import android.content.Context; import android.os.Handler; import android.os.IBinder; Loading @@ -24,14 +25,14 @@ 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.telephony.SubscriptionManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.ims.MmTelFeatureConnection.IFeatureUpdate; import java.util.concurrent.Executor; Loading @@ -41,6 +42,20 @@ import java.util.concurrent.Executor; 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; Loading @@ -55,6 +70,7 @@ public abstract class FeatureConnection { // 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) { Loading @@ -78,6 +94,10 @@ public abstract class FeatureConnection { 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; Loading Loading @@ -110,6 +130,7 @@ public abstract class FeatureConnection { synchronized (mLock) { if (mIsAvailable) { mIsAvailable = false; mRegistrationBinder = null; if (mBinder != null) { mBinder.unlinkToDeath(mDeathRecipient, 0); } Loading @@ -120,11 +141,22 @@ public abstract class FeatureConnection { } } /** * 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 Loading @@ -151,6 +183,37 @@ public abstract class FeatureConnection { 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."); Loading
src/java/com/android/ims/FeatureConnector.java 0 → 100644 +288 −0 Original line number Diff line number Diff line /* * 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.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 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 listener) { mContext = context; mPhoneId = phoneId; mListener = listener; mExecutor = new HandlerExecutor(this); mLogPrefix = "?"; } public FeatureConnector(Context context, int phoneId, Listener listener, String logPrefix) { mContext = context; mPhoneId = phoneId; mListener = listener; mExecutor = new HandlerExecutor(this); mLogPrefix = logPrefix; } @VisibleForTesting public FeatureConnector(Context context, int phoneId, Listener listener, Executor executor, String logPrefix) { mContext = context; mPhoneId = phoneId; mListener= listener; mExecutor = executor; mLogPrefix = logPrefix; } @VisibleForTesting public FeatureConnector(Context context, int phoneId, Listener 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(T manager)} 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, "connect"); if (!isSupported()) { Log.i(TAG, "connect: NOT support!"); 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, "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, "createImsService"); mManager = (T) 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, "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, "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, "notifyNotReady"); mListener.connectionUnavailable(); } protected String getLogMessage(String message) { return "Connector-[" + mLogPrefix + "] " + message; } }
src/java/com/android/ims/IFeatureConnector.java 0 → 100644 +24 −0 Original line number Diff line number Diff line /* * 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
src/java/com/android/ims/ImsManager.java +10 −230 File changed.Preview size limit exceeded, changes collapsed. Show changes
src/java/com/android/ims/MmTelFeatureConnection.java +0 −54 Original line number Diff line number Diff line Loading @@ -414,13 +414,11 @@ public class MmTelFeatureConnection extends FeatureConnection { } } private IFeatureUpdate mStatusCallback; // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent. private boolean mSupportsEmergencyCalling = false; // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If // it becomes disconnected, invalidate. private IImsRegistration mRegistrationBinder; private IImsConfig mConfigBinder; private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager; private final CapabilityCallbackManager mCapabilityCallbackManager; Loading Loading @@ -454,20 +452,6 @@ public class MmTelFeatureConnection extends FeatureConnection { return serviceProxy; } 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(); } public MmTelFeatureConnection(Context context, int slotId) { super(context, slotId, ImsFeature.FEATURE_MMTEL); Loading Loading @@ -497,27 +481,6 @@ public class MmTelFeatureConnection extends FeatureConnection { } } private @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, 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; Loading Loading @@ -729,16 +692,6 @@ 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(); Loading Loading @@ -820,13 +773,6 @@ public class MmTelFeatureConnection extends FeatureConnection { } } /** * @param c Callback that will fire when the feature status has changed. */ public void setStatusCallback(IFeatureUpdate c) { mStatusCallback = c; } @Override protected Integer retrieveFeatureState() { if (mBinder != null) { Loading