1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.nfc.ndefpush;
18 
19 import android.util.Log;
20 
21 import android.nfc.NdefMessage;
22 import android.nfc.FormatException;
23 
24 import java.io.ByteArrayInputStream;
25 import java.io.DataInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.DataOutputStream;
28 
29 /**
30  * Implementation of the NDEF Push Protocol.
31  */
32 public class NdefPushProtocol {
33     public static final byte ACTION_IMMEDIATE = (byte) 0x01;
34     public static final byte ACTION_BACKGROUND = (byte) 0x02;
35 
36     private static final String TAG = "NdefMessageSet";
37     private static final byte VERSION = 1;
38 
39     private int mNumMessages;
40     private byte[] mActions;
41     private NdefMessage[] mMessages;
42 
NdefPushProtocol(NdefMessage msg, byte action)43     public NdefPushProtocol(NdefMessage msg, byte action) {
44         mNumMessages = 1;
45         mActions = new byte[1];
46         mActions[0] = action;
47         mMessages = new NdefMessage[1];
48         mMessages[0] = msg;
49     }
50 
NdefPushProtocol(byte[] actions, NdefMessage[] messages)51     public NdefPushProtocol(byte[] actions, NdefMessage[] messages) {
52         if (actions.length != messages.length || actions.length == 0) {
53             throw new IllegalArgumentException(
54                     "actions and messages must be the same size and non-empty");
55         }
56 
57         // Keep a copy of these arrays
58         int numMessages = actions.length;
59         mActions = new byte[numMessages];
60         System.arraycopy(actions, 0, mActions, 0, numMessages);
61         mMessages = new NdefMessage[numMessages];
62         System.arraycopy(messages, 0, mMessages, 0, numMessages);
63         mNumMessages = numMessages;
64     }
65 
NdefPushProtocol(byte[] data)66     public NdefPushProtocol(byte[] data) throws FormatException {
67         ByteArrayInputStream buffer = new ByteArrayInputStream(data);
68         DataInputStream input = new DataInputStream(buffer);
69 
70         // Check version of protocol
71         byte version;
72         try {
73             version = input.readByte();
74         } catch (java.io.IOException e) {
75             Log.w(TAG, "Unable to read version");
76             throw new FormatException("Unable to read version");
77         }
78 
79         if(version != VERSION) {
80             Log.w(TAG, "Got version " + version + ",  expected " + VERSION);
81             throw new FormatException("Got version " + version + ",  expected " + VERSION);
82         }
83 
84         // Read numMessages
85         try {
86             mNumMessages = input.readInt();
87         } catch(java.io.IOException e) {
88             Log.w(TAG, "Unable to read numMessages");
89             throw new FormatException("Error while parsing NdefMessageSet");
90         }
91         if(mNumMessages == 0) {
92             Log.w(TAG, "No NdefMessage inside NdefMessageSet packet");
93             throw new FormatException("Error while parsing NdefMessageSet");
94         }
95 
96         // Read actions and messages
97         mActions = new byte[mNumMessages];
98         mMessages = new NdefMessage[mNumMessages];
99         for(int i = 0; i < mNumMessages; i++) {
100             // Read action
101             try {
102                 mActions[i] = input.readByte();
103             } catch(java.io.IOException e) {
104                 Log.w(TAG, "Unable to read action for message " + i);
105                 throw new FormatException("Error while parsing NdefMessageSet");
106             }
107             // Read message length
108             int length;
109             try {
110                 length = input.readInt();
111             } catch(java.io.IOException e) {
112                 Log.w(TAG, "Unable to read length for message " + i);
113                 throw new FormatException("Error while parsing NdefMessageSet");
114             }
115             byte[] bytes = new byte[length];
116             // Read message
117             int lengthRead;
118             try {
119                 lengthRead = input.read(bytes);
120             } catch(java.io.IOException e) {
121                 Log.w(TAG, "Unable to read bytes for message " + i);
122                 throw new FormatException("Error while parsing NdefMessageSet");
123             }
124             if(length != lengthRead) {
125                 Log.w(TAG, "Read " + lengthRead + " bytes but expected " +
126                     length);
127                 throw new FormatException("Error while parsing NdefMessageSet");
128             }
129             // Create and store NdefMessage
130             try {
131                 mMessages[i] = new NdefMessage(bytes);
132             } catch(FormatException e) {
133                 throw e;
134             }
135         }
136     }
137 
getImmediate()138     public NdefMessage getImmediate() {
139         // Find and return the first immediate message
140         for(int i = 0; i < mNumMessages; i++) {
141             if(mActions[i] == ACTION_IMMEDIATE) {
142                 return mMessages[i];
143             }
144         }
145         return null;
146     }
147 
toByteArray()148     public byte[] toByteArray() {
149         ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
150         DataOutputStream output = new DataOutputStream(buffer);
151 
152         try {
153             output.writeByte(VERSION);
154             output.writeInt(mNumMessages);
155             for(int i = 0; i < mNumMessages; i++) {
156                 output.writeByte(mActions[i]);
157                 byte[] bytes = mMessages[i].toByteArray();
158                 output.writeInt(bytes.length);
159                 output.write(bytes);
160             }
161         } catch(java.io.IOException e) {
162             return null;
163         }
164 
165         return buffer.toByteArray();
166     }
167 }
168