package com.eolwral.osmonitor.ipc; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Comparator; import java.util.PriorityQueue; import java.util.concurrent.Semaphore; import android.content.Context; import android.os.AsyncTask; import com.eolwral.osmonitor.util.CoreUtil; import com.eolwral.osmonitor.core.commandInfo; import com.eolwral.osmonitor.settings.Settings; import com.google.flatbuffers.FlatBufferBuilder; /** * implement communicate mechanize between process with Unix socket */ public class IpcService { /** * Singleton instance */ private static IpcService instance = null; private Context ipcContext = null; /** * Connection Object */ private IpcConnection client = null; /** * comparator for using time stamp */ private class QueuedComparator implements Comparator<QueuedTask> { @Override public int compare(QueuedTask lhs, QueuedTask rhs) { if(lhs.timestamp > rhs.timestamp) return 1; else if (lhs.timestamp < rhs.timestamp) return -1; return 0; } } /** * a queue for IPC command */ private static PriorityQueue<QueuedTask> cmdQueue = null; /** * exclusive lock for cmdQueue */ private final Semaphore cmdQueueLock = new Semaphore(1, true); /** * a class for single task */ private class QueuedTask { public byte[] action = null; public long timestamp = 0; public ipcClientListener listener = null; public byte [] result = null; /** * constructor */ QueuedTask(byte[] action, long timestamp, ipcClientListener listener) { this.action = action; this.timestamp = timestamp; this.listener = listener; } /** * replace equals that will only check listener */ @Override public boolean equals (Object object) { if(object instanceof QueuedTask) { QueuedTask compareObj = (QueuedTask)object; if(compareObj.listener == this.listener) return true; } return false; } } /** * callback interface for ipcClient */ public interface ipcClientListener { public void onRecvData(byte [] result); } /** * background worker for IpcService */ private static ipcTask worker = null; /** * initialize IpcService * @param[in] context */ public static void Initialize(Context context) { if (instance == null) { instance = new IpcService(); instance.ipcContext = context; instance.createConnection(); } } /** * get a instance for IpcService [avoid duplicated connections] * @return IpcService */ public static IpcService getInstance() { return instance; } /** * internal use only for creating object * @param[in] context Context */ private IpcService() { // prepare a priority queue QueuedComparator cmdComparator = new QueuedComparator(); cmdQueue = new PriorityQueue<QueuedTask>(1, cmdComparator); } /** * create a connection */ public void createConnection() { //clean up connection if (client != null) { try { client.close(); } catch (IOException e) { } } // create ipcConnection client = new IpcConnection(CoreUtil.getSocketName(instance.ipcContext)); } /** * destructor for ipcClient */ protected void finalize() { try { if (client != null) client.close(); } catch (IOException e) { } } /** * connection to osmcore * @return true == success, false == fail */ private boolean connect() { try { Settings settings = Settings.getInstance(ipcContext); client.connect(settings.getInterval()*1000); // send token OutputStream outData = client.getOutputStream(); if (outData == null) throw new IOException(); byte [] outToken = settings.getToken().getBytes(); outData.write(outToken); } catch (IOException e) { return false; } catch (Exception e) { return false; } return true; } /** * check daemon status * @return true == alive, false == dead */ private boolean checkStatus() { if (client == null) return false; return client.isConnected(); } /** * restart the daemon * @return true == success, false == fail */ private boolean restartDaemon() { return CoreUtil.execCore(ipcContext); } /** * force connect to osmcore * @return true == connected, false == not connected */ public boolean forceConnect() { if(restartDaemon()) { waitTime(1); return connect(); } return false; } /** * send a force exit command */ public void forceExit() { if(!checkStatus()) return; FlatBufferBuilder flatbuffer = new FlatBufferBuilder(1); // prepare an empty ipcData int [] ipcDataArray = new int[1]; byte [] ipcDataPayLoad = new byte[1]; int emptyPayLoad = ipcData.createPayloadVector(flatbuffer, ipcDataPayLoad); ipcDataArray[0] = ipcData.createipcData(flatbuffer, ipcCategory.NONEXIST, emptyPayLoad); int ipcDataList = ipcMessage.createDataVector(flatbuffer, ipcDataArray); // prepare an ipcMessage ipcMessage.startipcMessage(flatbuffer); ipcMessage.addType(flatbuffer, ipcType.EXIT); ipcMessage.addData(flatbuffer, ipcDataList); int message = ipcMessage.endipcMessage(flatbuffer); ipcMessage.finishipcMessageBuffer(flatbuffer, message); // send try { OutputStream outputStream = client.getOutputStream(); outputStream.write(flatbuffer.sizedByteArray()); } catch (IOException e) {} return; } /** * send command to osmcore * @param[in] category command type * @param[in] arguments arguments for command */ public void sendCommand(byte category, Object... arguments) { FlatBufferBuilder cmdFlatBuffer = new FlatBufferBuilder(1); // prepare arguments of commandInfo int commandArgsIndex = 0; int [] commandArgs = new int[arguments.length]; for (Object argument: arguments) { if (String.class.isInstance(argument)) commandArgs[commandArgsIndex] = cmdFlatBuffer.createString((String) argument); else commandArgs[commandArgsIndex] = cmdFlatBuffer.createString(argument.toString()); commandArgsIndex++; } // prepare commandInfo int commandArgsList = commandInfo.createArgumentsVector(cmdFlatBuffer, commandArgs); int commandInfoObject = commandInfo.createcommandInfo(cmdFlatBuffer, commandArgsList); commandInfo.finishcommandInfoBuffer(cmdFlatBuffer, commandInfoObject); // prepare the payload of ipcMessage FlatBufferBuilder flatbuffer = new FlatBufferBuilder(1); int commandPayLoad = ipcData.createPayloadVector(flatbuffer, cmdFlatBuffer.sizedByteArray()); // prepare an ipcMessage int [] cmdDataArray = new int[1]; cmdDataArray[0] = ipcData.createipcData(flatbuffer, category, commandPayLoad); int cmdDataList = ipcMessage.createDataVector(flatbuffer, cmdDataArray); int cmdMessage = ipcMessage.createipcMessage(flatbuffer, ipcType.COMMAND, cmdDataList); ipcMessage.finishipcMessageBuffer(flatbuffer, cmdMessage); // send try { OutputStream outputStream = client.getOutputStream(); outputStream.write(flatbuffer.sizedByteArray()); } catch (IOException e) {} } /** * add a request to queue * @param[in] action request actions which is a array * @param[in] sec how long before execute the request * @param[in] obj callback when request done * @return true == success, false == fail */ public boolean addRequest(byte[] action, int sec, ipcClientListener obj) { // check worker if (worker == null) { worker = new ipcTask(); worker.execute(); } // add a task into queue sec = (1000 * sec); long timestamp = System.currentTimeMillis()+ sec; try { cmdQueueLock.acquire(); QueuedTask newTask = new QueuedTask(action, timestamp, obj); if(!cmdQueue.contains(newTask)) cmdQueue.add(newTask); cmdQueueLock.release(); } catch (InterruptedException e) { e.printStackTrace(); } return true; } /** * search specific listener and remove its requests from queue * @param[in] listener */ public void removeRequest(ipcClientListener obj) { try { QueuedTask checkObj = new QueuedTask(null, 0, obj); cmdQueueLock.acquire(); cmdQueue.remove(checkObj); cmdQueueLock.release(); } catch (InterruptedException e) { } } /** * remove all requests from queue */ public void removeAllRequest() { try { cmdQueueLock.acquire(); cmdQueue.clear(); cmdQueueLock.release(); } catch (InterruptedException e) { } } /** * disconnect with daemon */ public void disconnect() { if(client == null) { return; } removeAllRequest(); try { client.close(); } catch (Exception e) {} return; } /** * wait for specific seconds * @param[in] seconds */ private void waitTime(int sec) { try { // sleep Thread.sleep(1000*sec); } catch (InterruptedException e) { } } /* AsyncTask */ private class ipcTask extends AsyncTask<Void, QueuedTask, Void> { private boolean prepareIpc() { // check connection's status if (!checkStatus()) { restartDaemon(); if(!connect()) return false; } return true; } /** * background worker for IPC communication */ @Override protected Void doInBackground(Void... params) { QueuedTask job = null; while (true) { // check connection's status if (!prepareIpc()) { waitTime(1); continue; } // search jobs try { cmdQueueLock.acquire(); job = cmdQueue.peek(); cmdQueueLock.release(); } catch (InterruptedException e) {} // if no jobs, just wait if(job == null) { waitTime(1); continue; } // if job isn't ready to go , just wait if( job.timestamp > System.currentTimeMillis()) { waitTime(1); continue; } // process job try { cmdQueueLock.acquire(); job = cmdQueue.poll(); cmdQueueLock.release(); } catch (InterruptedException e) { } // if no jobs, just wait if(job == null) { waitTime(1); continue; } job.result = sendMessage(job); // if result is empty, just disconnect if(job.result == null) disconnect(); publishProgress(job); job = null; } } /** * send result to the requester * @param[in] job the job has been finished */ protected void onProgressUpdate(QueuedTask... job) { if (job.length == 0) return; QueuedTask procJob = job[job.length - 1]; procJob.listener.onRecvData(procJob.result); procJob.listener = null; procJob.action = null; procJob.result = null; } /** * send request to osmcore and get data when data is ready * @param[in] job the new job * @return result the new data */ private byte [] sendMessage(QueuedTask job) { byte [] result = null; try { // prepare ipcMessage FlatBufferBuilder flatbuffer = new FlatBufferBuilder(1); // prepare a vector for ipcData int [] ipcDataArray = new int[job.action.length]; for (int index = 0; index < job.action.length; index++) { // prepare empty data payload byte [] ipcDataPayLoad = new byte[1]; int emptyPayLoad = ipcData.createPayloadVector(flatbuffer, ipcDataPayLoad); ipcDataArray[index] = ipcData.createipcData(flatbuffer, job.action[index], emptyPayLoad); } int ipcDataList = ipcMessage.createDataVector(flatbuffer, ipcDataArray); // prepare ipcMessage ipcMessage.startipcMessage(flatbuffer); ipcMessage.addType(flatbuffer, ipcType.ACTION); ipcMessage.addData(flatbuffer, ipcDataList); int message = ipcMessage.endipcMessage(flatbuffer); ipcMessage.finishipcMessageBuffer(flatbuffer, message); // send message and wait result OutputStream outputStream = client.getOutputStream(); InputStream inputStream = client.getInputStream(); // send outputStream.write(flatbuffer.sizedByteArray()); // receive (blocking mode) & read data size int totalSize = 0; byte[] sizeBuffer = new byte[4]; if( inputStream.read(sizeBuffer, 0, 4) == 0) throw new IOException("Unable to get transfer size"); // convert byte to int totalSize = (int) sizeBuffer[0] & 0xFF; totalSize |= (int) (sizeBuffer[1] & 0xFF ) << 8; totalSize |= (int) (sizeBuffer[2] & 0xFF ) << 16; totalSize |= (int) (sizeBuffer[3] & 0xFF ) << 24; // check limit (10M) if(totalSize > IpcConnection.recvBufferSize) throw new Exception("Excced memory limit"); if (totalSize == 0) throw new Exception("No Data"); // prepare enough buffer size result = new byte[totalSize]; // receive data int transferSize = 0; while(transferSize != totalSize) transferSize += inputStream.read(result, transferSize, totalSize-transferSize); } catch (Exception e) { result = null; } return result; } } }