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

Commit 6d49f5cf authored by James Lin's avatar James Lin Committed by Automerger Merge Worker
Browse files

Merge "[RCS UCE] Create the UceController instance and the exposed interface...

Merge "[RCS UCE] Create the UceController instance and the exposed interface of controllers" am: 635f3621 am: 4edf3dde am: 513feda6 am: ce0bffbf

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/net/ims/+/1425909

Change-Id: If21e5384b269d3beaed4296085f0512828861b8d
parents 98c2ca44 ce0bffbf
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.rcs.uce;

import android.annotation.NonNull;
import android.telephony.ims.RcsContactUceCapability;

import java.util.List;

/**
 * The adapter class of the class CapabilityExchangeEventListener.
 */
public class CapabilityExchangeListenerAdapter {
    /**
     * Interface used by the framework to respond to OPTIONS requests.
     */
    public interface OptionsRequestCallback {
        /**
         * Respond to a remote capability request from the contact specified with the capabilities
         * of this device.
         */
        void respondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities);
        /**
         * Respond to a remote capability request from the contact specified with the specified
         * error.
         */
        void respondToCapabilityRequestWithError(int code, @NonNull String reason);
    }
}
+39 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.rcs.uce;

import com.android.ims.RcsFeatureManager;

/**
 * The base interface of each controllers.
 */
public interface ControllerBase {
    /**
     * The RcsFeature has been connected to the framework.
     */
    void onRcsConnected(RcsFeatureManager manager);

    /**
     * The framework has lost the binding to the RcsFeature.
     */
    void onRcsDisconnected();

    /**
     * Notify to destroy this instance. The UceController instance is unusable after destroyed.
     */
    void onDestroy();
}
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.rcs.uce;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import android.telephony.ims.ImsException;

import java.util.List;

/**
 * The adapter class of RcsCapabilityExchangeImplBase
 */
public class RcsCapabilityExchangeImplAdapter {

    /**  Service is unknown. */
    public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0;
    /** The command failed with an unknown error. */
    public static final int COMMAND_CODE_GENERIC_FAILURE = 1;
    /**  Invalid parameter(s). */
    public static final int COMMAND_CODE_INVALID_PARAM = 2;
    /**  Fetch error. */
    public static final int COMMAND_CODE_FETCH_ERROR = 3;
    /**  Request timed out. */
    public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4;
    /**  Failure due to insufficient memory available. */
    public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5;
    /**  Network connection is lost. */
    public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6;
    /**  Requested feature/resource is not supported. */
    public static final int COMMAND_CODE_NOT_SUPPORTED = 7;
    /**  Contact or resource is not found. */
    public static final int COMMAND_CODE_NOT_FOUND = 8;
    /**  Service is not available. */
    public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9;
    /** Command resulted in no change in state, ignoring. */
    public static final int COMMAND_CODE_NO_CHANGE = 10;

    /**
     * The adapter class of the interface PublishResponseCallback
     */
    public interface PublishResponseCallback {
        /**
         * Notify the framework that the command associated with this callback has failed.
         */
        void onCommandError(int code) throws ImsException;

        /**
         * Provide the framework with a subsequent network response update to
         * {@link #publishCapabilities(RcsContactUceCapability, int)}.
         */
        void onNetworkResponse(int code, @NonNull String reason) throws ImsException;
    }

    /**
     * The adapter class of the interface OptionsResponseCallback
     */
    public interface OptionsResponseCallback {
        /**
         * Notify the framework that the command associated with this callback has
         * failed.
         */
        void onCommandError(int code) throws ImsException;

        /**
         * Send the response of a SIP OPTIONS capability exchange to the framework.
         */
        void onNetworkResponse(int code, @NonNull String reason,
                @Nullable List<String> theirCaps) throws ImsException;
    }

    /**
     * The adapter class of the interface SubscribeResponseCallback
     */
    public interface SubscribeResponseCallback {
        /**
         * Notify the framework that the command associated with this callback has failed.
         */
        void onCommandError(int code) throws ImsException;

        /**
         * Notify the framework  of the response to the SUBSCRIBE request from
         * {@link #subscribeForCapabilities(RcsContactUceCapability, int)}.
         */
        void onNetworkResponse(int code, @NonNull String reason) throws ImsException;

