/* * 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.request; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.RcsContactTerminatedReason; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.SipDetails; import android.telephony.ims.aidl.ISubscribeResponseCallback; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode; import com.android.ims.rcs.uce.eab.EabCapabilityResult; import com.android.ims.rcs.uce.presence.pidfparser.PidfParser; import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils; import com.android.ims.rcs.uce.presence.pidfparser.RcsContactUceCapabilityWrapper; import com.android.ims.rcs.uce.presence.subscribe.SubscribeController; import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * The UceRequest to request the capabilities when the presence mechanism is supported by the * network. */ public class SubscribeRequest extends CapabilityRequest { // The result callback of the capabilities request from IMS service. private final ISubscribeResponseCallback mResponseCallback = new ISubscribeResponseCallback.Stub() { @Override public void onCommandError(int code) { SubscribeRequest.this.onCommandError(code); } @Override public void onNetworkResponse(@NonNull SipDetails details) { SubscribeRequest.this.onNetworkResponse(details); } @Override public void onNotifyCapabilitiesUpdate(List pidfXmls) { SubscribeRequest.this.onCapabilitiesUpdate(pidfXmls); } @Override public void onResourceTerminated(List terminatedList) { SubscribeRequest.this.onResourceTerminated(terminatedList); } @Override public void onTerminated(String reason, long retryAfterMillis) { SubscribeRequest.this.onTerminated(reason, retryAfterMillis); } }; private SubscribeController mSubscribeController; public SubscribeRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, SubscribeController subscribeController) { super(subId, requestType, taskMgrCallback); mSubscribeController = subscribeController; logd("SubscribeRequest created"); } @VisibleForTesting public SubscribeRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, CapabilityRequestResponse requestResponse) { super(subId, requestType, taskMgrCallback, requestResponse); mSubscribeController = subscribeController; } @Override public void onFinish() { mSubscribeController = null; super.onFinish(); logd("SubscribeRequest finish"); } @Override public void requestCapabilities(@NonNull List requestCapUris) { SubscribeController subscribeController = mSubscribeController; if (subscribeController == null) { logw("requestCapabilities: request is finished"); mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); return; } logi("requestCapabilities: size=" + requestCapUris.size()); try { // Send the capabilities request. subscribeController.requestCapabilities(requestCapUris, mResponseCallback); // Setup the timeout timer. setupRequestTimeoutTimer(); } catch (RemoteException e) { logw("requestCapabilities exception: " + e); mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); } } // Receive the command error callback which is triggered by ISubscribeResponseCallback. private void onCommandError(@CommandCode int cmdError) { logd("onCommandError: error code=" + cmdError); if (mIsFinished) { logw("onCommandError: request is already finished"); return; } mRequestResponse.setCommandError(cmdError); mRequestManagerCallback.notifyCommandError(mCoordinatorId, mTaskId); } // Receive the network response callback which is triggered by ISubscribeResponseCallback. private void onNetworkResponse(@NonNull SipDetails details) { logd("onNetworkResponse: sip details=" + details.toString()); if (mIsFinished) { logw("onNetworkResponse: request is already finished"); return; } mRequestResponse.setSipDetails(details); mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId); } // Receive the resource terminated callback which is triggered by ISubscribeResponseCallback. private void onResourceTerminated(List terminatedResource) { if (mIsFinished) { logw("onResourceTerminated: request is already finished"); return; } if (terminatedResource == null) { logw("onResourceTerminated: the parameter is null"); terminatedResource = Collections.emptyList(); } logd("onResourceTerminated: size=" + terminatedResource.size()); // Add the terminated resource into the RequestResponse and notify the RequestManager // to process the RcsContactUceCapabilities update. mRequestResponse.addTerminatedResource(terminatedResource); mRequestManagerCallback.notifyResourceTerminated(mCoordinatorId, mTaskId); } // Receive the capabilities update callback which is triggered by ISubscribeResponseCallback. private void onCapabilitiesUpdate(List pidfXml) { if (mIsFinished) { logw("onCapabilitiesUpdate: request is already finished"); return; } if (pidfXml == null) { logw("onCapabilitiesUpdate: The parameter is null"); pidfXml = Collections.EMPTY_LIST; } // Convert from the pidf xml to the list of RcsContactUceCapabilityWrapper List capabilityList = pidfXml.stream() .map(pidf -> PidfParser.getRcsContactUceCapabilityWrapper(pidf)) .filter(Objects::nonNull) .collect(Collectors.toList()); // When the given PIDF xml is empty, set the contacts who have not received the // capabilities updated as non-RCS user. List notReceivedCapabilityList = new ArrayList<>(); if (capabilityList.isEmpty()) { logd("onCapabilitiesUpdate: The capabilities list is empty, Set to non-RCS user."); List notReceiveCapUpdatedContactList = mRequestResponse.getNotReceiveCapabilityUpdatedContact(); notReceivedCapabilityList = notReceiveCapUpdatedContactList.stream() .map(PidfParserUtils::getNotFoundContactCapabilities) .filter(Objects::nonNull) .collect(Collectors.toList()); } List updateCapabilityList = new ArrayList<>(); List malformedListWithEntityURI = new ArrayList<>(); for (RcsContactUceCapabilityWrapper capability : capabilityList) { if (!capability.isMalformed()) { updateCapabilityList.add(capability.toRcsContactUceCapability()); } else { logw("onCapabilitiesUpdate: malformed capability was found and not saved."); malformedListWithEntityURI.add(capability.getEntityUri()); } } logd("onCapabilitiesUpdate: PIDF size=" + pidfXml.size() + ", not received capability size=" + notReceivedCapabilityList.size() + ", normal capability size=" + updateCapabilityList.size() + ", malformed but entity uri is valid capability size=" + malformedListWithEntityURI.size()); for (RcsContactUceCapability emptyCapability : notReceivedCapabilityList) { updateCapabilityList.add(emptyCapability); } // All tuples in received xml are malformed but entity uri is valid. // The capability should be get from the DB and report it to callback. List cachedCapabilityList = mRequestManagerCallback.getCapabilitiesFromCache(malformedListWithEntityURI); for (EabCapabilityResult cacheEabCapability : cachedCapabilityList) { RcsContactUceCapability cachedCapability = cacheEabCapability.getContactCapabilities(); if (cachedCapability != null) { updateCapabilityList.add(cachedCapability); } } // Add these updated RcsContactUceCapability into the RequestResponse and notify // the RequestManager to process the RcsContactUceCapabilities updated. logd("onCapabilitiesUpdate: updatedCapability size=" + updateCapabilityList.size()); mRequestResponse.addUpdatedCapabilities(updateCapabilityList); mRequestManagerCallback.notifyCapabilitiesUpdated(mCoordinatorId, mTaskId); } // Receive the terminated callback which is triggered by ISubscribeResponseCallback. private void onTerminated(String reason, long retryAfterMillis) { logd("onTerminated: reason=" + reason + ", retryAfter=" + retryAfterMillis); if (mIsFinished) { logd("onTerminated: This request is already finished"); return; } mRequestResponse.setTerminated(reason, retryAfterMillis); mRequestManagerCallback.notifyTerminated(mCoordinatorId, mTaskId); } @VisibleForTesting public ISubscribeResponseCallback getResponseCallback() { return mResponseCallback; } }