| /* |
| * Copyright (c) 2015, Motorola Mobility LLC |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * - Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * - Neither the name of Motorola Mobility nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| package com.android.service.ims; |
| |
| import android.content.Context; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.telephony.PhoneNumberUtils; |
| |
| import com.android.ims.internal.Logger; |
| import com.android.service.ims.presence.ContactCapabilityResponse; |
| import com.android.service.ims.presence.PresenceAvailabilityTask; |
| import com.android.service.ims.presence.PresenceCapabilityTask; |
| import com.android.service.ims.presence.PresenceTask; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * TaskManager |
| */ |
| public class TaskManager{ |
| /* |
| * The logger |
| */ |
| private Logger logger = Logger.getLogger(this.getClass().getName()); |
| |
| private static TaskManager sTaskManager = null; |
| |
| private int mTaskId = 0; |
| |
| public final static int TASK_TYPE_GET_CAPABILITY = 1; |
| public final static int TASK_TYPE_GET_AVAILABILITY = 2; |
| public final static int TASK_TYPE_PUBLISH = 3; |
| |
| private Map<String, Task> mTaskMap; |
| |
| private final Object mSyncObj = new Object(); |
| |
| private static final int TASK_MANAGER_ON_TERMINATED = 1; |
| private static final int TASK_MANAGER_ON_TIMEOUT = 2; |
| |
| private static MessageHandler sMsgHandler; |
| |
| public TaskManager(){ |
| logger.debug("TaskManager created."); |
| mTaskMap = new HashMap<String, Task>(); |
| |
| HandlerThread messageHandlerThread = new HandlerThread("MessageHandler", |
| android.os.Process.THREAD_PRIORITY_BACKGROUND); |
| |
| messageHandlerThread.start(); |
| Looper messageHandlerLooper = messageHandlerThread.getLooper(); |
| sMsgHandler = new MessageHandler(messageHandlerLooper); |
| } |
| |
| public static synchronized TaskManager getDefault(){ |
| if(sTaskManager == null){ |
| sTaskManager = new TaskManager(); |
| } |
| |
| return sTaskManager; |
| } |
| |
| public synchronized int generateTaskId(){ |
| return mTaskId++; |
| } |
| |
| public void putTask(int taskId, Task task){ |
| synchronized (mSyncObj){ |
| putTaskInternal(taskId, task); |
| } |
| } |
| |
| private synchronized void putTaskInternal(int taskId, Task task){ |
| Task sameKeyTask = mTaskMap.put(String.valueOf(taskId), task); |
| |
| logger.debug("Added Task: " + task + "Original same key task:" + sameKeyTask); |
| } |
| |
| public int addCapabilityTask(Context context, String[] contacts, |
| ContactCapabilityResponse listener, long timeout){ |
| int taskId = TaskManager.getDefault().generateTaskId(); |
| synchronized (mSyncObj){ |
| Task task = new PresenceCapabilityTask(context, taskId, TASK_TYPE_GET_CAPABILITY, |
| listener, contacts, timeout); |
| putTaskInternal(taskId, task); |
| } |
| |
| return taskId; |
| } |
| |
| public int addAvailabilityTask(String contact, ContactCapabilityResponse listener){ |
| int taskId = TaskManager.getDefault().generateTaskId(); |
| synchronized (mSyncObj){ |
| String[] contacts = new String[1]; |
| contacts[0] = contact; |
| Task task = new PresenceAvailabilityTask(taskId, TASK_TYPE_GET_AVAILABILITY, |
| listener, contacts); |
| putTaskInternal(taskId, task); |
| } |
| |
| return taskId; |
| } |
| |
| public int addPublishTask(String contact){ |
| int taskId = TaskManager.getDefault().generateTaskId(); |
| synchronized (mSyncObj){ |
| String[] contacts = new String[1]; |
| contacts[0] = contact; |
| Task task = new PresenceTask(taskId, TASK_TYPE_PUBLISH, null /*listener*/, contacts); |
| putTaskInternal(taskId, task); |
| } |
| |
| return taskId; |
| } |
| |
| // If need to call getTask in this class please add another one getTaskInternal |
| public Task getTask(int taskId){ |
| synchronized (mSyncObj){ |
| return mTaskMap.get(String.valueOf(taskId)); |
| } |
| } |
| |
| public void removeTask(int taskId){ |
| synchronized (mSyncObj){ |
| Task task = mTaskMap.remove(String.valueOf(taskId)); |
| if(task instanceof PresenceCapabilityTask){ |
| ((PresenceCapabilityTask)task).cancelTimer(); |
| } |
| logger.debug("Removed Task: " + task); |
| } |
| } |
| |
| public Task getTaskForSingleContactQuery(String contact) { |
| synchronized (mSyncObj){ |
| Set<String> keys= mTaskMap.keySet(); |
| if(keys == null){ |
| logger.debug("getTaskByContact keys=null"); |
| return null; |
| } |
| |
| for(String key:keys){ |
| Task task = mTaskMap.get(key); |
| if(task == null){ |
| continue; |
| } |
| |
| if (task instanceof PresenceTask) { |
| PresenceTask presenceTask = (PresenceTask) task; |
| if(presenceTask.mContacts.length == 1 && |
| PhoneNumberUtils.compare(contact, presenceTask.mContacts[0])){ |
| return task; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public Task getTaskByRequestId(int sipRequestId){ |
| synchronized (mSyncObj){ |
| Set<String> keys= mTaskMap.keySet(); |
| if(keys == null){ |
| logger.debug("getTaskByRequestId keys=null"); |
| return null; |
| } |
| |
| for(String key:keys){ |
| if(mTaskMap.get(key).mSipRequestId == sipRequestId){ |
| logger.debug("getTaskByRequestId, sipRequestId=" + sipRequestId + |
| " task=" + mTaskMap.get(key)); |
| return mTaskMap.get(key); |
| } |
| } |
| } |
| |
| logger.debug("getTaskByRequestId, sipRequestId=" + sipRequestId + " task=null"); |
| return null; |
| } |
| |
| public void onTerminated(String contact){ // for single number capability polling |
| if(contact == null){ |
| return; |
| } |
| |
| synchronized (mSyncObj){ |
| Set<String> keys= mTaskMap.keySet(); |
| if(keys == null){ |
| logger.debug("onTerminated keys is null"); |
| return; |
| } |
| |
| for(String key:keys){ |
| Task task = mTaskMap.get(key); |
| if(task == null){ |
| continue; |
| } |
| |
| if(task instanceof PresenceCapabilityTask){ |
| PresenceCapabilityTask capabilityTask = (PresenceCapabilityTask)task; |
| if(capabilityTask.mContacts != null && capabilityTask.mContacts[0] != null && |
| PhoneNumberUtils.compare(contact, capabilityTask.mContacts[0])){ |
| if(!capabilityTask.isWaitingForNotify()){ |
| logger.debug("onTerminated the tesk is not waiting for NOTIFY yet"); |
| continue; |
| } |
| |
| MessageData messageData = new MessageData(); |
| messageData.mTask = capabilityTask; |
| messageData.mReason = null; |
| |
| Message notifyMessage = sMsgHandler.obtainMessage( |
| TASK_MANAGER_ON_TERMINATED, |
| messageData); |
| sMsgHandler.sendMessage(notifyMessage); |
| } |
| } |
| } |
| } |
| } |
| |
| public void onTerminated(int requestId, String reason){ |
| logger.debug("onTerminated requestId=" + requestId + " reason=" + reason); |
| |
| Task task = getTaskByRequestId(requestId); |
| if(task == null){ |
| logger.debug("onTerminated Can't find request " + requestId); |
| return; |
| } |
| |
| synchronized (mSyncObj){ |
| if(task instanceof PresenceCapabilityTask){ |
| MessageData messageData = new MessageData(); |
| messageData.mTask = (PresenceCapabilityTask)task; |
| messageData.mReason = reason; |
| |
| Message notifyMessage = sMsgHandler.obtainMessage(TASK_MANAGER_ON_TERMINATED, |
| messageData); |
| sMsgHandler.sendMessage(notifyMessage); |
| } |
| } |
| } |
| |
| public void onTimeout(int taskId){ |
| logger.debug("onTimeout taskId=" + taskId); |
| |
| Task task = getTask(taskId); |
| if(task == null){ |
| logger.debug("onTimeout task = null"); |
| return; |
| } |
| synchronized (mSyncObj){ |
| if(task instanceof PresenceCapabilityTask){ |
| MessageData messageData = new MessageData(); |
| messageData.mTask = (PresenceCapabilityTask)task; |
| messageData.mReason = null; |
| |
| Message timeoutMessage = sMsgHandler.obtainMessage(TASK_MANAGER_ON_TIMEOUT, |
| messageData); |
| sMsgHandler.sendMessage(timeoutMessage); |
| }else{ |
| logger.debug("not PresenceCapabilityTask, taskId=" + taskId); |
| } |
| } |
| } |
| |
| public class MessageData{ |
| public PresenceCapabilityTask mTask; |
| public String mReason; |
| } |
| |
| public class MessageHandler extends Handler{ |
| MessageHandler(Looper looper){ |
| super(looper); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| super.handleMessage(msg); |
| |
| logger.debug( "Thread=" + Thread.currentThread().getName() + " received " |
| + msg); |
| |
| |
| |
| if(msg == null){ |
| logger.error("msg=null"); |
| return; |
| } |
| |
| switch (msg.what) { |
| case TASK_MANAGER_ON_TERMINATED: |
| { |
| MessageData messageData = (MessageData) msg.obj; |
| if(messageData != null && messageData.mTask != null){ |
| messageData.mTask.onTerminated(messageData.mReason); |
| } |
| break; |
| } |
| |
| case TASK_MANAGER_ON_TIMEOUT: |
| { |
| MessageData messageData = (MessageData) msg.obj; |
| if(messageData != null && messageData.mTask != null){ |
| messageData.mTask.onTimeout(); |
| } |
| break; |
| } |
| |
| default: |
| logger.debug("handleMessage unknown msg=" + msg.what); |
| } |
| } |
| } |
| |
| public void clearTimeoutAvailabilityTask(long availabilityExpire) { |
| logger.debug("clearTimeoutAvailabilityTask"); |
| |
| synchronized (mSyncObj) { |
| long currentTime = System.currentTimeMillis(); |
| |
| Iterator<Map.Entry<String, Task>> iterator = mTaskMap.entrySet().iterator(); |
| while (iterator.hasNext()) { |
| Map.Entry<String, Task> entry = iterator.next(); |
| |
| Task task = (Task) entry.getValue(); |
| logger.debug("Currently existing Availability task, key: " + entry.getKey() |
| + ", Task: " + task); |
| |
| if ((task != null) && (task instanceof PresenceAvailabilityTask)) { |
| PresenceAvailabilityTask presenceTask = (PresenceAvailabilityTask)task; |
| |
| long notifyTimestamp = presenceTask.getNotifyTimestamp(); |
| long createTimestamp = presenceTask.getCreateTimestamp(); |
| logger.debug("createTimestamp=" + createTimestamp + " notifyTimestamp=" + |
| notifyTimestamp + " currentTime=" + currentTime); |
| |
| // remove it if it didn't get notify in 60s. |
| // or get notify for 60s |
| if(((notifyTimestamp != 0) && |
| (notifyTimestamp + availabilityExpire < currentTime)) || |
| (notifyTimestamp == 0) && |
| (createTimestamp + availabilityExpire < currentTime)) { |
| logger.debug("remove expired availability task:" + presenceTask); |
| iterator.remove(); |
| } |
| } |
| } |
| } |
| } |
| |
| public PresenceAvailabilityTask getAvailabilityTaskByContact(String contact){ |
| synchronized (mSyncObj){ |
| Set<String> keys= mTaskMap.keySet(); |
| if(keys == null){ |
| logger.debug("getTaskByContact keys=null"); |
| return null; |
| } |
| |
| for(String key:keys){ |
| Task task = mTaskMap.get(key); |
| if(task == null){ |
| continue; |
| } |
| |
| if(task instanceof PresenceAvailabilityTask){ |
| PresenceAvailabilityTask availabilityTask = (PresenceAvailabilityTask)task; |
| if(PhoneNumberUtils.compare(contact, availabilityTask.mContacts[0])){ |
| return availabilityTask; |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| } |
| |