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

Commit f1b91fe2 authored by James.cf Lin's avatar James.cf Lin Committed by Automerger Merge Worker
Browse files

Fix the UCE capability request with subscribe mechanism am: 6ba4f3fa am: 5fe759d9

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

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I2575b3a0ee2edef32f1f9106bed80ed3e4830918
parents 6d187e57 5fe759d9
Loading
Loading
Loading
Loading
+62 −36
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.ims.rcs.uce;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.Uri;
import android.os.HandlerThread;
@@ -37,6 +38,7 @@ 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.eab.EabControllerImpl;
import com.android.ims.rcs.uce.eab.EabUtil;
import com.android.ims.rcs.uce.options.OptionsController;
import com.android.ims.rcs.uce.options.OptionsControllerImpl;
import com.android.ims.rcs.uce.presence.publish.PublishController;
@@ -89,15 +91,11 @@ public class UceController {
        /**
         * The network reply that the request is forbidden.
         * @param isForbidden If UCE requests are forbidden by the network.
         * @param errorCode The {@link RcsUceAdapter#ErrorCode} of the forbidden reason.
         * @param retryAfterMillis The time to wait for the retry.
         */
        void updateRequestForbidden(boolean isForbidden, long retryAfterMillis);

        /**
         * Check if UCE request is forbidden by the network.
         * @return true when the UCE is forbidden by the network
         */
        boolean isRequestForbiddenByNetwork();
        void updateRequestForbidden(boolean isForbidden, @Nullable Integer errorCode,
                long retryAfterMillis);

        /**
         * Get the milliseconds need to wait for retry.
@@ -105,6 +103,12 @@ public class UceController {
         */
        long getRetryAfterMillis();

        /**
         * Check if UCE request is forbidden by the network.
         * @return true when the UCE is forbidden by the network
         */
        boolean isRequestForbiddenByNetwork();

        /**
         * Trigger the capabilities request with OPTIONS
         */
@@ -211,7 +215,7 @@ public class UceController {
    private UceRequestManager mRequestManager;

    // The server state for UCE requests.
    private ServerState mServerState;
    private final ServerState mServerState;

    public UceController(Context context, int subId) {
        mSubId = subId;
@@ -340,18 +344,19 @@ public class UceController {
        }

        @Override
        public void updateRequestForbidden(boolean isForbidden, long retryAfterMillis) {
            onRequestForbidden(isForbidden, retryAfterMillis);
        public void updateRequestForbidden(boolean isForbidden, @Nullable Integer errorCode,
                long retryAfterMillis) {
            mServerState.updateRequestForbidden(isForbidden, errorCode, retryAfterMillis);
        }

        @Override
        public boolean isRequestForbiddenByNetwork() {
            return mServerState.isRequestForbidden();
        public long getRetryAfterMillis() {
            return mServerState.getRetryAfterMillis();
        }

        @Override
        public long getRetryAfterMillis() {
            return mServerState.getRetryAfterMillis();
        public boolean isRequestForbiddenByNetwork() {
            return (mServerState.getForbiddenErrorCode() != null) ? true : false;
        }

        @Override
@@ -365,7 +370,7 @@ public class UceController {
        public void refreshCapabilities(@NonNull List<Uri> contactNumbers,
                @NonNull IRcsUceControllerCallback callback) throws RemoteException{
            logd("refreshCapabilities: " + contactNumbers.size());
            UceController.this.requestCapabilities(contactNumbers, callback);
            UceController.this.requestCapabilitiesInternal(contactNumbers, true, callback);
        }

        @Override
@@ -416,7 +421,12 @@ public class UceController {
     */
    public void requestCapabilities(@NonNull List<Uri> uriList,
            @NonNull IRcsUceControllerCallback c) throws RemoteException {
        if (uriList == null || c == null) {
        requestCapabilitiesInternal(uriList, false, c);
    }

    private void requestCapabilitiesInternal(@NonNull List<Uri> uriList, boolean skipFromCache,
            @NonNull IRcsUceControllerCallback c) throws RemoteException {
        if (uriList == null || uriList.isEmpty() || c == null) {
            logw("requestCapabilities: parameter is empty");
            if (c != null) {
                c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
@@ -432,15 +442,18 @@ public class UceController {

        // Check if UCE requests are forbidden by the network.
        if (mServerState.isRequestForbidden()) {
            Integer errorCode = mServerState.getForbiddenErrorCode();
            long retryAfter = mServerState.getRetryAfterMillis();
            logw("requestCapabilities: The request is forbidden, retryAfter=" + retryAfter);
            c.onError(RcsUceAdapter.ERROR_FORBIDDEN, retryAfter);
            logw("requestCapabilities: The request is forbidden, errorCode=" + errorCode
                    + ", retryAfter=" + retryAfter);
            errorCode = (errorCode != null) ? errorCode : RcsUceAdapter.ERROR_FORBIDDEN;
            c.onError(errorCode, retryAfter);
            return;
        }

        // Trigger the capabilities request task
        logd("requestCapabilities: " + uriList.size());
        mRequestManager.sendCapabilityRequest(uriList, c);
        mRequestManager.sendCapabilityRequest(uriList, skipFromCache, c);
    }

    /**
@@ -466,9 +479,12 @@ public class UceController {

        // Check if UCE requests are forbidden by the network.
        if (mServerState.isRequestForbidden()) {
            Integer errorCode = mServerState.getForbiddenErrorCode();
            long retryAfter = mServerState.getRetryAfterMillis();
            logw("requestAvailability: The request is forbidden, retryAfter=" + retryAfter);
            c.onError(RcsUceAdapter.ERROR_FORBIDDEN, retryAfter);
            logw("requestCapabilities: The request is forbidden, errorCode=" + errorCode
                + ", retryAfter=" + retryAfter);
            errorCode = (errorCode != null) ? errorCode : RcsUceAdapter.ERROR_FORBIDDEN;
            c.onError(errorCode, retryAfter);
            return;
        }

@@ -482,6 +498,9 @@ public class UceController {
     */
    public void onRequestPublishCapabilitiesFromService(@StackPublishTriggerType int triggerType) {
        logd("onRequestPublishCapabilitiesFromService: " + triggerType);
        // Reset the forbidden status if the service requests to publish the device's capabilities
        mServerState.updateRequestForbidden(false, null, 0L);
        // Send the publish request.
        mPublishController.requestPublishCapabilitiesFromService(triggerType);
    }

@@ -504,14 +523,6 @@ public class UceController {
        mOptionsController.retrieveCapabilitiesForRemote(contactUri, remoteCapabilities, c);
    }

    /**
     * Update the Request forbidden state.
     */
    public void onRequestForbidden(boolean isForbidden, long retryAfterMillis) {
        logi("onRequestForbidden: forbidden=" + isForbidden + ", retry=" + retryAfterMillis);
        mServerState.forbidUceRequest(isForbidden, retryAfterMillis);
    }

    /**
     * Register a {@link PublishStateCallback} to receive the published state changed.
     */
@@ -557,34 +568,40 @@ public class UceController {
     */
    @VisibleForTesting
    public static class ServerState {
        // If UCE requests are forbidden by the network.
        private boolean mIsForbidden;
        private Integer mForbiddenErrorCode;

        // The timestamp when the network allows the UCE requests. This value may be null if the
        // network doesn't specified any retryAfter info.
        private Instant mAllowedTimestamp;

        private final Object mNetworkStateLock = new Object();
        private final Object mServerStateLock = new Object();

        public ServerState() {
            mIsForbidden = false;
            mForbiddenErrorCode = null;
            mAllowedTimestamp = null;
        }

        public void forbidUceRequest(boolean isForbidden, long retryAfterMillis) {
            synchronized (mNetworkStateLock) {
        public void updateRequestForbidden(boolean isForbidden, @Nullable Integer errorCode,
                long retryAfterMillis) {
            synchronized (mServerStateLock) {
                mIsForbidden = isForbidden;
                if (!mIsForbidden) {
                    mForbiddenErrorCode = null;
                    mAllowedTimestamp = null;
                } else {
                    mForbiddenErrorCode =
                        (errorCode == null) ? RcsUceAdapter.ERROR_FORBIDDEN : errorCode;
                    mAllowedTimestamp = Instant.now().plus(retryAfterMillis, ChronoUnit.MILLIS);
                }
                Log.d(LOG_TAG, "forbidUceRequest: " + mIsForbidden + ",time=" + mAllowedTimestamp);
                Log.i(LOG_TAG, "updateRequestForbidden: isForbidden=" + mIsForbidden
                        + ", errorCode=" + mForbiddenErrorCode + ", time=" + mAllowedTimestamp);
            }
        }

        public boolean isRequestForbidden() {
            synchronized (mNetworkStateLock) {
            synchronized (mServerStateLock) {
                if (mIsForbidden && mAllowedTimestamp != null) {
                    return Instant.now().isBefore(mAllowedTimestamp);
                }
@@ -592,8 +609,17 @@ public class UceController {
            }
        }

        public @Nullable Integer getForbiddenErrorCode() {
            synchronized (mServerStateLock) {
                if (!mIsForbidden) {
                    return null;
                }
                return mForbiddenErrorCode;
            }
        }

        public long getRetryAfterMillis() {
            synchronized (mNetworkStateLock) {
            synchronized (mServerStateLock) {
                if (!mIsForbidden || mAllowedTimestamp == null) {
                    return 0L;
                }
+37 −18
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsContactUceCapability.OptionsBuilder;
import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;
import android.telephony.ims.RcsContactUceCapability.RcsUcsCapabilityBuilder;
import android.text.TextUtils;
import android.util.Log;

@@ -201,7 +200,7 @@ public class EabControllerImpl implements EabController {

    private EabCapabilityResult generateEabResult(Uri contactUri,
            Predicate<Cursor> isExpiredMethod) {
        RcsContactUceCapability.RcsUcsCapabilityBuilder builder = null;
        RcsUceCapabilityBuilderWrapper builder = null;
        EabCapabilityResult result;

        // query EAB provider
@@ -231,9 +230,18 @@ public class EabControllerImpl implements EabController {
                        EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE,
                        null);
            } else {
                if (builder.getMechanism() == CAPABILITY_MECHANISM_PRESENCE) {
                    PresenceBuilder presenceBuilder = builder.getPresenceBuilder();
                    result = new EabCapabilityResult(contactUri,
                            EabCapabilityResult.EAB_QUERY_SUCCESSFUL,
                        builder.build());
                            presenceBuilder.build());
                } else {
                    OptionsBuilder optionsBuilder = builder.getOptionsBuilder();
                    result = new EabCapabilityResult(contactUri,
                            EabCapabilityResult.EAB_QUERY_SUCCESSFUL,
                            optionsBuilder.build());
                }

            }
        } else {
            result = new EabCapabilityResult(contactUri,
@@ -242,26 +250,36 @@ public class EabControllerImpl implements EabController {
        return result;
    }

    private void updateCapability(Uri contactUri, Cursor cursor, RcsUcsCapabilityBuilder builder) {
        if (builder instanceof PresenceBuilder) {
            ((PresenceBuilder) builder).addCapabilityTuple(createPresenceTuple(contactUri, cursor));
    private void updateCapability(Uri contactUri, Cursor cursor,
                RcsUceCapabilityBuilderWrapper builderWrapper) {
        if (builderWrapper.getMechanism() == CAPABILITY_MECHANISM_PRESENCE) {
            PresenceBuilder builder = builderWrapper.getPresenceBuilder();
            if (builder != null) {
                builder.addCapabilityTuple(createPresenceTuple(contactUri, cursor));
            }
        } else {
            ((OptionsBuilder) builder).addFeatureTag(createOptionTuple(cursor));
            OptionsBuilder builder = builderWrapper.getOptionsBuilder();
            if (builder != null) {
                builder.addFeatureTag(createOptionTuple(cursor));
            }
        }
    }

    private RcsUcsCapabilityBuilder createNewBuilder(Uri contactUri, Cursor cursor) {
    private RcsUceCapabilityBuilderWrapper createNewBuilder(Uri contactUri, Cursor cursor) {
        int mechanism = getIntValue(cursor, EabProvider.EabCommonColumns.MECHANISM);
        int result = getIntValue(cursor, EabProvider.EabCommonColumns.REQUEST_RESULT);
        RcsUceCapabilityBuilderWrapper builderWrapper =
                new RcsUceCapabilityBuilderWrapper(mechanism);

        if (mechanism == CAPABILITY_MECHANISM_PRESENCE) {
            PresenceBuilder builder = new PresenceBuilder(
                    contactUri, CAPABILITY_MECHANISM_PRESENCE, result);
            builder.addCapabilityTuple(createPresenceTuple(contactUri, cursor));
            return builder;
            builderWrapper.setPresenceBuilder(builder);
        } else {
            return new OptionsBuilder(contactUri);
            builderWrapper.setOptionsBuilder(new OptionsBuilder(contactUri));
        }
        return builderWrapper;
    }

    private String createOptionTuple(Cursor cursor) {
@@ -323,16 +341,16 @@ public class EabControllerImpl implements EabController {
        RcsContactPresenceTuple.Builder rcsContactPresenceTupleBuilder =
                new RcsContactPresenceTuple.Builder(status, serviceId, version);
        if (description != null) {
            rcsContactPresenceTupleBuilder.addDescription(description);
            rcsContactPresenceTupleBuilder.setServiceDescription(description);
        }
        if (contactUri != null) {
            rcsContactPresenceTupleBuilder.addContactUri(contactUri);
            rcsContactPresenceTupleBuilder.setContactUri(contactUri);
        }
        if (serviceCapabilities != null) {
            rcsContactPresenceTupleBuilder.addServiceCapabilities(serviceCapabilities);
            rcsContactPresenceTupleBuilder.setServiceCapabilities(serviceCapabilities);
        }
        if (timeStamp != null) {
            rcsContactPresenceTupleBuilder.addTimeStamp(timeStamp);
            rcsContactPresenceTupleBuilder.setTimestamp(timeStamp);
        }

        return rcsContactPresenceTupleBuilder.build();
@@ -459,9 +477,10 @@ public class EabControllerImpl implements EabController {
        int commonId = Integer.valueOf(result.getLastPathSegment());
        Log.d(TAG, "Insert into common table. Id: " + commonId);

        ContentValues[] presenceContent = new ContentValues[capability.getPresenceTuples().size()];
        ContentValues[] presenceContent =
                new ContentValues[capability.getCapabilityTuples().size()];
        for (int i = 0; i < presenceContent.length; i++) {
            RcsContactPresenceTuple tuple = capability.getPresenceTuples().get(i);
            RcsContactPresenceTuple tuple = capability.getCapabilityTuples().get(i);

            // Create new ServiceCapabilities
            ServiceCapabilities serviceCapabilities = tuple.getServiceCapabilities();
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.eab;

import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;

import com.android.ims.rcs.uce.eab.EabProvider.ContactColumns;
import com.android.ims.rcs.uce.eab.EabProvider.EabCommonColumns;
import com.android.ims.rcs.uce.eab.EabProvider.PresenceTupleColumns;
import com.android.ims.rcs.uce.util.UceUtils;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * The util to modify the EAB database.
 */
public class EabUtil {

    private static final String LOG_TAG = UceUtils.getLogPrefix() + "EabUtil";

    /**
     * Remove the given EAB contacts from the EAB database.
     */
    public static int removeContactFromEab(int subId, String contacts, Context context) {
        if (TextUtils.isEmpty(contacts)) {
            return -1;
        }
        List<String> contactList = Arrays.stream(contacts.split(",")).collect(Collectors.toList());
        if (contactList == null || contactList.isEmpty()) {
            return -1;
        }
        int count = 0;
        for (String contact : contactList) {
            int contactId = getEabContactId(contact, context);
            if (contactId == -1) {
                continue;
            }
            int commonId = getEabCommonId(contactId, context);
            count += removeContactCapabilities(contactId, commonId, context);
        }
        return count;
    }

    private static int getEabContactId(String contactNumber, Context context) {
        int contactId = -1;
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(
                    EabProvider.CONTACT_URI,
                    new String[] { EabProvider.EabCommonColumns._ID },
                    EabProvider.ContactColumns.PHONE_NUMBER + "=?",
                    new String[] { contactNumber }, null);
            if (cursor != null && cursor.moveToFirst()) {
                contactId = cursor.getInt(cursor.getColumnIndex(EabProvider.ContactColumns._ID));
            }
        } catch (Exception e) {
            Log.w(LOG_TAG, "getEabContactId exception " + e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return contactId;
    }

    private static int getEabCommonId(int contactId, Context context) {
        int commonId = -1;
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(
                    EabProvider.COMMON_URI,
                    new String[] { EabProvider.EabCommonColumns._ID },
                    EabProvider.EabCommonColumns.EAB_CONTACT_ID + "=?",
                    new String[] { String.valueOf(contactId) }, null);
            if (cursor != null && cursor.moveToFirst()) {
                commonId = cursor.getInt(cursor.getColumnIndex(EabProvider.EabCommonColumns._ID));
            }
        } catch (Exception e) {
            Log.w(LOG_TAG, "getEabCommonId exception " + e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return commonId;
    }

    private static int removeContactCapabilities(int contactId, int commonId, Context context) {
        int count = 0;
        count = context.getContentResolver().delete(EabProvider.PRESENCE_URI,
                PresenceTupleColumns.EAB_COMMON_ID + "=?", new String[]{String.valueOf(commonId)});
        context.getContentResolver().delete(EabProvider.COMMON_URI,
                EabCommonColumns.EAB_CONTACT_ID + "=?", new String[]{String.valueOf(contactId)});
        context.getContentResolver().delete(EabProvider.CONTACT_URI,
                ContactColumns._ID + "=?", new String[]{String.valueOf(contactId)});
        return count;
    }
}
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.eab;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.telephony.ims.RcsContactUceCapability.OptionsBuilder;
import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;

/**
 * The wrapper class of the PresenceBuilder and the OptionsBuilder.
 */
public class RcsUceCapabilityBuilderWrapper {
    private final int mMechanism;
    private PresenceBuilder mPresenceBuilder;
    private OptionsBuilder mOptionsBuilder;

    public RcsUceCapabilityBuilderWrapper(int mechanism) {
        mMechanism = mechanism;
    }

    public int getMechanism() {
        return mMechanism;
    }

    public void setPresenceBuilder(@NonNull PresenceBuilder presenceBuilder) {
        mPresenceBuilder = presenceBuilder;
    }

    public @Nullable PresenceBuilder getPresenceBuilder() {
        return mPresenceBuilder;
    }

    public void setOptionsBuilder(@NonNull OptionsBuilder optionsBuilder) {
        mOptionsBuilder = optionsBuilder;
    }

    public @Nullable OptionsBuilder getOptionsBuilder() {
        return mOptionsBuilder;
    }
}
+35 −28

File changed.

Preview size limit exceeded, changes collapsed.

Loading