1 /*
2  * Copyright (c) 2015, Motorola Mobility LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     - Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     - Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     - Neither the name of Motorola Mobility nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26  * DAMAGE.
27  */
28 
29 package com.android.service.ims.presence;
30 
31 import android.annotation.IntDef;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.telephony.ims.ImsManager;
35 
36 import com.android.ims.ResultCode;
37 import com.android.ims.internal.Logger;
38 import com.android.service.ims.Task;
39 import com.android.service.ims.TaskManager;
40 
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 
44 public class PresenceBase {
45     static private Logger logger = Logger.getLogger("PresenceBase");
46 
47     protected Context mContext;
48 
49     /**
50      * The phone is PUBLISH_STATE_200_OK when
51      * the response of the last publish is "200 OK"
52      */
53     public static final int PUBLISH_STATE_200_OK = 0;
54 
55     /**
56      * The phone didn't publish after power on.
57      * the phone didn't get any publish response yet.
58      */
59     public static final int PUBLISH_STATE_NOT_PUBLISHED = 1;
60 
61     /**
62      * The phone is PUBLISH_STATE_VOLTE_PROVISION_ERROR when the response is one of items
63      * in config_volte_provision_error_on_publish_response for PUBLISH or
64      * in config_volte_provision_error_on_subscribe_response for SUBSCRIBE.
65      */
66     public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 2;
67 
68     /**
69      * The phone is PUBLISH_STATE_RCS_PROVISION_ERROR when the response is one of items
70      * in config_rcs_provision_error_on_publish_response for PUBLISH or
71      * in config_rcs_provision_error_on_subscribe_response for SUBSCRIBE.Publ
72      */
73     public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 3;
74 
75     /**
76      * The phone is PUBLISH_STATE_REQUEST_TIMEOUT when
77      * The response of the last publish is "408 Request Timeout".
78      */
79     public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 4;
80 
81     /**
82      * The phone is PUBLISH_STATE_OTHER_ERROR when
83      * the response of the last publish is other temp error. Such as
84      * 503 Service Unavailable
85      * Device shall retry with exponential back-off
86      *
87      * 423 Interval Too Short. Requested expiry interval too short and server rejects it
88      * Device shall re-attempt subscription after changing the expiration interval in
89      * the Expires header field to be equal to or greater than the expiration interval
90      * within the Min-Expires header field of the 423 response.
91      */
92     public static final int PUBLISH_STATE_OTHER_ERROR = 5;
93 
94     @IntDef(value = {
95             PUBLISH_STATE_200_OK,
96             PUBLISH_STATE_NOT_PUBLISHED,
97             PUBLISH_STATE_VOLTE_PROVISION_ERROR,
98             PUBLISH_STATE_RCS_PROVISION_ERROR,
99             PUBLISH_STATE_REQUEST_TIMEOUT,
100             PUBLISH_STATE_OTHER_ERROR
101     }, prefix="PUBLISH_STATE_")
102     @Retention(RetentionPolicy.SOURCE)
103     public @interface PresencePublishState {}
104 
PresenceBase(Context context)105     public PresenceBase(Context context) {
106         mContext = context;
107     }
108 
handleCallback(Task task, int resultCode, boolean forCmdStatus)109     protected void handleCallback(Task task, int resultCode, boolean forCmdStatus) {
110         if (task == null) {
111             logger.debug("task == null");
112             return;
113         }
114 
115         if (task.mListener != null) {
116             if(resultCode >= ResultCode.SUCCESS){
117                 if(!forCmdStatus){
118                     task.mListener.onSuccess(task.mTaskId);
119                 }
120             }else{
121                 task.mListener.onError(task.mTaskId, resultCode);
122             }
123         }
124 
125         // remove task when error
126         // remove task when SIP response success.
127         // For list capability polling we will waiting for the terminated notify or timeout.
128         if (resultCode != ResultCode.SUCCESS) {
129             if(task instanceof PresencePublishTask){
130                 PresencePublishTask publishTask = (PresencePublishTask) task;
131                 logger.debug("handleCallback for publishTask=" + publishTask);
132                 if(resultCode == PUBLISH_STATE_VOLTE_PROVISION_ERROR) {
133                     // retry 3 times for "403 Not Authorized for Presence".
134                     if (publishTask.getRetryCount() >= 3) {
135                         //remove capability after try 3 times by PresencePolling
136                         logger.debug("handleCallback remove task=" + task);
137                         TaskManager.getDefault().removeTask(task.mTaskId);
138                     } else {
139                         // Continue retry
140                         publishTask.setRetryCount(publishTask.getRetryCount() + 1);
141                     }
142                 } else {
143                     logger.debug("handleCallback remove task=" + task);
144                     TaskManager.getDefault().removeTask(task.mTaskId);
145                 }
146             } else {
147                 logger.debug("handleCallback remove task=" + task);
148                 TaskManager.getDefault().removeTask(task.mTaskId);
149             }
150         }else{
151             if(forCmdStatus || !forCmdStatus && (task instanceof PresenceCapabilityTask)){
152                 logger.debug("handleCallback remove task later");
153 
154                 //waiting for Notify from network
155                 if(!forCmdStatus){
156                     ((PresenceCapabilityTask)task).setWaitingForNotify(true);
157                 }
158             }else{
159                 if(!forCmdStatus && (task instanceof PresenceAvailabilityTask) &&
160                         (resultCode == ResultCode.SUCCESS)){
161                     // Availiablity, cache for 60s, remove it later.
162                     logger.debug("handleCallback PresenceAvailabilityTask cache for 60s task="
163                             + task);
164                     return;
165                 }
166 
167                 logger.debug("handleCallback remove task=" + task);
168                 TaskManager.getDefault().removeTask(task.mTaskId);
169             }
170         }
171     }
172 
onCommandStatusUpdated(int taskId, int requestId, int resultCode)173     public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) {
174         Task task = TaskManager.getDefault().getTask(taskId);
175         if (task != null){
176             task.mSipRequestId = requestId;
177             task.mCmdStatus = resultCode;
178             TaskManager.getDefault().putTask(task.mTaskId, task);
179         }
180 
181         handleCallback(task, resultCode, true);
182     }
183 
notifyDm()184     protected void notifyDm() {
185         logger.debug("notifyDm");
186         Intent intent = new Intent(
187                 ImsManager.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION);
188         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
189 
190         mContext.sendBroadcast(intent);
191     }
192 
isInConfigList(int errorNo, String phrase, String[] errorArray)193     protected boolean isInConfigList(int errorNo, String phrase, String[] errorArray) {
194         String inErrorString = ("" + errorNo).trim();
195 
196         logger.debug("errorArray length=" + errorArray.length + " errorArray=" + errorArray);
197         for (String errorStr : errorArray) {
198             if (errorStr != null && errorStr.startsWith(inErrorString)) {
199                 String errorPhrase = errorStr.substring(inErrorString.length());
200                 if(errorPhrase == null || errorPhrase.isEmpty()) {
201                     return true;
202                 }
203 
204                 if(phrase == null || phrase.isEmpty()) {
205                     return false;
206                 }
207 
208                 return phrase.toLowerCase().contains(errorPhrase.toLowerCase());
209             }
210         }
211         return false;
212     }
213 }
214 
215