Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cda27536 authored by Brad Ebinger's avatar Brad Ebinger Committed by android-build-merger
Browse files

Merge "Framework Integration of dynamic query for ImsService"

am: f61b348b

Change-Id: I5ac960318cda441d45bc6980a302325e1ab4fac0
parents f9471166 f61b348b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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];
+431 −134

File changed.

Preview size limit exceeded, changes collapsed.

+87 −44
Original line number Diff line number Diff line
@@ -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;
@@ -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 {

@@ -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();
@@ -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) {
@@ -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();
        }
    }

@@ -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;
@@ -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;
@@ -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;
        }
    }

@@ -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());
        }
@@ -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());
        }
@@ -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;
        }
    }

@@ -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;
        }
    }

@@ -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;
@@ -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.
@@ -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);
            }
        }
    }
@@ -727,7 +771,6 @@ public class ImsServiceController {
            mImsServiceConnection = null;
            mImsServiceControllerBinder = null;
            setServiceController(null);
            mIsBound = false;
        }
    }
}
+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();
        }
    }
}
+13 −10
Original line number Diff line number Diff line
@@ -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);
@@ -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);
@@ -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