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