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

Commit 09d4d5d2 authored by Brad Ebinger's avatar Brad Ebinger Committed by Android (Google) Code Review
Browse files

Merge "Framework Integration of dynamic query for ImsService" into pi-dev

parents 2e1b5b9d 5f6a5219
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