Loading src/java/com/android/internal/telephony/PhoneFactory.java +1 −1 Original line number Diff line number Diff line Loading @@ -155,7 +155,7 @@ public class PhoneFactory { Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage); sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones, isDynamicBinding); sImsResolver.populateCacheAndStartBind(); sImsResolver.initPopulateCacheAndStartBind(); int[] networkModes = new int[numPhones]; sPhones = new Phone[numPhones]; Loading src/java/com/android/internal/telephony/ims/ImsResolver.java +431 −134 File changed.Preview size limit exceeded, changes collapsed. Show changes src/java/com/android/internal/telephony/ims/ImsServiceController.java +87 −44 Original line number Diff line number Diff line Loading @@ -34,8 +34,8 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.util.Log; import android.util.Pair; import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.ims.internal.IImsServiceFeatureCallback; Loading @@ -50,16 +50,15 @@ import java.util.Set; * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the * ImsService will support. * * When the ImsService is first bound, {@link IImsServiceController#createImsFeature} will be * called * When the ImsService is first bound, {@link ImsService#createMmTelFeature(int)} and * {@link ImsService#createRcsFeature(int)} will be called * on each feature that the service supports. For each ImsFeature that is created, * {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the * listener that the ImsService now supports that feature. * * When {@link #changeImsServiceFeatures} is called with a set of features that is different from * the original set, {@link IImsServiceController#createImsFeature} and * {@link IImsServiceController#removeImsFeature} will be called for each feature that is * created/removed. * the original set, create and {@link IImsServiceController#removeImsFeature} will be called for * each feature that is created/removed. */ public class ImsServiceController { Loading @@ -74,6 +73,10 @@ public class ImsServiceController { @Override public void binderDied() { Log.e(LOG_TAG, "ImsService(" + mComponentName + ") died. Restarting..."); synchronized (mLock) { mIsBinding = false; mIsBound = false; } notifyAllFeaturesRemoved(); cleanUpService(); startDelayedRebindToService(); Loading @@ -98,7 +101,7 @@ public class ImsServiceController { mImsServiceControllerBinder = service; setServiceController(service); // create all associated features in the ImsService for (Pair<Integer, Integer> i : mImsFeatures) { for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) { addImsServiceFeature(i); } } catch (RemoteException e) { Loading @@ -120,13 +123,28 @@ public class ImsServiceController { synchronized (mLock) { mIsBinding = false; } cleanupConnection(); Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting..."); // Service disconnected, but we are still technically bound. Waiting for reconnect. } @Override public void onBindingDied(ComponentName name) { synchronized (mLock) { mIsBinding = false; mIsBound = false; } cleanupConnection(); Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind..."); startDelayedRebindToService(); } private void cleanupConnection() { if (isServiceControllerAvailable()) { mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0); } notifyAllFeaturesRemoved(); cleanUpService(); Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Rebinding..."); startDelayedRebindToService(); } } Loading Loading @@ -175,7 +193,7 @@ public class ImsServiceController { private boolean mIsBound = false; private boolean mIsBinding = false; // Set of a pair of slotId->feature private HashSet<Pair<Integer, Integer>> mImsFeatures; private HashSet<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures; // Binder interfaces to the features set in mImsFeatures; private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>(); private IImsServiceController mIImsServiceController; Loading Loading @@ -315,15 +333,15 @@ public class ImsServiceController { } /** * Sends request to bind to ImsService designated by the {@ComponentName} with the feature set * imsFeatureSet * Sends request to bind to ImsService designated by the {@link ComponentName} with the feature * set imsFeatureSet. * * @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be * created once the service is bound. * @return {@link true} if the service is in the process of being bound, {@link false} if it * has failed. */ public boolean bind(HashSet<Pair<Integer, Integer>> imsFeatureSet) { public boolean bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) { synchronized (mLock) { if (!mIsBound && !mIsBinding) { mIsBinding = true; Loading Loading @@ -387,26 +405,32 @@ public class ImsServiceController { * For every feature that is added, the service calls the associated create. For every * ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called. */ public void changeImsServiceFeatures(HashSet<Pair<Integer, Integer>> newImsFeatures) public void changeImsServiceFeatures( HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures) throws RemoteException { synchronized (mLock) { Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for " + "ImsService: " + mComponentName); HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldImsFeatures = new HashSet<>(mImsFeatures); // Set features first in case we lose binding and need to rebind later. mImsFeatures = newImsFeatures; if (mIsBound) { // add features to service. HashSet<Pair<Integer, Integer>> newFeatures = new HashSet<>(newImsFeatures); newFeatures.removeAll(mImsFeatures); for (Pair<Integer, Integer> i : newFeatures) { HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures = new HashSet<>(mImsFeatures); newFeatures.removeAll(oldImsFeatures); for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) { addImsServiceFeature(i); } // remove old features HashSet<Pair<Integer, Integer>> oldFeatures = new HashSet<>(mImsFeatures); oldFeatures.removeAll(newImsFeatures); for (Pair<Integer, Integer> i : oldFeatures) { HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures = new HashSet<>(oldImsFeatures); oldFeatures.removeAll(mImsFeatures); for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) { removeImsServiceFeature(i); } } Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for " + "ImsService: " + mComponentName); mImsFeatures = newImsFeatures; } } Loading Loading @@ -464,7 +488,11 @@ public class ImsServiceController { public void enableIms(int slotId) { try { synchronized (mLock) { if (isServiceControllerAvailable()) { mIImsServiceController.enableIms(slotId); } } } catch (RemoteException e) { Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage()); } Loading @@ -472,7 +500,11 @@ public class ImsServiceController { public void disableIms(int slotId) { try { synchronized (mLock) { if (isServiceControllerAvailable()) { mIImsServiceController.disableIms(slotId); } } } catch (RemoteException e) { Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage()); } Loading Loading @@ -512,7 +544,8 @@ public class ImsServiceController { */ public IImsRegistration getRegistration(int slotId) throws RemoteException { synchronized (mLock) { return mIImsServiceController.getRegistration(slotId); return isServiceControllerAvailable() ? mIImsServiceController.getRegistration(slotId) : null; } } Loading @@ -521,7 +554,7 @@ public class ImsServiceController { */ public IImsConfig getConfig(int slotId) throws RemoteException { synchronized (mLock) { return mIImsServiceController.getConfig(slotId); return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null; } } Loading @@ -537,6 +570,15 @@ public class ImsServiceController { mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController); } /** * @return true if the controller is currently bound. */ public boolean isBound() { synchronized (mLock) { return mIsBound; } } /** * Check to see if the service controller is available, overridden for compat versions, * @return true if available, false otherwise; Loading Loading @@ -623,46 +665,48 @@ public class ImsServiceController { } // This method should only be called when synchronized on mLock private void addImsServiceFeature(Pair<Integer, Integer> featurePair) throws RemoteException { private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) throws RemoteException { if (!isServiceControllerAvailable() || mCallbacks == null) { Log.w(LOG_TAG, "addImsServiceFeature called with null values."); return; } ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.first, featurePair.second); ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId, featurePair.featureType); mFeatureStatusCallbacks.add(c); IInterface f = createImsFeature(featurePair.first, featurePair.second, c.getCallback()); addImsFeatureBinder(featurePair.first, featurePair.second, f); IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType, c.getCallback()); addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f); // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController mCallbacks.imsServiceFeatureCreated(featurePair.first, featurePair.second, this); mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this); // Send callback to ImsServiceProxy to change supported ImsFeatures sendImsFeatureCreatedCallback(featurePair.first, featurePair.second); sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType); } // This method should only be called when synchronized on mLock private void removeImsServiceFeature(Pair<Integer, Integer> featurePair) private void removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) throws RemoteException { if (!isServiceControllerAvailable() || mCallbacks == null) { Log.w(LOG_TAG, "removeImsServiceFeature called with null values."); return; } ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c -> c.mSlotId == featurePair.first && c.mFeatureType == featurePair.second) c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType) .findFirst().orElse(null); // Remove status callbacks from list. if (callbackToRemove != null) { mFeatureStatusCallbacks.remove(callbackToRemove); } removeImsFeature(featurePair.first, featurePair.second, removeImsFeature(featurePair.slotId, featurePair.featureType, (callbackToRemove != null ? callbackToRemove.getCallback() : null)); removeImsFeatureBinder(featurePair.first, featurePair.second); removeImsFeatureBinder(featurePair.slotId, featurePair.featureType); // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController mCallbacks.imsServiceFeatureRemoved(featurePair.first, featurePair.second, this); mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this); // Send callback to ImsServiceProxy to change supported ImsFeatures // Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an // ImsManager requests the ImsService while it is being removed in ImsResolver, this // callback will clean it up after. sendImsFeatureRemovedCallback(featurePair.first, featurePair.second); sendImsFeatureRemovedCallback(featurePair.slotId, featurePair.featureType); } // This method should only be called when already synchronized on mLock. Loading Loading @@ -714,9 +758,9 @@ public class ImsServiceController { return; } synchronized (mLock) { for (Pair<Integer, Integer> feature : mImsFeatures) { mCallbacks.imsServiceFeatureRemoved(feature.first, feature.second, this); sendImsFeatureRemovedCallback(feature.first, feature.second); for (ImsFeatureConfiguration.FeatureSlotPair feature : mImsFeatures) { mCallbacks.imsServiceFeatureRemoved(feature.slotId, feature.featureType, this); sendImsFeatureRemovedCallback(feature.slotId, feature.featureType); } } } Loading @@ -727,7 +771,6 @@ public class ImsServiceController { mImsServiceConnection = null; mImsServiceControllerBinder = null; setServiceController(null); mIsBound = false; } } } src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java 0 → 100644 +160 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.telephony.ims; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.util.Log; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Manages the querying of multiple ImsServices asynchronously in order to retrieve the ImsFeatures * they support. */ public class ImsServiceFeatureQueryManager { private final class ImsServiceFeatureQuery implements ServiceConnection { private static final String LOG_TAG = "ImsServiceFeatureQuery"; private final ComponentName mName; private final String mIntentFilter; ImsServiceFeatureQuery(ComponentName name, String intentFilter) { mName = name; mIntentFilter = intentFilter; } /** * Starts the bind to the ImsService specified ComponentName. * @return true if binding started, false if it failed and will not recover. */ public boolean start() { Log.d(LOG_TAG, "start: intent filter=" + mIntentFilter + ", name=" + mName); Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName); int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | Context.BIND_IMPORTANT; boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags); if (!bindStarted) { // Docs say to unbind if this fails. cleanup(); } return bindStarted; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(LOG_TAG, "onServiceConnected for component: " + name); if (service != null) { queryImsFeatures(IImsServiceController.Stub.asInterface(service)); } else { Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up."); cleanup(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.w(LOG_TAG, "onServiceDisconnected for component: " + name); } private void queryImsFeatures(IImsServiceController controller) { ImsFeatureConfiguration config; try { config = controller.querySupportedImsFeatures(); } catch (RemoteException e) { Log.w(LOG_TAG, "queryImsFeatures - error: " + e); cleanup(); mListener.onError(mName); return; } Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures(); // Complete, remove from active queries and notify. cleanup(); mListener.onComplete(mName, servicePairs); } private void cleanup() { mContext.unbindService(this); synchronized (mLock) { mActiveQueries.remove(mName); } } } public interface Listener { /** * Called when a query has completed. * @param name The Package Name of the query * @param features A Set of slotid->feature pairs that the ImsService supports. */ void onComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features); /** * Called when a query has failed and should be retried. */ void onError(ComponentName name); } // Maps an active ImsService query (by Package Name String) its query. private final Map<ComponentName, ImsServiceFeatureQuery> mActiveQueries = new HashMap<>(); private final Context mContext; private final Listener mListener; private final Object mLock = new Object(); public ImsServiceFeatureQueryManager(Context context, Listener listener) { mContext = context; mListener = listener; } /** * Starts an ImsService feature query for the ComponentName and Intent specified. * @param name The ComponentName of the ImsService being queried. * @param intentFilter The Intent filter that the ImsService specified. * @return true if the query started, false if it was unable to start. */ public boolean startQuery(ComponentName name, String intentFilter) { synchronized (mLock) { if (mActiveQueries.containsKey(name)) { // We already have an active query, wait for it to return. return true; } ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter); mActiveQueries.put(name, query); return query.start(); } } /** * @return true if there are any active queries, false if the manager is idle. */ public boolean isQueryInProgress() { synchronized (mLock) { return !mActiveQueries.isEmpty(); } } } tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java +13 −10 Original line number Diff line number Diff line Loading @@ -68,8 +68,8 @@ public class ImsRegistrationTests { @Test public void testRegistrationConfigParcel() { ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .build(); Parcel p = Parcel.obtain(); testConfig.writeToParcel(p, 0); Loading @@ -85,14 +85,14 @@ public class ImsRegistrationTests { @Test public void testRegistrationConfigPermutationEqual() { ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .build(); // Permute field insertion ordering to ensure order doesn't matter. ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_RCS) .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .build(); assertEquals(testConfig, testConfig2); Loading @@ -101,13 +101,16 @@ public class ImsRegistrationTests { @SmallTest @Test public void testRegistrationConfigConstructorsEqual() { ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration( new int[] {ImsFeature.FEATURE_MMTEL, ImsFeature.FEATURE_RCS}); // Permute field insertion ordering to ensure order doesn't matter. ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder() .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .build(); // Permute field insertion ordering to ensure order doesn't matter. ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_RCS) .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .build(); assertEquals(testConfig, testConfig2); Loading Loading
src/java/com/android/internal/telephony/PhoneFactory.java +1 −1 Original line number Diff line number Diff line Loading @@ -155,7 +155,7 @@ public class PhoneFactory { Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage); sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones, isDynamicBinding); sImsResolver.populateCacheAndStartBind(); sImsResolver.initPopulateCacheAndStartBind(); int[] networkModes = new int[numPhones]; sPhones = new Phone[numPhones]; Loading
src/java/com/android/internal/telephony/ims/ImsResolver.java +431 −134 File changed.Preview size limit exceeded, changes collapsed. Show changes
src/java/com/android/internal/telephony/ims/ImsServiceController.java +87 −44 Original line number Diff line number Diff line Loading @@ -34,8 +34,8 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.util.Log; import android.util.Pair; import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.ims.internal.IImsServiceFeatureCallback; Loading @@ -50,16 +50,15 @@ import java.util.Set; * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the * ImsService will support. * * When the ImsService is first bound, {@link IImsServiceController#createImsFeature} will be * called * When the ImsService is first bound, {@link ImsService#createMmTelFeature(int)} and * {@link ImsService#createRcsFeature(int)} will be called * on each feature that the service supports. For each ImsFeature that is created, * {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the * listener that the ImsService now supports that feature. * * When {@link #changeImsServiceFeatures} is called with a set of features that is different from * the original set, {@link IImsServiceController#createImsFeature} and * {@link IImsServiceController#removeImsFeature} will be called for each feature that is * created/removed. * the original set, create and {@link IImsServiceController#removeImsFeature} will be called for * each feature that is created/removed. */ public class ImsServiceController { Loading @@ -74,6 +73,10 @@ public class ImsServiceController { @Override public void binderDied() { Log.e(LOG_TAG, "ImsService(" + mComponentName + ") died. Restarting..."); synchronized (mLock) { mIsBinding = false; mIsBound = false; } notifyAllFeaturesRemoved(); cleanUpService(); startDelayedRebindToService(); Loading @@ -98,7 +101,7 @@ public class ImsServiceController { mImsServiceControllerBinder = service; setServiceController(service); // create all associated features in the ImsService for (Pair<Integer, Integer> i : mImsFeatures) { for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) { addImsServiceFeature(i); } } catch (RemoteException e) { Loading @@ -120,13 +123,28 @@ public class ImsServiceController { synchronized (mLock) { mIsBinding = false; } cleanupConnection(); Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting..."); // Service disconnected, but we are still technically bound. Waiting for reconnect. } @Override public void onBindingDied(ComponentName name) { synchronized (mLock) { mIsBinding = false; mIsBound = false; } cleanupConnection(); Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind..."); startDelayedRebindToService(); } private void cleanupConnection() { if (isServiceControllerAvailable()) { mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0); } notifyAllFeaturesRemoved(); cleanUpService(); Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Rebinding..."); startDelayedRebindToService(); } } Loading Loading @@ -175,7 +193,7 @@ public class ImsServiceController { private boolean mIsBound = false; private boolean mIsBinding = false; // Set of a pair of slotId->feature private HashSet<Pair<Integer, Integer>> mImsFeatures; private HashSet<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures; // Binder interfaces to the features set in mImsFeatures; private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>(); private IImsServiceController mIImsServiceController; Loading Loading @@ -315,15 +333,15 @@ public class ImsServiceController { } /** * Sends request to bind to ImsService designated by the {@ComponentName} with the feature set * imsFeatureSet * Sends request to bind to ImsService designated by the {@link ComponentName} with the feature * set imsFeatureSet. * * @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be * created once the service is bound. * @return {@link true} if the service is in the process of being bound, {@link false} if it * has failed. */ public boolean bind(HashSet<Pair<Integer, Integer>> imsFeatureSet) { public boolean bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) { synchronized (mLock) { if (!mIsBound && !mIsBinding) { mIsBinding = true; Loading Loading @@ -387,26 +405,32 @@ public class ImsServiceController { * For every feature that is added, the service calls the associated create. For every * ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called. */ public void changeImsServiceFeatures(HashSet<Pair<Integer, Integer>> newImsFeatures) public void changeImsServiceFeatures( HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures) throws RemoteException { synchronized (mLock) { Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for " + "ImsService: " + mComponentName); HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldImsFeatures = new HashSet<>(mImsFeatures); // Set features first in case we lose binding and need to rebind later. mImsFeatures = newImsFeatures; if (mIsBound) { // add features to service. HashSet<Pair<Integer, Integer>> newFeatures = new HashSet<>(newImsFeatures); newFeatures.removeAll(mImsFeatures); for (Pair<Integer, Integer> i : newFeatures) { HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures = new HashSet<>(mImsFeatures); newFeatures.removeAll(oldImsFeatures); for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) { addImsServiceFeature(i); } // remove old features HashSet<Pair<Integer, Integer>> oldFeatures = new HashSet<>(mImsFeatures); oldFeatures.removeAll(newImsFeatures); for (Pair<Integer, Integer> i : oldFeatures) { HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures = new HashSet<>(oldImsFeatures); oldFeatures.removeAll(mImsFeatures); for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) { removeImsServiceFeature(i); } } Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for " + "ImsService: " + mComponentName); mImsFeatures = newImsFeatures; } } Loading Loading @@ -464,7 +488,11 @@ public class ImsServiceController { public void enableIms(int slotId) { try { synchronized (mLock) { if (isServiceControllerAvailable()) { mIImsServiceController.enableIms(slotId); } } } catch (RemoteException e) { Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage()); } Loading @@ -472,7 +500,11 @@ public class ImsServiceController { public void disableIms(int slotId) { try { synchronized (mLock) { if (isServiceControllerAvailable()) { mIImsServiceController.disableIms(slotId); } } } catch (RemoteException e) { Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage()); } Loading Loading @@ -512,7 +544,8 @@ public class ImsServiceController { */ public IImsRegistration getRegistration(int slotId) throws RemoteException { synchronized (mLock) { return mIImsServiceController.getRegistration(slotId); return isServiceControllerAvailable() ? mIImsServiceController.getRegistration(slotId) : null; } } Loading @@ -521,7 +554,7 @@ public class ImsServiceController { */ public IImsConfig getConfig(int slotId) throws RemoteException { synchronized (mLock) { return mIImsServiceController.getConfig(slotId); return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null; } } Loading @@ -537,6 +570,15 @@ public class ImsServiceController { mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController); } /** * @return true if the controller is currently bound. */ public boolean isBound() { synchronized (mLock) { return mIsBound; } } /** * Check to see if the service controller is available, overridden for compat versions, * @return true if available, false otherwise; Loading Loading @@ -623,46 +665,48 @@ public class ImsServiceController { } // This method should only be called when synchronized on mLock private void addImsServiceFeature(Pair<Integer, Integer> featurePair) throws RemoteException { private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) throws RemoteException { if (!isServiceControllerAvailable() || mCallbacks == null) { Log.w(LOG_TAG, "addImsServiceFeature called with null values."); return; } ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.first, featurePair.second); ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId, featurePair.featureType); mFeatureStatusCallbacks.add(c); IInterface f = createImsFeature(featurePair.first, featurePair.second, c.getCallback()); addImsFeatureBinder(featurePair.first, featurePair.second, f); IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType, c.getCallback()); addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f); // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController mCallbacks.imsServiceFeatureCreated(featurePair.first, featurePair.second, this); mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this); // Send callback to ImsServiceProxy to change supported ImsFeatures sendImsFeatureCreatedCallback(featurePair.first, featurePair.second); sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType); } // This method should only be called when synchronized on mLock private void removeImsServiceFeature(Pair<Integer, Integer> featurePair) private void removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) throws RemoteException { if (!isServiceControllerAvailable() || mCallbacks == null) { Log.w(LOG_TAG, "removeImsServiceFeature called with null values."); return; } ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c -> c.mSlotId == featurePair.first && c.mFeatureType == featurePair.second) c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType) .findFirst().orElse(null); // Remove status callbacks from list. if (callbackToRemove != null) { mFeatureStatusCallbacks.remove(callbackToRemove); } removeImsFeature(featurePair.first, featurePair.second, removeImsFeature(featurePair.slotId, featurePair.featureType, (callbackToRemove != null ? callbackToRemove.getCallback() : null)); removeImsFeatureBinder(featurePair.first, featurePair.second); removeImsFeatureBinder(featurePair.slotId, featurePair.featureType); // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController mCallbacks.imsServiceFeatureRemoved(featurePair.first, featurePair.second, this); mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this); // Send callback to ImsServiceProxy to change supported ImsFeatures // Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an // ImsManager requests the ImsService while it is being removed in ImsResolver, this // callback will clean it up after. sendImsFeatureRemovedCallback(featurePair.first, featurePair.second); sendImsFeatureRemovedCallback(featurePair.slotId, featurePair.featureType); } // This method should only be called when already synchronized on mLock. Loading Loading @@ -714,9 +758,9 @@ public class ImsServiceController { return; } synchronized (mLock) { for (Pair<Integer, Integer> feature : mImsFeatures) { mCallbacks.imsServiceFeatureRemoved(feature.first, feature.second, this); sendImsFeatureRemovedCallback(feature.first, feature.second); for (ImsFeatureConfiguration.FeatureSlotPair feature : mImsFeatures) { mCallbacks.imsServiceFeatureRemoved(feature.slotId, feature.featureType, this); sendImsFeatureRemovedCallback(feature.slotId, feature.featureType); } } } Loading @@ -727,7 +771,6 @@ public class ImsServiceController { mImsServiceConnection = null; mImsServiceControllerBinder = null; setServiceController(null); mIsBound = false; } } }
src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java 0 → 100644 +160 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.telephony.ims; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.util.Log; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Manages the querying of multiple ImsServices asynchronously in order to retrieve the ImsFeatures * they support. */ public class ImsServiceFeatureQueryManager { private final class ImsServiceFeatureQuery implements ServiceConnection { private static final String LOG_TAG = "ImsServiceFeatureQuery"; private final ComponentName mName; private final String mIntentFilter; ImsServiceFeatureQuery(ComponentName name, String intentFilter) { mName = name; mIntentFilter = intentFilter; } /** * Starts the bind to the ImsService specified ComponentName. * @return true if binding started, false if it failed and will not recover. */ public boolean start() { Log.d(LOG_TAG, "start: intent filter=" + mIntentFilter + ", name=" + mName); Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName); int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | Context.BIND_IMPORTANT; boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags); if (!bindStarted) { // Docs say to unbind if this fails. cleanup(); } return bindStarted; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(LOG_TAG, "onServiceConnected for component: " + name); if (service != null) { queryImsFeatures(IImsServiceController.Stub.asInterface(service)); } else { Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up."); cleanup(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.w(LOG_TAG, "onServiceDisconnected for component: " + name); } private void queryImsFeatures(IImsServiceController controller) { ImsFeatureConfiguration config; try { config = controller.querySupportedImsFeatures(); } catch (RemoteException e) { Log.w(LOG_TAG, "queryImsFeatures - error: " + e); cleanup(); mListener.onError(mName); return; } Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures(); // Complete, remove from active queries and notify. cleanup(); mListener.onComplete(mName, servicePairs); } private void cleanup() { mContext.unbindService(this); synchronized (mLock) { mActiveQueries.remove(mName); } } } public interface Listener { /** * Called when a query has completed. * @param name The Package Name of the query * @param features A Set of slotid->feature pairs that the ImsService supports. */ void onComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features); /** * Called when a query has failed and should be retried. */ void onError(ComponentName name); } // Maps an active ImsService query (by Package Name String) its query. private final Map<ComponentName, ImsServiceFeatureQuery> mActiveQueries = new HashMap<>(); private final Context mContext; private final Listener mListener; private final Object mLock = new Object(); public ImsServiceFeatureQueryManager(Context context, Listener listener) { mContext = context; mListener = listener; } /** * Starts an ImsService feature query for the ComponentName and Intent specified. * @param name The ComponentName of the ImsService being queried. * @param intentFilter The Intent filter that the ImsService specified. * @return true if the query started, false if it was unable to start. */ public boolean startQuery(ComponentName name, String intentFilter) { synchronized (mLock) { if (mActiveQueries.containsKey(name)) { // We already have an active query, wait for it to return. return true; } ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter); mActiveQueries.put(name, query); return query.start(); } } /** * @return true if there are any active queries, false if the manager is idle. */ public boolean isQueryInProgress() { synchronized (mLock) { return !mActiveQueries.isEmpty(); } } }
tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java +13 −10 Original line number Diff line number Diff line Loading @@ -68,8 +68,8 @@ public class ImsRegistrationTests { @Test public void testRegistrationConfigParcel() { ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .build(); Parcel p = Parcel.obtain(); testConfig.writeToParcel(p, 0); Loading @@ -85,14 +85,14 @@ public class ImsRegistrationTests { @Test public void testRegistrationConfigPermutationEqual() { ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .build(); // Permute field insertion ordering to ensure order doesn't matter. ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_RCS) .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .build(); assertEquals(testConfig, testConfig2); Loading @@ -101,13 +101,16 @@ public class ImsRegistrationTests { @SmallTest @Test public void testRegistrationConfigConstructorsEqual() { ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration( new int[] {ImsFeature.FEATURE_MMTEL, ImsFeature.FEATURE_RCS}); // Permute field insertion ordering to ensure order doesn't matter. ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder() .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .build(); // Permute field insertion ordering to ensure order doesn't matter. ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder() .addFeature(ImsFeature.FEATURE_RCS) .addFeature(ImsFeature.FEATURE_MMTEL) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS) .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL) .build(); assertEquals(testConfig, testConfig2); Loading