1 /* 2 * Copyright (c) 2021 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 static com.android.ims.rcs.uce.util.NetworkSipCode.SIP_CODE_SERVER_INTERNAL_ERROR; 20 import static com.android.ims.rcs.uce.util.NetworkSipCode.SIP_SERVICE_UNAVAILABLE; 21 22 import android.os.RemoteException; 23 import android.telephony.ims.RcsContactUceCapability; 24 import android.telephony.ims.aidl.IOptionsRequestCallback; 25 26 import com.android.ims.rcs.uce.request.RemoteOptionsRequest.RemoteOptResponse; 27 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback; 28 import com.android.ims.rcs.uce.UceStatsWriter; 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import java.util.Collection; 32 33 /** 34 * Responsible for the manager the remote options request and triggering the callback to notify 35 * the result of the request. 36 */ 37 public class RemoteOptionsCoordinator extends UceRequestCoordinator { 38 /** 39 * The builder of the RemoteOptionsCoordinator. 40 */ 41 public static final class Builder { 42 RemoteOptionsCoordinator mRemoteOptionsCoordinator; 43 Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c)44 public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c) { 45 mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c, 46 UceStatsWriter.getInstance()); 47 } 48 @VisibleForTesting Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c, UceStatsWriter instance)49 public Builder(int subId, Collection<UceRequest> requests, RequestManagerCallback c, 50 UceStatsWriter instance) { 51 mRemoteOptionsCoordinator = new RemoteOptionsCoordinator(subId, requests, c, instance); 52 } 53 setOptionsRequestCallback(IOptionsRequestCallback callback)54 public Builder setOptionsRequestCallback(IOptionsRequestCallback callback) { 55 mRemoteOptionsCoordinator.setOptionsRequestCallback(callback); 56 return this; 57 } 58 build()59 public RemoteOptionsCoordinator build() { 60 return mRemoteOptionsCoordinator; 61 } 62 } 63 64 /** 65 * Different request updated events will create different {@link RequestResult}. Define the 66 * interface to get the {@link RequestResult} instance according to the given task ID and 67 * {@link RemoteOptResponse}. 68 */ 69 @FunctionalInterface 70 private interface RequestResultCreator { createRequestResult(long taskId, RemoteOptResponse response)71 RequestResult createRequestResult(long taskId, RemoteOptResponse response); 72 } 73 74 // The RequestResult creator of the remote options response. 75 private static final RequestResultCreator sRemoteResponseCreator = (taskId, response) -> { 76 RcsContactUceCapability capability = response.getRcsContactCapability(); 77 if (capability != null) { 78 return RequestResult.createSuccessResult(taskId); 79 } else { 80 int errorCode = response.getErrorSipCode().orElse(SIP_CODE_SERVER_INTERNAL_ERROR); 81 return RequestResult.createFailedResult(taskId, errorCode, 0L); 82 } 83 }; 84 85 // The callback to notify the result of the remote options request. 86 private IOptionsRequestCallback mOptionsReqCallback; 87 88 private final UceStatsWriter mUceStatsWriter; 89 RemoteOptionsCoordinator(int subId, Collection<UceRequest> requests, RequestManagerCallback requestMgrCallback, UceStatsWriter instance)90 private RemoteOptionsCoordinator(int subId, Collection<UceRequest> requests, 91 RequestManagerCallback requestMgrCallback, UceStatsWriter instance) { 92 super(subId, requests, requestMgrCallback); 93 mUceStatsWriter = instance; 94 logd("RemoteOptionsCoordinator: created"); 95 } 96 setOptionsRequestCallback(IOptionsRequestCallback callback)97 public void setOptionsRequestCallback(IOptionsRequestCallback callback) { 98 mOptionsReqCallback = callback; 99 } 100 101 @Override onFinish()102 public void onFinish() { 103 logd("RemoteOptionsCoordinator: onFinish"); 104 mOptionsReqCallback = null; 105 super.onFinish(); 106 } 107 108 @Override onRequestUpdated(long taskId, int event)109 public void onRequestUpdated(long taskId, int event) { 110 if (mIsFinished) return; 111 RemoteOptionsRequest request = (RemoteOptionsRequest) getUceRequest(taskId); 112 if (request == null) { 113 logw("onRequestUpdated: Cannot find RemoteOptionsRequest taskId=" + taskId); 114 return; 115 } 116 117 logd("onRequestUpdated: taskId=" + taskId + ", event=" + REQUEST_EVENT_DESC.get(event)); 118 switch (event) { 119 case REQUEST_UPDATE_REMOTE_REQUEST_DONE: 120 handleRemoteRequestDone(request); 121 break; 122 default: 123 logw("onRequestUpdated: invalid event " + event); 124 break; 125 } 126 127 // End this instance if all the UceRequests in the coordinator are finished. 128 checkAndFinishRequestCoordinator(); 129 } 130 handleRemoteRequestDone(RemoteOptionsRequest request)131 private void handleRemoteRequestDone(RemoteOptionsRequest request) { 132 // Trigger the options request callback 133 RemoteOptResponse response = request.getRemoteOptResponse(); 134 RcsContactUceCapability capability = response.getRcsContactCapability(); 135 if (capability != null) { 136 boolean isNumberBlocked = response.isNumberBlocked(); 137 triggerOptionsReqCallback(capability, isNumberBlocked); 138 } else { 139 int errorCode = response.getErrorSipCode().orElse(SIP_CODE_SERVER_INTERNAL_ERROR); 140 String reason = response.getErrorReason().orElse(SIP_SERVICE_UNAVAILABLE); 141 triggerOptionsReqWithErrorCallback(errorCode, reason); 142 } 143 144 // Finish this request. 145 request.onFinish(); 146 147 // Remove this request from the activated collection and notify RequestManager. 148 Long taskId = request.getTaskId(); 149 RequestResult requestResult = sRemoteResponseCreator.createRequestResult(taskId, response); 150 moveRequestToFinishedCollection(taskId, requestResult); 151 } 152 triggerOptionsReqCallback(RcsContactUceCapability deviceCaps, boolean isRemoteNumberBlocked)153 private void triggerOptionsReqCallback(RcsContactUceCapability deviceCaps, 154 boolean isRemoteNumberBlocked) { 155 try { 156 logd("triggerOptionsReqCallback: start"); 157 mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.INCOMING_OPTION_EVENT, true, 0, 158 200); 159 160 mOptionsReqCallback.respondToCapabilityRequest(deviceCaps, isRemoteNumberBlocked); 161 } catch (RemoteException e) { 162 logw("triggerOptionsReqCallback exception: " + e); 163 } finally { 164 logd("triggerOptionsReqCallback: done"); 165 } 166 } 167 triggerOptionsReqWithErrorCallback(int errorCode, String reason)168 private void triggerOptionsReqWithErrorCallback(int errorCode, String reason) { 169 try { 170 logd("triggerOptionsReqWithErrorCallback: start"); 171 mUceStatsWriter.setUceEvent(mSubId, UceStatsWriter.INCOMING_OPTION_EVENT, true, 0, 172 errorCode); 173 174 mOptionsReqCallback.respondToCapabilityRequestWithError(errorCode, reason); 175 } catch (RemoteException e) { 176 logw("triggerOptionsReqWithErrorCallback exception: " + e); 177 } finally { 178 logd("triggerOptionsReqWithErrorCallback: done"); 179 } 180 } 181 checkAndFinishRequestCoordinator()182 private void checkAndFinishRequestCoordinator() { 183 synchronized (mCollectionLock) { 184 // Return because there are requests running. 185 if (!mActivatedRequests.isEmpty()) { 186 return; 187 } 188 // Notify UceRequestManager to remove this instance from the collection. 189 mRequestManagerCallback.notifyRequestCoordinatorFinished(mCoordinatorId); 190 logd("checkAndFinishRequestCoordinator: id=" + mCoordinatorId); 191 } 192 } 193 194 @VisibleForTesting getActivatedRequest()195 public Collection<UceRequest> getActivatedRequest() { 196 return mActivatedRequests.values(); 197 } 198 199 @VisibleForTesting getFinishedRequest()200 public Collection<RequestResult> getFinishedRequest() { 201 return mFinishedRequests.values(); 202 } 203 } 204