12ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson/*
22ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * Copyright (C) 2011 The Android Open Source Project
32ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson *
42ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * Licensed under the Apache License, Version 2.0 (the "License");
52ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * you may not use this file except in compliance with the License.
62ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * You may obtain a copy of the License at
72ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson *
82ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson *      http://www.apache.org/licenses/LICENSE-2.0
92ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson *
102ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * Unless required by applicable law or agreed to in writing, software
112ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * distributed under the License is distributed on an "AS IS" BASIS,
122ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * See the License for the specific language governing permissions and
142ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * limitations under the License.
152ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson */
162ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
172ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonpackage com.android.nfc.snep;
182ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
194a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport com.android.nfc.DeviceHost.LlcpSocket;
204a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport com.android.nfc.LlcpException;
212ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport com.android.nfc.NfcService;
222ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
232ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport android.nfc.NdefMessage;
242ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport android.util.Log;
252ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
264a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport java.io.IOException;
274a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton
282ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonpublic final class SnepClient {
292ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private static final String TAG = "SnepClient";
302ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private static final boolean DBG = false;
312ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private static final int DEFAULT_ACCEPTABLE_LENGTH = 100*1024;
32525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project    private static final int DEFAULT_MIU = 128;
33525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project    private static final int DEFAULT_RWSIZE = 1;
342ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    SnepMessenger mMessenger = null;
35154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson    private final Object mTransmissionLock = new Object();
362ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
372ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private final String mServiceName;
382ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private final int mPort;
39154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson    private int  mState = DISCONNECTED;
402ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private final int mAcceptableLength;
412ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    private final int mFragmentLength;
42525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project    private final int mMiu;
43525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project    private final int mRwSize;
442ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
45154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson    private static final int DISCONNECTED = 0;
46154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson    private static final int CONNECTING = 1;
47154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson    private static final int CONNECTED = 2;
48154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson
492ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    public SnepClient() {
502ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mServiceName = SnepServer.DEFAULT_SERVICE_NAME;
512ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mPort = SnepServer.DEFAULT_PORT;
522ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
532ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mFragmentLength = -1;
54525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mMiu = DEFAULT_MIU;
55525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mRwSize = DEFAULT_RWSIZE;
562ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
572ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
582ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    public SnepClient(String serviceName) {
592ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mServiceName = serviceName;
602ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mPort = -1;
612ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
622ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mFragmentLength = -1;
63525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mMiu = DEFAULT_MIU;
64525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mRwSize = DEFAULT_RWSIZE;
65525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project    }
66525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project
67525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project    public SnepClient(int miu, int rwSize) {
68525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mServiceName = SnepServer.DEFAULT_SERVICE_NAME;
69525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mPort = SnepServer.DEFAULT_PORT;
70525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
71525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mFragmentLength = -1;
72525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mMiu = miu;
73525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mRwSize = rwSize;
742ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
752ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
762ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    SnepClient(String serviceName, int fragmentLength) {
772ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mServiceName = serviceName;
782ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mPort = -1;
792ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mAcceptableLength = DEFAULT_ACCEPTABLE_LENGTH;
802ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mFragmentLength = fragmentLength;
81525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mMiu = DEFAULT_MIU;
82525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mRwSize = DEFAULT_RWSIZE;
832ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
842ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
852ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    SnepClient(String serviceName, int acceptableLength, int fragmentLength) {
862ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mServiceName = serviceName;
872ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mPort = -1;
882ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mAcceptableLength = acceptableLength;
892ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        mFragmentLength = fragmentLength;
90525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mMiu = DEFAULT_MIU;
91525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mRwSize = DEFAULT_RWSIZE;
922ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
932ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
942ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    public void put(NdefMessage msg) throws IOException {
95154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        SnepMessenger messenger;
962ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        synchronized (this) {
97154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (mState != CONNECTED) {
982ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson                throw new IOException("Socket not connected.");
992ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            }
100154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            messenger = mMessenger;
101154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        }
1022ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
103154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        synchronized (mTransmissionLock) {
1042ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            try {
105154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                messenger.sendMessage(SnepMessage.getPutRequest(msg));
106154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                messenger.getMessage();
1072ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            } catch (SnepException e) {
1082ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson                throw new IOException(e);
1092ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            }
1102ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        }
1112ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
1122ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
1132ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    public SnepMessage get(NdefMessage msg) throws IOException {
114154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        SnepMessenger messenger;
1152ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        synchronized (this) {
116154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (mState != CONNECTED) {
1172ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson                throw new IOException("Socket not connected.");
1182ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            }
119154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            messenger = mMessenger;
120154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        }
1212ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
122154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        synchronized (mTransmissionLock) {
1232ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            try {
124154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                messenger.sendMessage(SnepMessage.getGetRequest(mAcceptableLength, msg));
125154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                return messenger.getMessage();
1262ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            } catch (SnepException e) {
1272ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson                throw new IOException(e);
1282ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            }
1292ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        }
1302ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
1312ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
1322ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    public void connect() throws IOException {
1332ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        synchronized (this) {
134154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (mState != DISCONNECTED) {
135154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                throw new IOException("Socket already in use.");
136154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            }
137154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            mState = CONNECTING;
138154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        }
139154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson
140d1246caeb5eb22aeb7e411e357e89ec945be1d87Ben Dodson        LlcpSocket socket = null;
141154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        SnepMessenger messenger;
142154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        try {
143154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (DBG) Log.d(TAG, "about to create socket");
144154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            // Connect to the snep server on the remote side
145525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            socket = NfcService.getInstance().createLlcpSocket(0, mMiu, mRwSize, 1024);
146154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (socket == null) {
147154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                throw new IOException("Could not connect to socket.");
148154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            }
149154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (mPort == -1) {
150154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                if (DBG) Log.d(TAG, "about to connect to service " + mServiceName);
1514a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton                socket.connectToService(mServiceName);
152154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            } else {
153154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                if (DBG) Log.d(TAG, "about to connect to port " + mPort);
1544a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton                socket.connectToSap(mPort);
1552ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            }
1564a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton            int miu = socket.getRemoteMiu();
157154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            int fragmentLength = (mFragmentLength == -1) ?  miu : Math.min(miu, mFragmentLength);
158154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            messenger = new SnepMessenger(true, socket, fragmentLength);
159154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        } catch (LlcpException e) {
1603329e45c618c5896cb662f686930a75eb2ee5bbcMartijn Coenen            synchronized (this) {
1613329e45c618c5896cb662f686930a75eb2ee5bbcMartijn Coenen                mState = DISCONNECTED;
1623329e45c618c5896cb662f686930a75eb2ee5bbcMartijn Coenen            }
163154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            throw new IOException("Could not connect to socket");
1640a0a2428190767616e00435930a566d7cb76888fMartijn Coenen        } catch (IOException e) {
1650a0a2428190767616e00435930a566d7cb76888fMartijn Coenen            if (socket != null) {
1660a0a2428190767616e00435930a566d7cb76888fMartijn Coenen                try {
1670a0a2428190767616e00435930a566d7cb76888fMartijn Coenen                    socket.close();
1680a0a2428190767616e00435930a566d7cb76888fMartijn Coenen                } catch (IOException e2) {
1690a0a2428190767616e00435930a566d7cb76888fMartijn Coenen                }
1700a0a2428190767616e00435930a566d7cb76888fMartijn Coenen            }
1713329e45c618c5896cb662f686930a75eb2ee5bbcMartijn Coenen            synchronized (this) {
1723329e45c618c5896cb662f686930a75eb2ee5bbcMartijn Coenen                mState = DISCONNECTED;
1733329e45c618c5896cb662f686930a75eb2ee5bbcMartijn Coenen            }
1740a0a2428190767616e00435930a566d7cb76888fMartijn Coenen            throw new IOException("Failed to connect to socket");
175154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        }
176154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson
177154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson        synchronized (this) {
178154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            mMessenger = messenger;
179154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            mState = CONNECTED;
1802ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        }
1812ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
1822ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson
1832ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    public void close() {
1842ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        synchronized (this) {
185154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson            if (mMessenger != null) {
1862ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson               try {
187154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                   mMessenger.close();
1882ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson               } catch (IOException e) {
1892ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson                   // ignore
1902ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson               } finally {
1912ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson                   mMessenger = null;
192154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson                   mState = DISCONNECTED;
1932ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson               }
1942ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson            }
1952ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson        }
1962ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson    }
1972ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson}
198