• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.presence.publish;
18 
19 import android.annotation.Nullable;
20 import android.telephony.ims.RcsUceAdapter;
21 import android.telephony.ims.aidl.IPublishResponseCallback;
22 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
23 import android.util.Log;
24 
25 import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
26 import com.android.ims.rcs.uce.util.NetworkSipCode;
27 import com.android.ims.rcs.uce.util.UceUtils;
28 
29 import java.time.Instant;
30 import java.util.Optional;
31 
32 /**
33  * Receiving the result callback of the publish request.
34  */
35 public class PublishRequestResponse {
36 
37     private static final String LOG_TAG = UceUtils.getLogPrefix() + "PublishRequestResp";
38 
39     private final long mTaskId;
40     private final String mPidfXml;
41     private volatile boolean mNeedRetry;
42     private volatile PublishControllerCallback mPublishCtrlCallback;
43 
44     private Optional<Integer> mCmdErrorCode;
45     private Optional<Integer> mNetworkRespSipCode;
46     private Optional<String> mReasonPhrase;
47     private Optional<Integer> mReasonHeaderCause;
48     private Optional<String> mReasonHeaderText;
49 
50     // The timestamp when receive the response from the network.
51     private Instant mResponseTimestamp;
52 
PublishRequestResponse(PublishControllerCallback publishCtrlCallback, long taskId, String pidfXml)53     public PublishRequestResponse(PublishControllerCallback publishCtrlCallback, long taskId,
54             String pidfXml) {
55         mTaskId = taskId;
56         mPidfXml = pidfXml;
57         mPublishCtrlCallback = publishCtrlCallback;
58         mCmdErrorCode = Optional.empty();
59         mNetworkRespSipCode = Optional.empty();
60         mReasonPhrase = Optional.empty();
61         mReasonHeaderCause = Optional.empty();
62         mReasonHeaderText = Optional.empty();
63     }
64 
65     // The result callback of the publish capability request.
66     private IPublishResponseCallback mResponseCallback = new IPublishResponseCallback.Stub() {
67         @Override
68         public void onCommandError(int code) {
69             PublishRequestResponse.this.onCommandError(code);
70         }
71 
72         @Override
73         public void onNetworkResponse(int code, String reason) {
74             PublishRequestResponse.this.onNetworkResponse(code, reason);
75         }
76 
77         @Override
78         public void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause,
79                 String reasonHeaderText) {
80             PublishRequestResponse.this.onNetworkResponse(code, reasonPhrase, reasonHeaderCause,
81                     reasonHeaderText);
82         }
83     };
84 
getResponseCallback()85     public IPublishResponseCallback getResponseCallback() {
86         return mResponseCallback;
87     }
88 
getTaskId()89     public long getTaskId() {
90         return mTaskId;
91     }
92 
93     /**
94      * Retrieve the command error code which received from the network.
95      */
getCmdErrorCode()96     public Optional<Integer> getCmdErrorCode() {
97         return mCmdErrorCode;
98     }
99 
100     /**
101      * Retrieve the network response sip code which received from the network.
102      */
getNetworkRespSipCode()103     public Optional<Integer> getNetworkRespSipCode() {
104         return mNetworkRespSipCode;
105     }
106 
107     /**
108      * Retrieve the reason phrase of the network response which received from the network.
109      */
getReasonPhrase()110     public Optional<String> getReasonPhrase() {
111         return mReasonPhrase;
112     }
113 
114     /**
115      * Retrieve the reason header from the network response.
116      */
getReasonHeaderCause()117     public Optional<Integer> getReasonHeaderCause() {
118         return mReasonHeaderCause;
119     }
120 
121     /**
122      * Retrieve the description of the reason header.
123      */
getReasonHeaderText()124     public Optional<String> getReasonHeaderText() {
125         return mReasonHeaderText;
126     }
127 
128     /**
129      * Retrieve the SIP code from the network response. It will get the value from the Reason
130      * Header first. If the ReasonHeader is not present, it will get the value from the Network
131      * response instead.
132      */
getResponseSipCode()133     public Optional<Integer> getResponseSipCode() {
134         return (mReasonHeaderCause.isPresent()) ? mReasonHeaderCause : mNetworkRespSipCode;
135     }
136 
137     /**
138      * Retrieve the REASON from the network response. It will get the value from the Reason Header
139      * first. If the ReasonHeader is not present, it will get the value from the Network response
140      * instead.
141      */
getResponseReason()142     public Optional<String> getResponseReason() {
143         return (mReasonHeaderText.isPresent()) ? mReasonHeaderText : mReasonPhrase;
144     }
145 
146     /**
147      * Get the timestamp of receiving the network response callback.
148      */
getResponseTimestamp()149     public @Nullable Instant getResponseTimestamp() {
150         return mResponseTimestamp;
151     }
152 
153     /**
154      * @return the PIDF XML sent during this request.
155      */
getPidfXml()156     public String getPidfXml() {
157         return mPidfXml;
158     }
159 
onDestroy()160     public void onDestroy() {
161         mPublishCtrlCallback = null;
162     }
163 
onCommandError(int errorCode)164     private void onCommandError(int errorCode) {
165         mResponseTimestamp = Instant.now();
166         mCmdErrorCode = Optional.of(errorCode);
167         updateRetryFlagByCommandError();
168 
169         PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
170         if (ctrlCallback != null) {
171             ctrlCallback.onRequestCommandError(this);
172         } else {
173             Log.d(LOG_TAG, "onCommandError: already destroyed. error code=" + errorCode);
174         }
175     }
176 
onNetworkResponse(int sipCode, String reason)177     private void onNetworkResponse(int sipCode, String reason) {
178         mResponseTimestamp = Instant.now();
179         mNetworkRespSipCode = Optional.of(sipCode);
180         mReasonPhrase = Optional.ofNullable(reason);
181         updateRetryFlagByNetworkResponse();
182 
183         PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
184         if (ctrlCallback != null) {
185             ctrlCallback.onRequestNetworkResp(this);
186         } else {
187             Log.d(LOG_TAG, "onNetworkResponse: already destroyed. sip code=" + sipCode);
188         }
189     }
190 
onNetworkResponse(int sipCode, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText)191     private void onNetworkResponse(int sipCode, String reasonPhrase, int reasonHeaderCause,
192             String reasonHeaderText) {
193         mResponseTimestamp = Instant.now();
194         mNetworkRespSipCode = Optional.of(sipCode);
195         mReasonPhrase = Optional.ofNullable(reasonPhrase);
196         mReasonHeaderCause = Optional.of(reasonHeaderCause);
197         mReasonHeaderText = Optional.ofNullable(reasonHeaderText);
198         updateRetryFlagByNetworkResponse();
199 
200         PublishControllerCallback ctrlCallback = mPublishCtrlCallback;
201         if (ctrlCallback != null) {
202             ctrlCallback.onRequestNetworkResp(this);
203         } else {
204             Log.d(LOG_TAG, "onNetworkResponse: already destroyed. sipCode=" + sipCode +
205                     ", reasonHeader=" + reasonHeaderCause);
206         }
207     }
208 
updateRetryFlagByCommandError()209     private void updateRetryFlagByCommandError() {
210         switch(getCmdErrorCode().orElse(-1)) {
211             case RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT:
212             case RcsCapabilityExchangeImplBase.COMMAND_CODE_INSUFFICIENT_MEMORY:
213             case RcsCapabilityExchangeImplBase.COMMAND_CODE_LOST_NETWORK_CONNECTION:
214             case RcsCapabilityExchangeImplBase.COMMAND_CODE_SERVICE_UNAVAILABLE:
215                 mNeedRetry = true;
216                 break;
217         }
218     }
219 
updateRetryFlagByNetworkResponse()220     private void updateRetryFlagByNetworkResponse() {
221         // Disable retry flag because the retry mechanism is implemented in the ImsService.
222         mNeedRetry = false;
223     }
224 
225     /*
226      * Check whether the publishing request is successful.
227      */
isRequestSuccess()228     public boolean isRequestSuccess() {
229         if (isCommandError()) {
230             return false;
231         }
232         // The result of the request was treated as successful if the command error code is present
233         // and its value is COMMAND_CODE_NO_CHANGE.
234         if (isCommandCodeNoChange()) {
235             return true;
236         }
237 
238         final int sipCodeOk = NetworkSipCode.SIP_CODE_OK;
239         if (getNetworkRespSipCode().filter(c -> c == sipCodeOk).isPresent() &&
240                 (!getReasonHeaderCause().isPresent()
241                         || getReasonHeaderCause().filter(c -> c == sipCodeOk).isPresent())) {
242             return true;
243         }
244         return false;
245     }
246 
247     /**
248      * Check if the PUBLISH request is failed with receiving the command error.
249      * @return true if the command is failure.
250      */
isCommandError()251     private boolean isCommandError() {
252         // The request is failed if the command error code is present and its value is not
253         // COMMAND_CODE_NO_CHANGE.
254         if (getCmdErrorCode().isPresent() && !isCommandCodeNoChange()) {
255             return true;
256         }
257         return false;
258     }
259 
260     // @return true If it received the command code COMMAND_CODE_NO_CHANGE
isCommandCodeNoChange()261     private boolean isCommandCodeNoChange() {
262         if (getCmdErrorCode().filter(code ->
263                 code == RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE).isPresent()) {
264             return true;
265         }
266         return false;
267     }
268 
269     /**
270      * Check whether the publishing request needs to be retried.
271      */
needRetry()272     public boolean needRetry() {
273         return mNeedRetry;
274     }
275 
276     /**
277      * @return The publish state when the publish request is finished.
278      */
getPublishState()279      public int getPublishState() {
280          if (isCommandError()) {
281              return getPublishStateByCmdErrorCode();
282          } else {
283              return getPublishStateByNetworkResponse();
284          }
285      }
286 
287     /**
288      * Convert the command error code to the publish state
289      */
getPublishStateByCmdErrorCode()290     private int getPublishStateByCmdErrorCode() {
291         if (getCmdErrorCode().orElse(-1) ==
292                 RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT) {
293             return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
294         }
295         return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
296     }
297 
298     /**
299      * Convert the network sip code to the publish state
300      */
getPublishStateByNetworkResponse()301     private int getPublishStateByNetworkResponse() {
302         int respSipCode;
303         if (isCommandCodeNoChange()) {
304             // If the command code is COMMAND_CODE_NO_CHANGE, it should be treated as successful.
305             respSipCode = NetworkSipCode.SIP_CODE_OK;
306         } else if (getReasonHeaderCause().isPresent()) {
307             respSipCode = getReasonHeaderCause().get();
308         } else {
309             respSipCode = getNetworkRespSipCode().orElse(-1);
310         }
311 
312         switch (respSipCode) {
313             case NetworkSipCode.SIP_CODE_OK:
314                 return RcsUceAdapter.PUBLISH_STATE_OK;
315             case NetworkSipCode.SIP_CODE_FORBIDDEN:
316             case NetworkSipCode.SIP_CODE_NOT_FOUND:
317                 return RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
318             case NetworkSipCode.SIP_CODE_REQUEST_TIMEOUT:
319                 return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
320             default:
321                 return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
322         }
323     }
324 
325     /**
326      * Get the information of the publish request response.
327      */
328     @Override
toString()329     public String toString() {
330         StringBuilder builder = new StringBuilder();
331         builder.append("taskId=").append(mTaskId)
332                 .append(", CmdErrorCode=").append(getCmdErrorCode().orElse(-1))
333                 .append(", NetworkRespSipCode=").append(getNetworkRespSipCode().orElse(-1))
334                 .append(", ReasonPhrase=").append(getReasonPhrase().orElse(""))
335                 .append(", ReasonHeaderCause=").append(getReasonHeaderCause().orElse(-1))
336                 .append(", ReasonHeaderText=").append(getReasonHeaderText().orElse(""))
337                 .append(", ResponseTimestamp=").append(mResponseTimestamp)
338                 .append(", isRequestSuccess=").append(isRequestSuccess())
339                 .append(", needRetry=").append(mNeedRetry);
340         return builder.toString();
341     }
342 }
343