package com.codegy.aerlink.services.notifications; import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.Arrays; /** * Created by Guiye on 18/5/15. */ public class NotificationPacketProcessor { private enum Status { Init, AppId, Title, Message, PositiveAction, NegativeAction, Finished } private static final String LOG_TAG = NotificationPacketProcessor.class.getSimpleName(); private NotificationData notificationData; private ByteArrayOutputStream processingAttribute; private byte[] bytesFromPreviousPacket; // The number of bytes left to process on the current packet private int bytesLeftToProcess; // The number of bytes of the current attribute being processed that are in the next packet private int attributeBytesInNextPacket; private Status processingStatus; public NotificationPacketProcessor(NotificationData notificationData) { processingStatus = Status.Init; bytesLeftToProcess = 0; attributeBytesInNextPacket = 0; bytesFromPreviousPacket = new byte[] {}; processingAttribute = new ByteArrayOutputStream(); this.notificationData = notificationData; } public NotificationData getNotificationData() { return notificationData; } public boolean hasFinishedProcessing() { return processingStatus == Status.Finished || notificationData == null; } private int getAttributeLength(byte[] packet, int lengthIndex){ //get att0's length byte[] byteLength = {packet[lengthIndex + 2], packet[lengthIndex + 1]}; BigInteger length = new BigInteger(byteLength); return length.intValue(); } public static byte[] concat(byte[] a, byte[] b) { int aLen = a.length; int bLen = b.length; byte[] c = new byte[aLen+bLen]; System.arraycopy(a, 0, c, 0, aLen); System.arraycopy(b, 0, c, aLen, bLen); return c; } private void updateProcessingStatus() { switch (processingStatus) { case Init: processingStatus = Status.Title; break; case AppId: processingStatus = Status.Title; try { notificationData.setAppId(new String(processingAttribute.toByteArray(), "UTF-8")); processingAttribute.reset(); Log.d(LOG_TAG, "App ID: " + notificationData.getAppId()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } break; case Title: processingStatus = Status.Message; try { notificationData.setTitle(new String(processingAttribute.toByteArray(), "UTF-8")); processingAttribute.reset(); Log.d(LOG_TAG, "Title: " + notificationData.getTitle()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } break; case Message: if (notificationData.hasPositiveAction()) { processingStatus = Status.PositiveAction; } else if (notificationData.hasNegativeAction()) { processingStatus = Status.NegativeAction; } else { processingStatus = Status.Finished; NotificationDataExpander.updateData(notificationData); } try { notificationData.setMessage(new String(processingAttribute.toByteArray(), "UTF-8")); processingAttribute.reset(); Log.d(LOG_TAG, "Message: " + notificationData.getMessage()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } break; case PositiveAction: if (notificationData.hasNegativeAction()) { processingStatus = Status.NegativeAction; } else { processingStatus = Status.Finished; NotificationDataExpander.updateData(notificationData); } try { notificationData.setPositiveAction(new String(processingAttribute.toByteArray(), "UTF-8")); processingAttribute.reset(); Log.d(LOG_TAG, "Positive Action: " + notificationData.getPositiveAction()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } break; case NegativeAction: processingStatus = Status.Finished; NotificationDataExpander.updateData(notificationData); try { notificationData.setNegativeAction(new String(processingAttribute.toByteArray(), "UTF-8")); processingAttribute.reset(); Log.d(LOG_TAG, "Negative Action: " + notificationData.getNegativeAction()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } break; } } public void process(byte[] packet) { // Get size of received data packet = concat(bytesFromPreviousPacket, packet); bytesLeftToProcess = packet.length; bytesFromPreviousPacket = new byte[] {}; int attributeIndex; while (bytesLeftToProcess > 0) { if (attributeBytesInNextPacket > 0) { // Still processing attribute started in a previous packet if (bytesLeftToProcess < attributeBytesInNextPacket) { // The attribute is still not finished with this packet // Save attribute data processingAttribute.write(packet, 0, bytesLeftToProcess); // Update bytes left of current attribute attributeBytesInNextPacket -= bytesLeftToProcess; // All bytes have been processed bytesLeftToProcess = 0; } else { // The attribute ends in this packet // Save attribute data processingAttribute.write(packet, 0, attributeBytesInNextPacket); // There may be bytes of another attribute left in this packet bytesLeftToProcess -= attributeBytesInNextPacket; if (bytesLeftToProcess > 0 && bytesLeftToProcess <= 2) { // Not enough bytes to start processing next attribute // Save bytes for next packet bytesFromPreviousPacket = Arrays.copyOfRange(packet, attributeBytesInNextPacket, packet.length); bytesLeftToProcess = 0; } // This attribute's bytes have been processed attributeBytesInNextPacket = 0; updateProcessingStatus(); } } else if (bytesLeftToProcess > 0) { // Attribute index if (processingStatus == Status.Init) { // Previous bytes' data is already known attributeIndex = 5; processingStatus = Status.AppId; } else { attributeIndex = packet.length - bytesLeftToProcess; } // Length of attribute to read int attributeLength = getAttributeLength(packet, attributeIndex); // Not counting bytes offering attribute length info int bytesInCurrentPacket = packet.length - (attributeIndex + 3); if (bytesInCurrentPacket < attributeLength) { // The attribute is divided // Save attribute data processingAttribute.write(packet, attributeIndex + 3, bytesInCurrentPacket); // Update bytes left of current attribute attributeBytesInNextPacket = attributeLength - bytesInCurrentPacket; // All bytes have been processed bytesLeftToProcess = 0; } else { // The attribute ends in this packet // Save attribute data processingAttribute.write(packet, attributeIndex + 3, attributeLength); // This attribute's bytes have been processed attributeBytesInNextPacket = 0; // There may be bytes of another attribute left in this packet bytesLeftToProcess = bytesInCurrentPacket - attributeLength; if (bytesLeftToProcess > 0 && bytesLeftToProcess <= 2) { // Not enough bytes to start processing next attribute // Offset of processed bytes int offset = attributeIndex + 3 + attributeLength; // Save bytes for next packet bytesFromPreviousPacket = Arrays.copyOfRange(packet, offset, packet.length); bytesLeftToProcess = 0; } updateProcessingStatus(); } } } } }