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 import java.util.Arrays; 44 45 public class PresenceBase { 46 static private Logger logger = Logger.getLogger("PresenceBase"); 47 48 protected Context mContext; 49 50 /** 51 * The phone is PUBLISH_STATE_200_OK when 52 * the response of the last publish is "200 OK" 53 */ 54 public static final int PUBLISH_STATE_200_OK = 0; 55 56 /** 57 * The phone didn't publish after power on. 58 * the phone didn't get any publish response yet. 59 */ 60 public static final int PUBLISH_STATE_NOT_PUBLISHED = 1; 61 62 /** 63 * The phone is PUBLISH_STATE_VOLTE_PROVISION_ERROR when the response is one of items 64 * in config_volte_provision_error_on_publish_response for PUBLISH or 65 * in config_volte_provision_error_on_subscribe_response for SUBSCRIBE. 66 */ 67 public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 2; 68 69 /** 70 * The phone is PUBLISH_STATE_RCS_PROVISION_ERROR when the response is one of items 71 * in config_rcs_provision_error_on_publish_response for PUBLISH or 72 * in config_rcs_provision_error_on_subscribe_response for SUBSCRIBE.Publ 73 */ 74 public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 3; 75 76 /** 77 * The phone is PUBLISH_STATE_REQUEST_TIMEOUT when 78 * The response of the last publish is "408 Request Timeout". 79 */ 80 public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 4; 81 82 /** 83 * The phone is PUBLISH_STATE_OTHER_ERROR when 84 * the response of the last publish is other temp error. Such as 85 * 503 Service Unavailable 86 * Device shall retry with exponential back-off 87 * 88 * 423 Interval Too Short. Requested expiry interval too short and server rejects it 89 * Device shall re-attempt subscription after changing the expiration interval in 90 * the Expires header field to be equal to or greater than the expiration interval 91 * within the Min-Expires header field of the 423 response. 92 */ 93 public static final int PUBLISH_STATE_OTHER_ERROR = 5; 94 95 @IntDef(value = { 96 PUBLISH_STATE_200_OK, 97 PUBLISH_STATE_NOT_PUBLISHED, 98 PUBLISH_STATE_VOLTE_PROVISION_ERROR, 99 PUBLISH_STATE_RCS_PROVISION_ERROR, 100 PUBLISH_STATE_REQUEST_TIMEOUT, 101 PUBLISH_STATE_OTHER_ERROR 102 }, prefix="PUBLISH_STATE_") 103 @Retention(RetentionPolicy.SOURCE) 104 public @interface PresencePublishState {} 105 PresenceBase(Context context)106 public PresenceBase(Context context) { 107 mContext = context; 108 } 109 handleCallback(Task task, int resultCode, boolean forCmdStatus)110 protected void handleCallback(Task task, int resultCode, boolean forCmdStatus) { 111 if (task == null) { 112 logger.debug("task == null"); 113 return; 114 } 115 116 if (task.mListener != null) { 117 if(resultCode >= ResultCode.SUCCESS){ 118 if(!forCmdStatus){ 119 task.mListener.onSuccess(task.mTaskId); 120 } 121 }else{ 122 task.mListener.onError(task.mTaskId, resultCode); 123 } 124 } 125 126 // remove task when error 127 // remove task when SIP response success. 128 // For list capability polling we will waiting for the terminated notify or timeout. 129 if (resultCode != ResultCode.SUCCESS) { 130 if(task instanceof PresencePublishTask){ 131 PresencePublishTask publishTask = (PresencePublishTask) task; 132 logger.debug("handleCallback for publishTask=" + publishTask); 133 if(resultCode == PUBLISH_STATE_VOLTE_PROVISION_ERROR) { 134 // retry 3 times for "403 Not Authorized for Presence". 135 if (publishTask.getRetryCount() >= 3) { 136 //remove capability after try 3 times by PresencePolling 137 logger.debug("handleCallback remove task=" + task); 138 TaskManager.getDefault().removeTask(task.mTaskId); 139 } else { 140 // Continue retry 141 publishTask.setRetryCount(publishTask.getRetryCount() + 1); 142 } 143 } else { 144 logger.debug("handleCallback remove task=" + task); 145 TaskManager.getDefault().removeTask(task.mTaskId); 146 } 147 } else { 148 logger.debug("handleCallback remove task=" + task); 149 TaskManager.getDefault().removeTask(task.mTaskId); 150 } 151 }else{ 152 if(forCmdStatus || !forCmdStatus && (task instanceof PresenceCapabilityTask)){ 153 logger.debug("handleCallback remove task later"); 154 155 //waiting for Notify from network 156 if(!forCmdStatus){ 157 ((PresenceCapabilityTask)task).setWaitingForNotify(true); 158 } 159 }else{ 160 if(!forCmdStatus && (task instanceof PresenceAvailabilityTask) && 161 (resultCode == ResultCode.SUCCESS)){ 162 // Availiablity, cache for 60s, remove it later. 163 logger.debug("handleCallback PresenceAvailabilityTask cache for 60s task=" 164 + task); 165 return; 166 } 167 168 logger.debug("handleCallback remove task=" + task); 169 TaskManager.getDefault().removeTask(task.mTaskId); 170 } 171 } 172 } 173 onCommandStatusUpdated(int taskId, int requestId, int resultCode)174 public void onCommandStatusUpdated(int taskId, int requestId, int resultCode) { 175 Task task = TaskManager.getDefault().getTask(taskId); 176 if (task != null){ 177 task.mSipRequestId = requestId; 178 task.mCmdStatus = resultCode; 179 TaskManager.getDefault().putTask(task.mTaskId, task); 180 } 181 182 handleCallback(task, resultCode, true); 183 } 184 notifyDm()185 protected void notifyDm() { 186 logger.debug("notifyDm"); 187 Intent intent = new Intent( 188 ImsManager.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION); 189 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 190 191 mContext.sendBroadcast(intent); 192 } 193 isInConfigList(int errorNo, String phrase, String[] errorArray)194 protected boolean isInConfigList(int errorNo, String phrase, String[] errorArray) { 195 String inErrorString = ("" + errorNo).trim(); 196 197 logger.debug("errorArray length=" + errorArray.length 198 + " errorArray=" + Arrays.toString(errorArray)); 199 for (String errorStr : errorArray) { 200 if (errorStr != null && errorStr.startsWith(inErrorString)) { 201 String errorPhrase = errorStr.substring(inErrorString.length()); 202 if(errorPhrase == null || errorPhrase.isEmpty()) { 203 return true; 204 } 205 206 if(phrase == null || phrase.isEmpty()) { 207 return false; 208 } 209 210 return phrase.toLowerCase().contains(errorPhrase.toLowerCase()); 211 } 212 } 213 return false; 214 } 215 } 216 217