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