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.ims.RcsContactTerminatedReason; 23 import android.telephony.ims.RcsContactUceCapability; 24 import android.telephony.ims.RcsUceAdapter; 25 import android.telephony.ims.aidl.ISubscribeResponseCallback; 26 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode; 27 28 import com.android.ims.rcs.uce.eab.EabCapabilityResult; 29 import com.android.ims.rcs.uce.presence.pidfparser.PidfParser; 30 import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils; 31 import com.android.ims.rcs.uce.presence.pidfparser.RcsContactUceCapabilityWrapper; 32 import com.android.ims.rcs.uce.presence.subscribe.SubscribeController; 33 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; 34 import com.android.internal.annotations.VisibleForTesting; 35 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.Objects; 40 import java.util.stream.Collectors; 41 42 /** 43 * The UceRequest to request the capabilities when the presence mechanism is supported by the 44 * network. 45 */ 46 public class SubscribeRequest extends CapabilityRequest { 47 48 // The result callback of the capabilities request from IMS service. 49 private final ISubscribeResponseCallback mResponseCallback = 50 new ISubscribeResponseCallback.Stub() { 51 @Override 52 public void onCommandError(int code) { 53 SubscribeRequest.this.onCommandError(code); 54 } 55 @Override 56 public void onNetworkResponse(int code, String reason) { 57 SubscribeRequest.this.onNetworkResponse(code, reason); 58 } 59 @Override 60 public void onNetworkRespHeader(int code, String reasonPhrase, 61 int reasonHeaderCause, String reasonHeaderText) { 62 SubscribeRequest.this.onNetworkResponse(code, reasonPhrase, reasonHeaderCause, 63 reasonHeaderText); 64 } 65 @Override 66 public void onNotifyCapabilitiesUpdate(List<String> pidfXmls) { 67 SubscribeRequest.this.onCapabilitiesUpdate(pidfXmls); 68 } 69 @Override 70 public void onResourceTerminated(List<RcsContactTerminatedReason> terminatedList) { 71 SubscribeRequest.this.onResourceTerminated(terminatedList); 72 } 73 @Override 74 public void onTerminated(String reason, long retryAfterMillis) { 75 SubscribeRequest.this.onTerminated(reason, retryAfterMillis); 76 } 77 }; 78 79 private SubscribeController mSubscribeController; 80 SubscribeRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, SubscribeController subscribeController)81 public SubscribeRequest(int subId, @UceRequestType int requestType, 82 RequestManagerCallback taskMgrCallback, SubscribeController subscribeController) { 83 super(subId, requestType, taskMgrCallback); 84 mSubscribeController = subscribeController; 85 logd("SubscribeRequest created"); 86 } 87 88 @VisibleForTesting SubscribeRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, CapabilityRequestResponse requestResponse)89 public SubscribeRequest(int subId, @UceRequestType int requestType, 90 RequestManagerCallback taskMgrCallback, SubscribeController subscribeController, 91 CapabilityRequestResponse requestResponse) { 92 super(subId, requestType, taskMgrCallback, requestResponse); 93 mSubscribeController = subscribeController; 94 } 95 96 @Override onFinish()97 public void onFinish() { 98 mSubscribeController = null; 99 super.onFinish(); 100 logd("SubscribeRequest finish"); 101 } 102 103 @Override requestCapabilities(@onNull List<Uri> requestCapUris)104 public void requestCapabilities(@NonNull List<Uri> requestCapUris) { 105 SubscribeController subscribeController = mSubscribeController; 106 if (subscribeController == null) { 107 logw("requestCapabilities: request is finished"); 108 mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); 109 mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); 110 return; 111 } 112 113 logi("requestCapabilities: size=" + requestCapUris.size()); 114 try { 115 // Send the capabilities request. 116 subscribeController.requestCapabilities(requestCapUris, mResponseCallback); 117 // Setup the timeout timer. 118 setupRequestTimeoutTimer(); 119 } catch (RemoteException e) { 120 logw("requestCapabilities exception: " + e); 121 mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE); 122 mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId); 123 } 124 } 125 126 // Receive the command error callback which is triggered by ISubscribeResponseCallback. onCommandError(@ommandCode int cmdError)127 private void onCommandError(@CommandCode int cmdError) { 128 logd("onCommandError: error code=" + cmdError); 129 if (mIsFinished) { 130 logw("onCommandError: request is already finished"); 131 return; 132 } 133 mRequestResponse.setCommandError(cmdError); 134 mRequestManagerCallback.notifyCommandError(mCoordinatorId, mTaskId); 135 } 136 137 // Receive the network response callback which is triggered by ISubscribeResponseCallback. onNetworkResponse(int sipCode, String reason)138 private void onNetworkResponse(int sipCode, String reason) { 139 logd("onNetworkResponse: code=" + sipCode + ", reason=" + reason); 140 if (mIsFinished) { 141 logw("onNetworkResponse: request is already finished"); 142 return; 143 } 144 mRequestResponse.setNetworkResponseCode(sipCode, reason); 145 mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId); 146 } 147 148 // Receive the network response callback which is triggered by ISubscribeResponseCallback. onNetworkResponse(int sipCode, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText)149 private void onNetworkResponse(int sipCode, String reasonPhrase, 150 int reasonHeaderCause, String reasonHeaderText) { 151 logd("onNetworkResponse: code=" + sipCode + ", reasonPhrase=" + reasonPhrase + 152 ", reasonHeaderCause=" + reasonHeaderCause + 153 ", reasonHeaderText=" + reasonHeaderText); 154 if (mIsFinished) { 155 logw("onNetworkResponse: request is already finished"); 156 return; 157 } 158 mRequestResponse.setNetworkResponseCode(sipCode, reasonPhrase, reasonHeaderCause, 159 reasonHeaderText); 160 mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId); 161 } 162 163 // Receive the resource terminated callback which is triggered by ISubscribeResponseCallback. onResourceTerminated(List<RcsContactTerminatedReason> terminatedResource)164 private void onResourceTerminated(List<RcsContactTerminatedReason> terminatedResource) { 165 if (mIsFinished) { 166 logw("onResourceTerminated: request is already finished"); 167 return; 168 } 169 170 if (terminatedResource == null) { 171 logw("onResourceTerminated: the parameter is null"); 172 terminatedResource = Collections.emptyList(); 173 } 174 175 logd("onResourceTerminated: size=" + terminatedResource.size()); 176 177 // Add the terminated resource into the RequestResponse and notify the RequestManager 178 // to process the RcsContactUceCapabilities update. 179 mRequestResponse.addTerminatedResource(terminatedResource); 180 mRequestManagerCallback.notifyResourceTerminated(mCoordinatorId, mTaskId); 181 } 182 183 // Receive the capabilities update callback which is triggered by ISubscribeResponseCallback. onCapabilitiesUpdate(List<String> pidfXml)184 private void onCapabilitiesUpdate(List<String> pidfXml) { 185 if (mIsFinished) { 186 logw("onCapabilitiesUpdate: request is already finished"); 187 return; 188 } 189 190 if (pidfXml == null) { 191 logw("onCapabilitiesUpdate: The parameter is null"); 192 pidfXml = Collections.EMPTY_LIST; 193 } 194 195 // Convert from the pidf xml to the list of RcsContactUceCapabilityWrapper 196 List<RcsContactUceCapabilityWrapper> capabilityList = pidfXml.stream() 197 .map(pidf -> PidfParser.getRcsContactUceCapabilityWrapper(pidf)) 198 .filter(Objects::nonNull) 199 .collect(Collectors.toList()); 200 201 // When the given PIDF xml is empty, set the contacts who have not received the 202 // capabilities updated as non-RCS user. 203 List<RcsContactUceCapability> notReceivedCapabilityList = new ArrayList<>(); 204 if (capabilityList.isEmpty()) { 205 logd("onCapabilitiesUpdate: The capabilities list is empty, Set to non-RCS user."); 206 List<Uri> notReceiveCapUpdatedContactList = 207 mRequestResponse.getNotReceiveCapabilityUpdatedContact(); 208 notReceivedCapabilityList = notReceiveCapUpdatedContactList.stream() 209 .map(PidfParserUtils::getNotFoundContactCapabilities) 210 .filter(Objects::nonNull) 211 .collect(Collectors.toList()); 212 } 213 214 List<RcsContactUceCapability> updateCapabilityList = new ArrayList<>(); 215 List<Uri> malformedListWithEntityURI = new ArrayList<>(); 216 for (RcsContactUceCapabilityWrapper capability : capabilityList) { 217 if (!capability.isMalformed()) { 218 updateCapabilityList.add(capability.toRcsContactUceCapability()); 219 } else { 220 logw("onCapabilitiesUpdate: malformed capability was found and not saved."); 221 malformedListWithEntityURI.add(capability.getEntityUri()); 222 } 223 } 224 logd("onCapabilitiesUpdate: PIDF size=" + pidfXml.size() 225 + ", not received capability size=" + notReceivedCapabilityList.size() 226 + ", normal capability size=" + updateCapabilityList.size() 227 + ", malformed but entity uri is valid capability size=" 228 + malformedListWithEntityURI.size()); 229 230 for (RcsContactUceCapability emptyCapability : notReceivedCapabilityList) { 231 updateCapabilityList.add(emptyCapability); 232 } 233 234 // All tuples in received xml are malformed but entity uri is valid. 235 // The capability should be get from the DB and report it to callback. 236 List<EabCapabilityResult> cachedCapabilityList = 237 mRequestManagerCallback.getCapabilitiesFromCache(malformedListWithEntityURI); 238 for (EabCapabilityResult cacheEabCapability : cachedCapabilityList) { 239 RcsContactUceCapability cachedCapability = cacheEabCapability.getContactCapabilities(); 240 if (cachedCapability != null) { 241 updateCapabilityList.add(cachedCapability); 242 } 243 } 244 // Add these updated RcsContactUceCapability into the RequestResponse and notify 245 // the RequestManager to process the RcsContactUceCapabilities updated. 246 logd("onCapabilitiesUpdate: updatedCapability size=" + updateCapabilityList.size()); 247 mRequestResponse.addUpdatedCapabilities(updateCapabilityList); 248 mRequestManagerCallback.notifyCapabilitiesUpdated(mCoordinatorId, mTaskId); 249 } 250 251 // Receive the terminated callback which is triggered by ISubscribeResponseCallback. onTerminated(String reason, long retryAfterMillis)252 private void onTerminated(String reason, long retryAfterMillis) { 253 logd("onTerminated: reason=" + reason + ", retryAfter=" + retryAfterMillis); 254 if (mIsFinished) { 255 logd("onTerminated: This request is already finished"); 256 return; 257 } 258 mRequestResponse.setTerminated(reason, retryAfterMillis); 259 mRequestManagerCallback.notifyTerminated(mCoordinatorId, mTaskId); 260 } 261 262 @VisibleForTesting getResponseCallback()263 public ISubscribeResponseCallback getResponseCallback() { 264 return mResponseCallback; 265 } 266 } 267