        /**
         * Provides the framework with latest XML PIDF documents included in the
         * network response for the requested  contacts' capabilities requested by the
         * Framework  using {@link #requestCapabilities(List, int)}. This should be
         * called every time a new NOTIFY event is received with new capability
         * information.
         */
        void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;

        /**
         * The subscription associated with a previous #requestCapabilities operation
         * has been terminated. This will mostly be due to the subscription expiring,
         * but may also happen due to an error.
         */
        void onTerminated(String reason, String retryAfter) throws ImsException;
    }
}
+400 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.rcs.uce;

import android.annotation.NonNull;
import android.content.Context;
import android.net.Uri;
import android.os.HandlerThread;
import android.os.Looper;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.RcsUceAdapter.CapabilitiesCallback;
import android.telephony.ims.RcsUceAdapter.PublishState;
import android.telephony.ims.RcsUceAdapter.PublishStateCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.util.Log;

import com.android.ims.RcsFeatureManager;
import com.android.ims.rcs.uce.eab.EabCapabilityResult;
import com.android.ims.rcs.uce.eab.EabController;
import com.android.ims.rcs.uce.options.OptionsController;
import com.android.ims.rcs.uce.presence.publish.PublishController;
import com.android.ims.rcs.uce.presence.subscribe.SubscribeController;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * The UceController will manage the RCS UCE requests on a per subscription basis. When it receives
 * the UCE requests from the RCS applications and from the ImsService, it will coordinate the
 * cooperation between the publish/subscribe/options components to complete the requests.
 */
public class UceController {

    private static final String LOG_TAG = "UceController";

    /**
     * The callback interface is called by the internal controllers to receive information from
     * others controllers
     */
    public interface UceControllerCallback {
        /**
         * Retrieve the capabilities associated with the given uris from the cache.
         */
        EabCapabilityResult getCapabilitiesFromCache(@NonNull List<Uri> uris);

        /**
         * Retrieve the contact's capabilities from the availability cache.
         */
        EabCapabilityResult getAvailabilityFromCache(@NonNull Uri uri);

        /**
         * Store the given capabilities to the cache.
         */
        void saveCapabilities(List<RcsContactUceCapability> contactCapabilities);

        /**
         * Retrieve the device's capabilities.
         */
        RcsContactUceCapability getDeviceCapabilities();

        /**
         * Trigger the capabilities request with OPTIONS
         */
        void requestCapabilitiesByOptions(@NonNull Uri contactUri,
                @NonNull RcsContactUceCapability ownCapabilities,
                @NonNull RcsCapabilityExchangeImplAdapter.OptionsResponseCallback callback);

        /**
         * The method is called when the given contacts' capabilities are expired and need to be
         * refreshed.
         */
        void refreshCapabilities(@NonNull List<Uri> contactNumbers,
                @NonNull CapabilitiesCallback callback);

        /**
         * The method is called when the EabController and the PublishController want to receive
         * published state changes.
         */
        void registerPublishStateCallback(@NonNull IRcsUcePublishStateCallback c);

        /**
         * Remove the existing PublishStateCallback.
         */
        void unregisterPublishStateCallback(@NonNull IRcsUcePublishStateCallback c);
    }

    /**
     * Used to inject RequestTaskManger instances for testing.
     */
    @VisibleForTesting
    public interface RequestTaskManagerFactory {
        UceRequestTaskManager createTaskManager(Context context, int subId, Looper looper);
    }

    /**
     * Used to inject Controller instances for testing.
     */
    @VisibleForTesting
    public interface ControllerFactory {
        /**
         * @return an {@link EabController} associated with the subscription id specified.
         */
        EabController createEabController(Context context, int subId, UceControllerCallback c,
                Looper looper);

        /**
         * @return an {@link PublishController} associated with the subscription id specified.
         */
        PublishController createPublishController(Context context, int subId,
                UceControllerCallback c, Looper looper);

        /**
         * @return an {@link SubscribeController} associated with the subscription id specified.
         */
        SubscribeController createSubscribeController(Context context, int subId,
                UceControllerCallback c, Looper looper);

        /**
         * @return an {@link OptionsController} associated with the subscription id specified.
         */
        OptionsController createOptionsController(Context context, int subId,
                UceControllerCallback c, Looper looper);
    }

    private final int mSubId;
    private final Context mContext;
    private volatile boolean mIsRcsConnected;
    private volatile boolean mIsDestroyedFlag;

    private Looper mLooper;

    private UceRequestTaskManager mTaskManager;
    private final RequestTaskManagerFactory mTaskManagerFactory;

