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