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