1 /*
2  * Copyright (C) 2012 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 package com.android.nfc.handover;
17 
18 import android.nfc.FormatException;
19 import android.nfc.NdefMessage;
20 import android.util.Log;
21 
22 import com.android.nfc.LlcpException;
23 import com.android.nfc.NfcService;
24 import com.android.nfc.DeviceHost.LlcpSocket;
25 
26 import java.io.ByteArrayOutputStream;
27 import java.io.IOException;
28 import java.util.Arrays;
29 
30 public final class HandoverClient {
31     private static final String TAG = "HandoverClient";
32     private static final int MIU = 128;
33     private static final boolean DBG = false;
34 
35     private static final int DISCONNECTED = 0;
36     private static final int CONNECTING = 1;
37     private static final int CONNECTED = 2;
38 
39     private static final Object mLock = new Object();
40 
41     // Variables below synchronized on mLock
42     LlcpSocket mSocket;
43     int mState;
44 
connect()45     public void connect() throws IOException {
46         synchronized (mLock) {
47             if (mState != DISCONNECTED) {
48                 throw new IOException("Socket in use.");
49             }
50             mState = CONNECTING;
51         }
52         NfcService service = NfcService.getInstance();
53         LlcpSocket sock = null;
54         try {
55             sock = service.createLlcpSocket(0, MIU, 1, 1024);
56         } catch (LlcpException e) {
57             synchronized (mLock) {
58                 mState = DISCONNECTED;
59             }
60             throw new IOException("Could not create socket");
61         }
62         try {
63             if (DBG) Log.d(TAG, "about to connect to service " +
64                     HandoverServer.HANDOVER_SERVICE_NAME);
65             sock.connectToService(HandoverServer.HANDOVER_SERVICE_NAME);
66         } catch (IOException e) {
67             if (sock != null) {
68                 try {
69                     sock.close();
70                 } catch (IOException e2) {
71                     // Ignore
72                 }
73             }
74             synchronized (mLock) {
75                 mState = DISCONNECTED;
76             }
77             throw new IOException("Could not connect to handover service");
78         }
79         synchronized (mLock) {
80             mSocket = sock;
81             mState = CONNECTED;
82         }
83     }
84 
close()85     public void close() {
86         synchronized (mLock) {
87             if (mSocket != null) {
88                 try {
89                     mSocket.close();
90                 } catch (IOException e) {
91                     // Ignore
92                 }
93                 mSocket = null;
94             }
95             mState = DISCONNECTED;
96         }
97     }
sendHandoverRequest(NdefMessage msg)98     public NdefMessage sendHandoverRequest(NdefMessage msg) throws IOException {
99         if (msg == null) return null;
100 
101         LlcpSocket sock = null;
102         synchronized (mLock) {
103             if (mState != CONNECTED) {
104                 throw new IOException("Socket not connected");
105             }
106             sock = mSocket;
107         }
108         int offset = 0;
109         byte[] buffer = msg.toByteArray();
110         ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
111 
112         try {
113             int remoteMiu = sock.getRemoteMiu();
114             if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
115             while (offset < buffer.length) {
116                 int length = Math.min(buffer.length - offset, remoteMiu);
117                 byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
118                 if (DBG) Log.d(TAG, "about to send a " + length + " byte packet");
119                 sock.send(tmpBuffer);
120                 offset += length;
121             }
122 
123             // Now, try to read back the handover response
124             byte[] partial = new byte[sock.getLocalMiu()];
125             NdefMessage handoverSelectMsg = null;
126             while (true) {
127                 int size = sock.receive(partial);
128                 if (size < 0) {
129                     break;
130                 }
131                 byteStream.write(partial, 0, size);
132                 try {
133                     handoverSelectMsg = new NdefMessage(byteStream.toByteArray());
134                     // If we get here, message is complete
135                     break;
136                 } catch (FormatException e) {
137                     // Ignore, and try to fetch more bytes
138                 }
139             }
140             return handoverSelectMsg;
141         } catch (IOException e) {
142             if (DBG) Log.d(TAG, "couldn't connect to handover service");
143         } finally {
144             if (sock != null) {
145                 try {
146                     if (DBG) Log.d(TAG, "about to close");
147                     sock.close();
148                 } catch (IOException e) {
149                     // Ignore
150                 }
151             }
152             try {
153                 byteStream.close();
154             } catch (IOException e) {
155                 // Ignore
156             }
157         }
158         return null;
159     }
160 }
161