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