    private EabController mEabController;
    private PublishController mPublishController;
    private SubscribeController mSubscribeController;
    private OptionsController mOptionsController;
    private final ControllerFactory mControllerFactory;

    public UceController(Context context, int subId, ControllerFactory controllerFactory,
            RequestTaskManagerFactory taskManagerFactory) {
        mSubId = subId;
        logi("create");

        mContext = context;
        mControllerFactory = controllerFactory;
        mTaskManagerFactory = taskManagerFactory;

        initLooper();
        initRequestTaskManager();
        initControllers();
    }

    private void initLooper() {
        // Init the looper, it will be passed to each controller.
        HandlerThread handlerThread = new HandlerThread("UceControllerHandlerThread");
        handlerThread.start();
        mLooper = handlerThread.getLooper();
    }

    private void initRequestTaskManager() {
        mTaskManager = mTaskManagerFactory.createTaskManager(mContext, mSubId, mLooper);
    }

    private void initControllers() {
        mEabController = mControllerFactory.createEabController(mContext, mSubId, mCtrlCallback,
                mLooper);
        mPublishController = mControllerFactory.createPublishController(mContext, mSubId,
                mCtrlCallback, mLooper);
        mSubscribeController = mControllerFactory.createSubscribeController(mContext, mSubId,
                mCtrlCallback, mLooper);
        mOptionsController = mControllerFactory.createOptionsController(mContext, mSubId,
                mCtrlCallback, mLooper);
    }

    /**
     * The RcsFeature has been connected to the framework. This method runs on main thread.
     */
    public void onRcsConnected(RcsFeatureManager manager) {
        logi("onRcsConnected");
        mIsRcsConnected = true;
        // Notify each controllers that RCS is connected.
        mEabController.onRcsConnected(manager);
        mPublishController.onRcsConnected(manager);
        mSubscribeController.onRcsConnected(manager);
        mOptionsController.onRcsConnected(manager);
    }

    /**
     * The framework has lost the binding to the RcsFeature. This method runs on main thread.
     */
    public void onRcsDisconnected() {
        logi("onRcsDisconnected");
        mIsRcsConnected = false;
        // Notify each controllers that RCS is disconnected.
        mEabController.onRcsDisconnected();
        mPublishController.onRcsDisconnected();
        mSubscribeController.onRcsDisconnected();
        mOptionsController.onRcsDisconnected();
    }

    /**
     * Notify to destroy this instance. This instance is unusable after destroyed.
     */
    public void onDestroy() {
        logi("onDestroy");
        mIsDestroyedFlag = true;
        mTaskManager.onDestroy();
        mEabController.onDestroy();
        mPublishController.onDestroy();
        mSubscribeController.onDestroy();
        mOptionsController.onDestroy();

        mLooper.quit();
    }

    // The implementation of the interface UceControllerCallback.
    private UceControllerCallback mCtrlCallback = new UceControllerCallback() {
        @Override
        public EabCapabilityResult getCapabilitiesFromCache(List<Uri> uris) {
            return mEabController.getCapabilities(uris);
        }

        @Override
        public EabCapabilityResult getAvailabilityFromCache(Uri contactUri) {
            return mEabController.getAvailability(contactUri);
        }

        @Override
        public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) {
            mEabController.saveCapabilities(contactCapabilities);
        }

        @Override
        public RcsContactUceCapability getDeviceCapabilities() {
            return mPublishController.getDeviceCapabilities();
        }

        @Override
        public void requestCapabilitiesByOptions(Uri uri, RcsContactUceCapability ownCapabilities,
                RcsCapabilityExchangeImplAdapter.OptionsResponseCallback callback) {
            mOptionsController.sendCapabilitiesRequest(uri, ownCapabilities, callback);
        }

        @Override
        public void refreshCapabilities(@NonNull List<Uri> contactNumbers,
                @NonNull CapabilitiesCallback callback) {
            logd("refreshCapabilities: " + contactNumbers.size());
            UceController.this.requestCapabilities(contactNumbers, callback);
        }

