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 android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_NETWORK;
20 
21 import android.annotation.NonNull;
22 import android.net.Uri;
23 import android.os.RemoteException;
24 import android.telephony.ims.RcsContactUceCapability;
25 import android.telephony.ims.RcsUceAdapter;
26 import android.telephony.ims.aidl.IOptionsResponseCallback;
27 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.CommandCode;
28 
29 import com.android.ims.rcs.uce.options.OptionsController;
30 import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
31 import com.android.ims.rcs.uce.util.NetworkSipCode;
32 import com.android.internal.annotations.VisibleForTesting;
33 
34 import java.util.Collections;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 
39 /**
40  * The UceRequest to request the capabilities when the OPTIONS mechanism is supported by the
41  * network.
42  */
43 public class OptionsRequest extends CapabilityRequest {
44 
45     // The result callback of the capabilities request from the IMS service.
46     private IOptionsResponseCallback mResponseCallback = new IOptionsResponseCallback.Stub() {
47         @Override
48         public void onCommandError(int code) {
49             OptionsRequest.this.onCommandError(code);
50         }
51 
52         @Override
53         public void onNetworkResponse(int sipCode, String reason, List<String> remoteCaps) {
54             OptionsRequest.this.onNetworkResponse(sipCode, reason, remoteCaps);
55         }
56     };
57 
58     private Uri mContactUri;
59     private OptionsController mOptionsController;
60 
OptionsRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, OptionsController optionsController)61     public OptionsRequest(int subId, @UceRequestType int requestType,
62             RequestManagerCallback taskMgrCallback, OptionsController optionsController) {
63         super(subId, requestType, taskMgrCallback);
64         mOptionsController = optionsController;
65         logd("OptionsRequest created");
66     }
67 
68     @VisibleForTesting
OptionsRequest(int subId, @UceRequestType int requestType, RequestManagerCallback taskMgrCallback, OptionsController optionsController, CapabilityRequestResponse requestResponse)69     public OptionsRequest(int subId, @UceRequestType int requestType,
70             RequestManagerCallback taskMgrCallback, OptionsController optionsController,
71             CapabilityRequestResponse requestResponse) {
72         super(subId, requestType, taskMgrCallback, requestResponse);
73         mOptionsController = optionsController;
74     }
75 
76     @Override
onFinish()77     public void onFinish() {
78         mOptionsController = null;
79         super.onFinish();
80         logd("OptionsRequest finish");
81     }
82 
83     @Override
requestCapabilities(@onNull List<Uri> requestCapUris)84     public void requestCapabilities(@NonNull List<Uri> requestCapUris) {
85         OptionsController optionsController = mOptionsController;
86         if (optionsController == null) {
87             logw("requestCapabilities: request is finished");
88             mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
89             mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId);
90             return;
91         }
92 
93         // Get the device's capabilities to send to the remote client.
94         RcsContactUceCapability deviceCap = mRequestManagerCallback.getDeviceCapabilities(
95                 RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS);
96         if (deviceCap == null) {
97             logw("requestCapabilities: Cannot get device capabilities");
98             mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
99             mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId);
100             return;
101         }
102 
103         mContactUri = requestCapUris.get(0);
104         Set<String> featureTags = deviceCap.getFeatureTags();
105 
106         logi("requestCapabilities: featureTag size=" + featureTags.size());
107         try {
108             // Send the capabilities request.
109             optionsController.sendCapabilitiesRequest(mContactUri, featureTags, mResponseCallback);
110             // Setup the timeout timer.
111             setupRequestTimeoutTimer();
112         } catch (RemoteException e) {
113             logw("requestCapabilities exception: " + e);
114             mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
115             mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId);
116         }
117     }
118 
119     // Receive the command error callback which is triggered by IOptionsResponseCallback.
onCommandError(@ommandCode int cmdError)120     private void onCommandError(@CommandCode int cmdError) {
121         logd("onCommandError: error code=" + cmdError);
122         if (mIsFinished) {
123             logw("onCommandError: The request is already finished");
124             return;
125         }
126         mRequestResponse.setCommandError(cmdError);
127         mRequestManagerCallback.notifyCommandError(mCoordinatorId, mTaskId);
128     }
129 
130     // Receive the network response callback which is triggered by IOptionsResponseCallback.
onNetworkResponse(int sipCode, String reason, List<String> remoteCaps)131     private void onNetworkResponse(int sipCode, String reason, List<String> remoteCaps) {
132         logd("onNetworkResponse: sipCode=" + sipCode + ", reason=" + reason
133                 + ", remoteCap size=" + ((remoteCaps == null) ? "null" : remoteCaps.size()));
134         if (mIsFinished) {
135             logw("onNetworkResponse: The request is already finished");
136             return;
137         }
138 
139         if (remoteCaps == null) {
140             remoteCaps = Collections.EMPTY_LIST;
141         }
142 
143         // Set the all the results to the request response.
144         mRequestResponse.setNetworkResponseCode(sipCode, reason);
145         mRequestResponse.setRemoteCapabilities(new HashSet<>(remoteCaps));
146         RcsContactUceCapability contactCapabilities = getContactCapabilities(mContactUri, sipCode,
147                 new HashSet<>(remoteCaps));
148         mRequestResponse.addUpdatedCapabilities(Collections.singletonList(contactCapabilities));
149 
150         // Notify that the network response is received.
151         mRequestManagerCallback.notifyNetworkResponse(mCoordinatorId, mTaskId);
152     }
153 
154     /**
155      * Convert the remote capabilities from string list type to RcsContactUceCapability.
156      */
getContactCapabilities(Uri contact, int sipCode, Set<String> featureTags)157     private RcsContactUceCapability getContactCapabilities(Uri contact, int sipCode,
158             Set<String> featureTags) {
159         int requestResult = RcsContactUceCapability.REQUEST_RESULT_FOUND;
160         if (!mRequestResponse.isNetworkResponseOK()) {
161             switch (sipCode) {
162                 case NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT:
163                     // Intentional fallthrough
164                 case NetworkSipCode.SIP_CODE_TEMPORARILY_UNAVAILABLE:
165                     requestResult = RcsContactUceCapability.REQUEST_RESULT_NOT_ONLINE;
166                     break;
167                 case NetworkSipCode.SIP_CODE_NOT_FOUND:
168                     // Intentional fallthrough
169                 case NetworkSipCode.SIP_CODE_DOES_NOT_EXIST_ANYWHERE:
170                     requestResult = RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND;
171                     break;
172                 default:
173                     requestResult = RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND;
174                     break;
175             }
176         }
177 
178         RcsContactUceCapability.OptionsBuilder optionsBuilder
179                 = new RcsContactUceCapability.OptionsBuilder(contact, SOURCE_TYPE_NETWORK);
180         optionsBuilder.setRequestResult(requestResult);
181         optionsBuilder.addFeatureTags(featureTags);
182         return optionsBuilder.build();
183     }
184 
185     @VisibleForTesting
getResponseCallback()186     public IOptionsResponseCallback getResponseCallback() {
187         return mResponseCallback;
188     }
189 }
190