1/*
2 * Copyright (C) 2011 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.snep;
18
19import com.android.nfc.DeviceHost.LlcpSocket;
20import com.android.nfc.LlcpException;
21import com.android.nfc.NfcService;
22
23import android.nfc.NdefMessage;
24import android.util.Log;
25
26import java.io.IOException;
27
28public final class SnepClient {
29    private static final String TAG = "SnepClient";
30    private static final boolean DBG = false;
31    private static final int DEFAULT_ACCEPTABLE_LENGTH = 100*1024;
32    private static final int DEFAULT_MIU = 128;
33    private static final int DEFAULT_RWSIZE = 1;
34    SnepMessenger mMessenger = null;
35    private final Object mTransmissionLock = new Object();
36
37    private final String mServiceName;
38    private final int mPort;
39    private int  mState = DISCONNECTED;
40    private final int mAcceptableLength;
41    private final int mFragmentLength;
42    private final int mMiu;
43    private final int mRwSize;
44
45    private static final int DISCONNECTED = 0;
46    private static final int CONNECTING = 1;
47    private static final int CONNECTED = 2;
48
49    public SnepClient() {
50        mServiceName = SnepServer.DEFAULT_SERVICE_NAME;
51        mPort = SnepServer.DEFAULT_PORT;
52        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
53        mFragmentLength = -1;
54        mMiu = DEFAULT_MIU;
55        mRwSize = DEFAULT_RWSIZE;
56    }
57
58    public SnepClient(String serviceName) {
59        mServiceName = serviceName;
60        mPort = -1;
61        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
62        mFragmentLength = -1;
63        mMiu = DEFAULT_MIU;
64        mRwSize = DEFAULT_RWSIZE;
65    }
66
67    public SnepClient(int miu, int rwSize) {
68        mServiceName = SnepServer.DEFAULT_SERVICE_NAME;
69        mPort = SnepServer.DEFAULT_PORT;
70        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
71        mFragmentLength = -1;
72        mMiu = miu;
73        mRwSize = rwSize;
74    }
75
76    SnepClient(String serviceName, int fragmentLength) {
77        mServiceName = serviceName;
78        mPort = -1;
79        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
80        mFragmentLength = fragmentLength;
81        mMiu = DEFAULT_MIU;
82        mRwSize = DEFAULT_RWSIZE;
83    }
84
85    SnepClient(String serviceName, int acceptableLength, int fragmentLength) {
86        mServiceName = serviceName;
87        mPort = -1;
88        mAcceptableLength = acceptableLength;
89        mFragmentLength = fragmentLength;
90        mMiu = DEFAULT_MIU;
91        mRwSize = DEFAULT_RWSIZE;
92    }
93
94    public void put(NdefMessage msg) throws IOException {
95        SnepMessenger messenger;
96        synchronized (this) {
97            if (mState != CONNECTED) {
98                throw new IOException("Socket not connected.");
99            }
100            messenger = mMessenger;
101        }
102
103        synchronized (mTransmissionLock) {
104            try {
105                messenger.sendMessage(SnepMessage.getPutRequest(msg));
106                messenger.getMessage();
107            } catch (SnepException e) {
108                throw new IOException(e);
109            }
110        }
111    }
112
113    public SnepMessage get(NdefMessage msg) throws IOException {
114        SnepMessenger messenger;
115        synchronized (this) {
116            if (mState != CONNECTED) {
117                throw new IOException("Socket not connected.");
118            }
119            messenger = mMessenger;
120        }
121
122        synchronized (mTransmissionLock) {
123            try {
124                messenger.sendMessage(SnepMessage.getGetRequest(mAcceptableLength, msg));
125                return messenger.getMessage();
126            } catch (SnepException e) {
127                throw new IOException(e);
128            }
129        }
130    }
131
132    public void connect() throws IOException {
133        synchronized (this) {
134            if (mState != DISCONNECTED) {
135                throw new IOException("Socket already in use.");
136            }
137            mState = CONNECTING;
138        }
139
140        LlcpSocket socket = null;
141        SnepMessenger messenger;
142        try {
143            if (DBG) Log.d(TAG, "about to create socket");
144            // Connect to the snep server on the remote side
145            socket = NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024);
146            if (socket == null) {
147                throw new IOException("Could not connect to socket.");
148            }
149            if (mPort == -1) {
150                if (DBG) Log.d(TAG, "about to connect to service " + mServiceName);
151                socket.connectToService(mServiceName);
152            } else {
153                if (DBG) Log.d(TAG, "about to connect to port " + mPort);
154                socket.connectToSap(mPort);
155            }
156            int miu = socket.getRemoteMiu();
157            int fragmentLength = (mFragmentLength == -1) ?  miu : Math.min(miu, mFragmentLength);
158            messenger = new SnepMessenger(true, socket, fragmentLength);
159        } catch (LlcpException e) {
160            synchronized (this) {
161                mState = DISCONNECTED;
162            }
163            throw new IOException("Could not connect to socket");
164        } catch (IOException e) {
165            if (socket != null) {
166                try {
167                    socket.close();
168                } catch (IOException e2) {
169                }
170            }
171            synchronized (this) {
172                mState = DISCONNECTED;
173            }
174            throw new IOException("Failed to connect to socket");
175        }
176
177        synchronized (this) {
178            mMessenger = messenger;
179            mState = CONNECTED;
180        }
181    }
182
183    public void close() {
184        synchronized (this) {
185            if (mMessenger != null) {
186               try {
187                   mMessenger.close();
188               } catch (IOException e) {
189                   // ignore
190               } finally {
191                   mMessenger = null;
192                   mState = DISCONNECTED;
193               }
194            }
195        }
196    }
197}
198