        @Override
        public void registerPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) {
            logd("UceControllerCallback: registerPublishStateCallback");
            UceController.this.registerPublishStateCallback(c);
        }

        @Override
        public void unregisterPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) {
            logd("UceControllerCallback: unregisterPublishStateCallback");
            UceController.this.unregisterPublishStateCallback(c);
        }
    };

    @VisibleForTesting
    public void setUceControllerCallback(UceControllerCallback callback) {
        mCtrlCallback = callback;
    }

    /**
     * Request to get the contacts' capabilities. This method will retrieve the capabilities from
     * the cache If the capabilities are out of date, it will trigger another request to get the
     * latest contact's capabilities from the carrier network.
     */
    public void requestCapabilities(@NonNull List<Uri> uriList, @NonNull CapabilitiesCallback c) {
        if (isUnavailable()) {
            logw("requestCapabilities: controller is unavailable");
            c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
            return;
        }

        // Trigger the capabilities request task
        logd("requestCapabilities");
        mTaskManager.triggerCapabilityRequestTask(mCtrlCallback, uriList, c);
    }

    /**
     * Request to get the contact's capabilities. It will check the availability cache first. If
     * the capability in the availability cache is expired then it will retrieve the capability
     * from the carrier network.
     */
    public void requestAvailability(@NonNull Uri uri, @NonNull CapabilitiesCallback c) {
        if (isUnavailable()) {
            logw("requestAvailability: controller is unavailable");
            c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
            return;
        }

        // Trigger the availability request task
        logd("requestAvailability");
        mTaskManager.triggerAvailabilityRequestTask(mCtrlCallback, uri, c);
    }

    /**
     * Publish the device's capabilities. This request is triggered from the ImsService.
     */
    public void onRequestPublishCapabilitiesFromService(int triggerType) {
        logd("onRequestPublishCapabilitiesFromService: " + triggerType);
        mPublishController.publishCapabilities(triggerType);
    }

    /**
     * This method is triggered by the ImsService to notify framework that the device's
     * capabilities has been unpublished from the network.
     */
    public void onUnpublish() {
        logd("onUnpublish");
        mPublishController.onUnpublish();
    }

    /**
     * Request publish the device's capabilities. This request is from the ImsService to send the
     * capabilities to the remote side.
     */
    public void retrieveOptionsCapabilitiesForRemote(@NonNull Uri contactUri,
            @NonNull List<String> remoteCapabilities,
            @NonNull CapabilityExchangeListenerAdapter.OptionsRequestCallback c) {
        logd("retrieveOptionsCapabilitiesForRemote");
        mOptionsController.retrieveCapabilitiesForRemote(contactUri, remoteCapabilities, c);
    }

    /**
     * Register a {@link PublishStateCallback} to receive the published state changed.
     */
    public void registerPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) {
        mPublishController.registerPublishStateCallback(c);
    }

    /**
     * Removes an existing {@link PublishStateCallback}.
     */
    public void unregisterPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) {
        mPublishController.unregisterPublishStateCallback(c);
    }

    /**
     * Get the UCE publish state if the PUBLISH is supported by the carrier.
     */
    public @PublishState int getUcePublishState() {
        return mPublishController.getUcePublishState();
    }

    public int getSubId() {
        return mSubId;
    }

    private boolean isUnavailable() {
        if (!mIsRcsConnected || mIsDestroyedFlag) {
            return true;
        }
        return false;
    }

    private void logd(String log) {
        Log.d(LOG_TAG, getLogPrefix().append(log).toString());
    }

    private void logi(String log) {
        Log.i(LOG_TAG, getLogPrefix().append(log).toString());
    }

    private void logw(String log) {
        Log.w(LOG_TAG, getLogPrefix().append(log).toString());
    }

    private StringBuilder getLogPrefix() {
        StringBuilder builder = new StringBuilder("[");
        builder.append(mSubId);
        builder.append("] ");
        return builder;
    }
}
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.rcs.uce;

import android.net.Uri;
import android.telephony.ims.RcsUceAdapter;

import com.android.ims.rcs.uce.UceController.UceControllerCallback;

import java.util.List;

/**
 * The interface of managing the capability request and the availability request.
 */
public interface UceRequestTaskManager {
    /**
     * Trigger the capability request task.
     */
    void triggerCapabilityRequestTask(UceControllerCallback controller, List<Uri> uriList,
            RcsUceAdapter.CapabilitiesCallback callback);
    /**
     * Trigger the availability request task.
     */
    void triggerAvailabilityRequestTask(UceControllerCallback controller, Uri uri,
            RcsUceAdapter.CapabilitiesCallback callback);

    /**
     * Notify the task manager to destroy.
     */
    void onDestroy();
}
Loading