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; 30 31 import java.util.Set; 32 import java.util.HashMap; 33 import java.util.Iterator; 34 import java.util.Map; 35 import java.util.List; 36 import java.util.ArrayList; 37 import android.os.RemoteException; 38 import android.content.Context; 39 import android.os.Handler; 40 import android.os.Message; 41 import android.os.HandlerThread; 42 import android.os.Looper; 43 import android.telephony.PhoneNumberUtils; 44 45 import com.android.ims.internal.uce.presence.PresCmdStatus; 46 47 import com.android.ims.internal.Logger; 48 import com.android.ims.RcsManager.ResultCode; 49 import com.android.ims.RcsPresenceInfo; 50 import com.android.ims.IRcsPresenceListener; 51 52 import com.android.service.ims.presence.PresenceTask; 53 import com.android.service.ims.presence.PresenceCapabilityTask; 54 import com.android.service.ims.presence.PresenceAvailabilityTask; 55 56 /** 57 * TaskManager 58 */ 59 public class TaskManager{ 60 /* 61 * The logger 62 */ 63 private Logger logger = Logger.getLogger(this.getClass().getName()); 64 65 private static TaskManager sTaskManager = null; 66 67 private int mTaskId = 0; 68 69 public final static int TASK_TYPE_GET_CAPABILITY = 1; 70 public final static int TASK_TYPE_GET_AVAILABILITY = 2; 71 public final static int TASK_TYPE_PUBLISH = 3; 72 73 private Map<String, Task> mTaskMap; 74 75 private final Object mSyncObj = new Object(); 76 77 private static final int TASK_MANAGER_ON_TERMINATED = 1; 78 private static final int TASK_MANAGER_ON_TIMEOUT = 2; 79 80 private static MessageHandler sMsgHandler; 81 TaskManager()82 public TaskManager(){ 83 logger.debug("TaskManager created."); 84 mTaskMap = new HashMap<String, Task>(); 85 86 HandlerThread messageHandlerThread = new HandlerThread("MessageHandler", 87 android.os.Process.THREAD_PRIORITY_BACKGROUND); 88 89 messageHandlerThread.start(); 90 Looper messageHandlerLooper = messageHandlerThread.getLooper(); 91 sMsgHandler = new MessageHandler(messageHandlerLooper); 92 } 93 getDefault()94 public static synchronized TaskManager getDefault(){ 95 if(sTaskManager == null){ 96 sTaskManager = new TaskManager(); 97 } 98 99 return sTaskManager; 100 } 101 generateTaskId()102 public synchronized int generateTaskId(){ 103 return mTaskId++; 104 } 105 putTask(int taskId, Task task)106 public void putTask(int taskId, Task task){ 107 synchronized (mSyncObj){ 108 putTaskInternal(taskId, task); 109 } 110 } 111 putTaskInternal(int taskId, Task task)112 private synchronized void putTaskInternal(int taskId, Task task){ 113 Task sameKeyTask = mTaskMap.put(String.valueOf(taskId), task); 114 115 logger.debug("Added Task: " + task + "Original same key task:" + sameKeyTask); 116 } 117 addCapabilityTask(Context context, String[] contacts, IRcsPresenceListener listener, long timeout)118 public int addCapabilityTask(Context context, String[] contacts, 119 IRcsPresenceListener listener, long timeout){ 120 int taskId = TaskManager.getDefault().generateTaskId(); 121 synchronized (mSyncObj){ 122 Task task = new PresenceCapabilityTask(context, taskId, TASK_TYPE_GET_CAPABILITY, 123 listener, contacts, timeout); 124 putTaskInternal(taskId, task); 125 } 126 127 return taskId; 128 } 129 addAvailabilityTask(String contact, IRcsPresenceListener listener)130 public int addAvailabilityTask(String contact, IRcsPresenceListener listener){ 131 int taskId = TaskManager.getDefault().generateTaskId(); 132 synchronized (mSyncObj){ 133 String[] contacts = new String[1]; 134 contacts[0] = contact; 135 Task task = new PresenceAvailabilityTask(taskId, TASK_TYPE_GET_AVAILABILITY, 136 listener, contacts); 137 putTaskInternal(taskId, task); 138 } 139 140 return taskId; 141 } 142 addPublishTask(String contact, IRcsPresenceListener listener)143 public int addPublishTask(String contact, IRcsPresenceListener listener){ 144 int taskId = TaskManager.getDefault().generateTaskId(); 145 synchronized (mSyncObj){ 146 String[] contacts = new String[1]; 147 contacts[0] = contact; 148 Task task = new PresenceTask(taskId, TASK_TYPE_PUBLISH, listener, contacts); 149 putTaskInternal(taskId, task); 150 } 151 152 return taskId; 153 } 154 155 // If need to call getTask in this class please add another one getTaskInternal getTask(int taskId)156 public Task getTask(int taskId){ 157 synchronized (mSyncObj){ 158 return mTaskMap.get(String.valueOf(taskId)); 159 } 160 } 161 removeTask(int taskId)162 public void removeTask(int taskId){ 163 synchronized (mSyncObj){ 164 Task task = mTaskMap.remove(String.valueOf(taskId)); 165 if(task instanceof PresenceCapabilityTask){ 166 ((PresenceCapabilityTask)task).cancelTimer(); 167 } 168 logger.debug("Removed Task: " + task); 169 } 170 } 171 getTaskByRequestId(int sipRequestId)172 public Task getTaskByRequestId(int sipRequestId){ 173 synchronized (mSyncObj){ 174 Set<String> keys= mTaskMap.keySet(); 175 if(keys == null){ 176 logger.debug("getTaskByRequestId keys=null"); 177 return null; 178 } 179 180 for(String key:keys){ 181 if(mTaskMap.get(key).mSipRequestId == sipRequestId){ 182 logger.debug("getTaskByRequestId, sipRequestId=" + sipRequestId + 183 " task=" + mTaskMap.get(key)); 184 return mTaskMap.get(key); 185 } 186 } 187 } 188 189 logger.debug("getTaskByRequestId, sipRequestId=" + sipRequestId + " task=null"); 190 return null; 191 } 192 onTerminated(String contact)193 public void onTerminated(String contact){ // for single number capability polling 194 if(contact == null){ 195 return; 196 } 197 198 synchronized (mSyncObj){ 199 Set<String> keys= mTaskMap.keySet(); 200 if(keys == null){ 201 logger.debug("onTerminated keys is null"); 202 return; 203 } 204 205 for(String key:keys){ 206 Task task = mTaskMap.get(key); 207 if(task == null){ 208 continue; 209 } 210 211 if(task instanceof PresenceCapabilityTask){ 212 PresenceCapabilityTask capabilityTask = (PresenceCapabilityTask)task; 213 if(capabilityTask.mContacts != null && capabilityTask.mContacts[0] != null && 214 PhoneNumberUtils.compare(contact, capabilityTask.mContacts[0])){ 215 if(!capabilityTask.isWaitingForNotify()){ 216 logger.debug("onTerminated the tesk is not waiting for NOTIFY yet"); 217 continue; 218 } 219 220 MessageData messageData = new MessageData(); 221 messageData.mTask = capabilityTask; 222 messageData.mReason = null; 223 224 Message notifyMessage = sMsgHandler.obtainMessage( 225 TASK_MANAGER_ON_TERMINATED, 226 messageData); 227 sMsgHandler.sendMessage(notifyMessage); 228 } 229 } 230 } 231 } 232 } 233 onTerminated(int requestId, String reason)234 public void onTerminated(int requestId, String reason){ 235 logger.debug("onTerminated requestId=" + requestId + " reason=" + reason); 236 237 Task task = getTaskByRequestId(requestId); 238 if(task == null){ 239 logger.debug("onTerminated Can't find request " + requestId); 240 return; 241 } 242 243 synchronized (mSyncObj){ 244 if(task instanceof PresenceCapabilityTask){ 245 MessageData messageData = new MessageData(); 246 messageData.mTask = (PresenceCapabilityTask)task; 247 messageData.mReason = reason; 248 249 Message notifyMessage = sMsgHandler.obtainMessage(TASK_MANAGER_ON_TERMINATED, 250 messageData); 251 sMsgHandler.sendMessage(notifyMessage); 252 } 253 } 254 } 255 onTimeout(int taskId)256 public void onTimeout(int taskId){ 257 logger.debug("onTimeout taskId=" + taskId); 258 259 Task task = getTask(taskId); 260 if(task == null){ 261 logger.debug("onTimeout task = null"); 262 return; 263 } 264 synchronized (mSyncObj){ 265 if(task instanceof PresenceCapabilityTask){ 266 MessageData messageData = new MessageData(); 267 messageData.mTask = (PresenceCapabilityTask)task; 268 messageData.mReason = null; 269 270 Message timeoutMessage = sMsgHandler.obtainMessage(TASK_MANAGER_ON_TIMEOUT, 271 messageData); 272 sMsgHandler.sendMessage(timeoutMessage); 273 }else{ 274 logger.debug("not PresenceCapabilityTask, taskId=" + taskId); 275 } 276 } 277 } 278 279 public class MessageData{ 280 public PresenceCapabilityTask mTask; 281 public String mReason; 282 } 283 284 public class MessageHandler extends Handler{ MessageHandler(Looper looper)285 MessageHandler(Looper looper){ 286 super(looper); 287 } 288 289 @Override handleMessage(Message msg)290 public void handleMessage(Message msg) { 291 super.handleMessage(msg); 292 293 logger.debug( "Thread=" + Thread.currentThread().getName() + " received " 294 + msg); 295 296 297 298 if(msg == null){ 299 logger.error("msg=null"); 300 return; 301 } 302 303 switch (msg.what) { 304 case TASK_MANAGER_ON_TERMINATED: 305 { 306 MessageData messageData = (MessageData) msg.obj; 307 if(messageData != null && messageData.mTask != null){ 308 messageData.mTask.onTerminated(messageData.mReason); 309 } 310 break; 311 } 312 313 case TASK_MANAGER_ON_TIMEOUT: 314 { 315 MessageData messageData = (MessageData) msg.obj; 316 if(messageData != null && messageData.mTask != null){ 317 messageData.mTask.onTimeout(); 318 } 319 break; 320 } 321 322 default: 323 logger.debug("handleMessage unknown msg=" + msg.what); 324 } 325 } 326 } 327 clearTimeoutAvailabilityTask(long availabilityExpire)328 public void clearTimeoutAvailabilityTask(long availabilityExpire) { 329 logger.debug("clearTimeoutAvailabilityTask"); 330 331 synchronized (mSyncObj) { 332 long currentTime = System.currentTimeMillis(); 333 334 Iterator<Map.Entry<String, Task>> iterator = mTaskMap.entrySet().iterator(); 335 while (iterator.hasNext()) { 336 Map.Entry<String, Task> entry = iterator.next(); 337 338 Task task = (Task) entry.getValue(); 339 logger.debug("Currently existing Availability task, key: " + entry.getKey() 340 + ", Task: " + task); 341 342 if ((task != null) && (task instanceof PresenceAvailabilityTask)) { 343 PresenceAvailabilityTask presenceTask = (PresenceAvailabilityTask)task; 344 345 long notifyTimestamp = presenceTask.getNotifyTimestamp(); 346 long createTimestamp = presenceTask.getCreateTimestamp(); 347 logger.debug("createTimestamp=" + createTimestamp + " notifyTimestamp=" + 348 notifyTimestamp + " currentTime=" + currentTime); 349 350 // remove it if it didn't get notify in 60s. 351 // or get notify for 60s 352 if(((notifyTimestamp != 0) && 353 (notifyTimestamp + availabilityExpire < currentTime)) || 354 (notifyTimestamp == 0) && 355 (createTimestamp + availabilityExpire < currentTime)) { 356 logger.debug("remove expired availability task:" + presenceTask); 357 iterator.remove(); 358 } 359 } 360 } 361 } 362 } 363 getAvailabilityTaskByContact(String contact)364 public PresenceAvailabilityTask getAvailabilityTaskByContact(String contact){ 365 synchronized (mSyncObj){ 366 Set<String> keys= mTaskMap.keySet(); 367 if(keys == null){ 368 logger.debug("getTaskByContact keys=null"); 369 return null; 370 } 371 372 for(String key:keys){ 373 Task task = mTaskMap.get(key); 374 if(task == null){ 375 continue; 376 } 377 378 if(task instanceof PresenceAvailabilityTask){ 379 PresenceAvailabilityTask availabilityTask = (PresenceAvailabilityTask)task; 380 if(PhoneNumberUtils.compare(contact, availabilityTask.mContacts[0])){ 381 return availabilityTask; 382 } 383 } 384 } 385 } 386 387 return null; 388 } 389 } 390 391