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
17package com.android.nfc.ndefpush;
18
19import android.util.Log;
20
21import android.nfc.NdefMessage;
22import android.nfc.FormatException;
23
24import java.io.ByteArrayInputStream;
25import java.io.DataInputStream;
26import java.io.ByteArrayOutputStream;
27import java.io.DataOutputStream;
28
29/**
30 * Implementation of the NDEF Push Protocol.
31 */
32public 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
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
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
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
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
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