1 /* 2 * Copyright (c) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ims.rcs.uce.request; 18 19 import android.annotation.NonNull; 20 import android.net.Uri; 21 import android.os.RemoteException; 22 import android.telephony.CarrierConfigManager; 23 import android.telephony.ims.RcsContactTerminatedReason; 24 import android.telephony.ims.RcsContactUceCapability; 25 import android.telephony.ims.RcsUceAdapter; 26 import android.telephony.ims.SipDetails; 27 import android.telephony.ims.aidl.ISubscribeResponseCallback; 28 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; 29 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode; 30 31 import com.android.ims.rcs.uce.eab.EabCapabilityResult; 32 import com.android.ims.rcs.uce.presence.pidfparser.PidfParser; 33 import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils; 34 import com.android.ims.rcs.uce.presence.pidfparser.RcsContactUceCapabilityWrapper; 35 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController; 36 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.telephony.flags.FeatureFlags; 39 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Objects; 44 import java.util.stream.Collectors; 45 46 /** 47 * The UceRequest to request the capabilities when the presence mechanism is supported by the 48 * network. 49 */ 50 public class SubscribeRequest extends CapabilityRequest { 51 public static final int MAX_RETRY_COUNT = 1; 52 53 // The result callback of the capabilities request from IMS service. 54 private final ISubscribeResponseCallback mResponseCallback = 55 new ISubscribeResponseCallback.Stub() { 56 @Override 57 public void onCommandError(int code) { 58 SubscribeRequest.this.onCommandError(code); 59 } 60 @Override 61 public void onNetworkResponse(@NonNull SipDetails details) { 62 SubscribeRequest.this.onNetworkResponse(details); 63 } 64 @Override 65 public void onNotifyCapabilitiesUpdate(List<String> pidfXmls) { 66 SubscribeRequest.this.onCapabilitiesUpdate(pidfXmls); 67 } 68 @Override 69 public void onResourceTerminated(List<RcsContactTerminatedReason> terminatedList) { 70 SubscribeRequest.this.onResourceTerminated(terminatedList); 71 } 72 @Override 73 public void onTerminated(String reason, long retryAfterMillis) { 74 SubscribeRequest.this.onTerminated(reason, retryAfterMillis); 75 } 76 }; 77 78 private SubscribeController mSubscribeController; 79 private final FeatureFlags mFeatureFlags; 80 SubscribeRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, FeatureFlags featureFlags)81 public SubscribeRequest(int subId, @UceRequestType int requestType, 82 RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, 83 FeatureFlags featureFlags) { 84 super(subId, requestType, taskMgrCallback); 85 mSubscribeController = subscribeController; 86 mFeatureFlags = featureFlags; 87 logd("SubscribeRequest created"); 88 } 89 90 @VisibleForTesting SubscribeRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, CapabilityRequestResponse requestResponse, FeatureFlags featureFlags)91 public SubscribeRequest(int subId, @UceRequestType int requestType, 92 RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, 93 CapabilityRequestResponse requestResponse, FeatureFlags featureFlags) { 94 super(subId, requestType, taskMgrCallback, requestResponse); 95 mSubscribeController = subscribeController; 96 mFeatureFlags = featureFlags; 97 } 98 99 @Override onFinish()100 public void onFinish() { 101 mSubscribeController = null; 102 super.onFinish(); 103 logd("SubscribeRequest finish"); 104 } 105 106 @Override requestCapabilities(@onNull List<Uri> requestCapUris)107 public void requestCapabilities(@NonNull List<Uri> requestCapUris) { 108 SubscribeController subscribeController = mSubscribeController; 109 if (subscribeController == null) { 110 logw("requestCapabilities: request is finished"); 111 mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); 112 mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); 113 return; 114 } 115 116 logi("requestCapabilities: size=" + requestCapUris.size()); 117 try { 118 // Send the capabilities request. 119 subscribeController.requestCapabilities(requestCapUris, mResponseCallback); 120 // Setup the timeout timer. 121 setupRequestTimeoutTimer(); 122 } catch (RemoteException e) { 123 logw("requestCapabilities exception: " + e); 124 mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); 125 mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); 126 } 127 } 128 129 // Receive the command error callback which is triggered by ISubscribeResponseCallback. onCommandError(@ommandCode int cmdError)130 private void onCommandError(@CommandCode int cmdError) { 131 logd("onCommandError: error code=" + cmdError); 132 if (mIsFinished) { 133 logw("onCommandError: request is already finished"); 134 return; 135 } 136 137 if (mFeatureFlags.enableSipSubscribeRetry() 138 && cmdError == RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT 139 && isRetryEnabled()) { 140 int retryCount = getRetryCount(); 141 if (retryCount < MAX_RETRY_COUNT) { 142 CapabilityRequest request = new SubscribeRequest(mSubId, mRequestType, 143 mRequestManagerCallback, mSubscribeController, mFeatureFlags); 144 request.setContactUri(getContactUri()); 145 request.setRetryCount(retryCount + 1); 146 // Do not use the cached capability to retry 147 request.setSkipGettingFromCache(true); 148 149 mRequestManagerCallback.sendSubscribeRetryRequest(request); 150 logd("onCommandError: Retry subscribe request"); 151 } else { 152 logd("onCommandError: Reached max retry"); 153 } 154 } 155 156 mRequestResponse.setCommandError(cmdError); 157 mRequestManagerCallback.notifyCommandError(mCoordinatorId, mTaskId); 158 } 159 160 // Receive the network response callback which is triggered by ISubscribeResponseCallback. onNetworkResponse(@onNull SipDetails details)161 private void onNetworkResponse(@NonNull SipDetails details) { 162 logd("onNetworkResponse: sip details=" + details.toString()); 163 164 if (mIsFinished) { 165 logw("onNetworkResponse: request is already finished"); 166 return; 167 } 168 mRequestResponse.setSipDetails(details); 169 mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId); 170 } 171 172 // Receive the resource terminated callback which is triggered by ISubscribeResponseCallback. onResourceTerminated(List<RcsContactTerminatedReason> terminatedResource)173 private void onResourceTerminated(List<RcsContactTerminatedReason> terminatedResource) { 174 if (mIsFinished) { 175 logw("onResourceTerminated: request is already finished"); 176 return; 177 } 178 179 if (terminatedResource == null) { 180 logw("onResourceTerminated: the parameter is null"); 181 terminatedResource = Collections.emptyList(); 182 } 183 184 logd("onResourceTerminated: size=" + terminatedResource.size()); 185 186 // Add the terminated resource into the RequestResponse and notify the RequestManager 187 // to process the RcsContactUceCapabilities update. 188 mRequestResponse.addTerminatedResource(terminatedResource); 189 mRequestManagerCallback.notifyResourceTerminated(mCoordinatorId, mTaskId); 190 } 191 192 // Receive the capabilities update callback which is triggered by ISubscribeResponseCallback. onCapabilitiesUpdate(List<String> pidfXml)193 private void onCapabilitiesUpdate(List<String> pidfXml) { 194 if (mIsFinished) { 195 logw("onCapabilitiesUpdate: request is already finished"); 196 return; 197 } 198 199 if (pidfXml == null) { 200 logw("onCapabilitiesUpdate: The parameter is null"); 201 pidfXml = Collections.EMPTY_LIST; 202 } 203 204 // Convert from the pidf xml to the list of RcsContactUceCapabilityWrapper 205 List<RcsContactUceCapabilityWrapper> capabilityList = pidfXml.stream() 206 .map(pidf -> PidfParser.getRcsContactUceCapabilityWrapper(pidf)) 207 .filter(Objects::nonNull) 208 .collect(Collectors.toList()); 209 210 // When the given PIDF xml is empty, set the contacts who have not received the 211 // capabilities updated as non-RCS user. 212 List<RcsContactUceCapability> notReceivedCapabilityList = new ArrayList<>(); 213 if (capabilityList.isEmpty()) { 214 logd("onCapabilitiesUpdate: The capabilities list is empty, Set to non-RCS user."); 215 List<Uri> notReceiveCapUpdatedContactList = 216 mRequestResponse.getNotReceiveCapabilityUpdatedContact(); 217 notReceivedCapabilityList = notReceiveCapUpdatedContactList.stream() 218 .map(PidfParserUtils::getNotFoundContactCapabilities) 219 .filter(Objects::nonNull) 220 .collect(Collectors.toList()); 221 } 222 223 List<RcsContactUceCapability> updateCapabilityList = new ArrayList<>(); 224 List<Uri> malformedListWithEntityURI = new ArrayList<>(); 225 for (RcsContactUceCapabilityWrapper capability : capabilityList) { 226 if (!capability.isMalformed()) { 227 updateCapabilityList.add(capability.toRcsContactUceCapability()); 228 } else { 229 logw("onCapabilitiesUpdate: malformed capability was found and not saved."); 230 malformedListWithEntityURI.add(capability.getEntityUri()); 231 } 232 } 233 logd("onCapabilitiesUpdate: PIDF size=" + pidfXml.size() 234 + ", not received capability size=" + notReceivedCapabilityList.size() 235 + ", normal capability size=" + updateCapabilityList.size() 236 + ", malformed but entity uri is valid capability size=" 237 + malformedListWithEntityURI.size()); 238 239 for (RcsContactUceCapability emptyCapability : notReceivedCapabilityList) { 240 updateCapabilityList.add(emptyCapability); 241 } 242 243 // All tuples in received xml are malformed but entity uri is valid. 244 // The capability should be get from the DB and report it to callback. 245 List<EabCapabilityResult> cachedCapabilityList = 246 mRequestManagerCallback.getCapabilitiesFromCache(malformedListWithEntityURI); 247 for (EabCapabilityResult cacheEabCapability : cachedCapabilityList) { 248 RcsContactUceCapability cachedCapability = cacheEabCapability.getContactCapabilities(); 249 if (cachedCapability != null) { 250 updateCapabilityList.add(cachedCapability); 251 } 252 } 253 // Add these updated RcsContactUceCapability into the RequestResponse and notify 254 // the RequestManager to process the RcsContactUceCapabilities updated. 255 logd("onCapabilitiesUpdate: updatedCapability size=" + updateCapabilityList.size()); 256 mRequestResponse.addUpdatedCapabilities(updateCapabilityList); 257 mRequestManagerCallback.notifyCapabilitiesUpdated(mCoordinatorId, mTaskId); 258 } 259 260 // Receive the terminated callback which is triggered by ISubscribeResponseCallback. onTerminated(String reason, long retryAfterMillis)261 private void onTerminated(String reason, long retryAfterMillis) { 262 logd("onTerminated: reason=" + reason + ", retryAfter=" + retryAfterMillis); 263 if (mIsFinished) { 264 logd("onTerminated: This request is already finished"); 265 return; 266 } 267 mRequestResponse.setTerminated(reason, retryAfterMillis); 268 mRequestManagerCallback.notifyTerminated(mCoordinatorId, mTaskId); 269 } 270 271 @VisibleForTesting getResponseCallback()272 public ISubscribeResponseCallback getResponseCallback() { 273 return mResponseCallback; 274 } 275 } 276