1f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen/*
2f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * Copyright (C) 2012 The Android Open Source Project
3f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen *
4f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * Licensed under the Apache License, Version 2.0 (the "License");
5f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * you may not use this file except in compliance with the License.
6f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * You may obtain a copy of the License at
7f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen *
8f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen *      http://www.apache.org/licenses/LICENSE-2.0
9f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen *
10f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * Unless required by applicable law or agreed to in writing, software
11f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * distributed under the License is distributed on an "AS IS" BASIS,
12f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * See the License for the specific language governing permissions and
14f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen * limitations under the License.
15f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen */
16f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenpackage com.android.nfc.handover;
17f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
18b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.DeviceHost.LlcpServerSocket;
19b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.DeviceHost.LlcpSocket;
20b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.LlcpException;
21b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.NfcService;
22b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.beam.BeamManager;
23b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.beam.BeamReceiveService;
24b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport com.android.nfc.beam.BeamTransferRecord;
25b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
26b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport android.bluetooth.BluetoothDevice;
27b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport android.content.Context;
28b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport android.content.Intent;
29f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenimport android.nfc.FormatException;
30f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenimport android.nfc.NdefMessage;
31b82d80d891077ccd74729e4143925a66eb89eef2Andres Moralesimport android.os.UserHandle;
32f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenimport android.util.Log;
33f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
34f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenimport java.io.ByteArrayOutputStream;
35f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenimport java.io.IOException;
36f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenimport java.util.Arrays;
37f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
38f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenenpublic final class HandoverServer {
39b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final String HANDOVER_SERVICE_NAME = "urn:nfc:sn:handover";
40b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final String TAG = "HandoverServer";
41b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final Boolean DBG = false;
42f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
43b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    static final int MIU = 128;
44f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
45b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    final HandoverDataParser mHandoverDataParser;
46f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    final int mSap;
47f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    final Callback mCallback;
48b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    private final Context mContext;
49f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
50f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    ServerThread mServerThread = null;
51f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    boolean mServerRunning = false;
52f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
53f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    public interface Callback {
54f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        void onHandoverRequestReceived();
55aaca83594d177442e993a8ba68349a5f7cb31200Andres Morales        void onHandoverBusy();
56f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    }
57f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
58b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales    public HandoverServer(Context context, int sap, HandoverDataParser manager, Callback callback) {
59b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mContext = context;
60f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        mSap = sap;
61b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales        mHandoverDataParser = manager;
62f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        mCallback = callback;
63f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    }
64f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
65f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    public synchronized void start() {
66f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        if (mServerThread == null) {
67f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mServerThread = new ServerThread();
68f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mServerThread.start();
69f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mServerRunning = true;
70f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        }
71f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    }
72f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
73f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    public synchronized void stop() {
74f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        if (mServerThread != null) {
75f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mServerThread.shutdown();
76f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mServerThread = null;
77f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mServerRunning = false;
78f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        }
79f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    }
80f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
81f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    private class ServerThread extends Thread {
82f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        private boolean mThreadRunning = true;
83f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        LlcpServerSocket mServerSocket;
84f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
85f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        @Override
86f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        public void run() {
87f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            boolean threadRunning;
88f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            synchronized (HandoverServer.this) {
89f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                threadRunning = mThreadRunning;
90f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            }
91f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
92f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            while (threadRunning) {
93f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                try {
94f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    synchronized (HandoverServer.this) {
95f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        mServerSocket = NfcService.getInstance().createLlcpServerSocket(mSap,
96f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                                HANDOVER_SERVICE_NAME, MIU, 1, 1024);
97f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
98f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    if (mServerSocket == null) {
99f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        if (DBG) Log.d(TAG, "failed to create LLCP service socket");
100f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        return;
101f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
102f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    if (DBG) Log.d(TAG, "created LLCP service socket");
103f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    synchronized (HandoverServer.this) {
104f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        threadRunning = mThreadRunning;
105f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
106f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
107f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    while (threadRunning) {
108f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        LlcpServerSocket serverSocket;
109f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        synchronized (HandoverServer.this) {
110f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            serverSocket = mServerSocket;
111f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
112f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
113f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        if (serverSocket == null) {
114f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            if (DBG) Log.d(TAG, "Server socket shut down.");
115f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            return;
116f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
117f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        if (DBG) Log.d(TAG, "about to accept");
118f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        LlcpSocket communicationSocket = serverSocket.accept();
119f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        if (DBG) Log.d(TAG, "accept returned " + communicationSocket);
120f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        if (communicationSocket != null) {
121f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            new ConnectionThread(communicationSocket).start();
122f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
123f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
124f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        synchronized (HandoverServer.this) {
125f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            threadRunning = mThreadRunning;
126f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
127f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
128f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    if (DBG) Log.d(TAG, "stop running");
129f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                } catch (LlcpException e) {
130f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    Log.e(TAG, "llcp error", e);
131f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                } catch (IOException e) {
132f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    Log.e(TAG, "IO error", e);
133f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                } finally {
134f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    synchronized (HandoverServer.this) {
135f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        if (mServerSocket != null) {
136f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            if (DBG) Log.d(TAG, "about to close");
137f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            try {
138f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                                mServerSocket.close();
139f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            } catch (IOException e) {
140f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                                // ignore
141f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            }
142f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            mServerSocket = null;
143f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
144f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
145f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
146f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
147f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                synchronized (HandoverServer.this) {
148f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    threadRunning = mThreadRunning;
149f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
150f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            }
151f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        }
152f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
153f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        public void shutdown() {
154f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            synchronized (HandoverServer.this) {
155f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                mThreadRunning = false;
156f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                if (mServerSocket != null) {
157f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    try {
158f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        mServerSocket.close();
159f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    } catch (IOException e) {
160f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        // ignore
161f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
162f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    mServerSocket = null;
163f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
164f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            }
165f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        }
166f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    }
167f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
168f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    private class ConnectionThread extends Thread {
169f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        private final LlcpSocket mSock;
170f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
171f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        ConnectionThread(LlcpSocket socket) {
172f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            super(TAG);
173f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            mSock = socket;
174f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        }
175f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
176f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        @Override
177f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        public void run() {
178f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            if (DBG) Log.d(TAG, "starting connection thread");
179f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
180f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
181f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            try {
182f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                boolean running;
183f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                synchronized (HandoverServer.this) {
184f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    running = mServerRunning;
185f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
186f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
187f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                byte[] partial = new byte[mSock.getLocalMiu()];
188f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
189f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                NdefMessage handoverRequestMsg = null;
190f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                while (running) {
191f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    int size = mSock.receive(partial);
192f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    if (size < 0) {
193f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        break;
194f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
195f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    byteStream.write(partial, 0, size);
196f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    // 1) Try to parse a handover request message from bytes received so far
197f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    try {
198f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        handoverRequestMsg = new NdefMessage(byteStream.toByteArray());
199f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    } catch (FormatException e) {
200f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        // Ignore, and try to fetch more bytes
201f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
202f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
203f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    if (handoverRequestMsg != null) {
204b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        BeamManager beamManager = BeamManager.getInstance();
205b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
206b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        if (beamManager.isBeamInProgress()) {
207aaca83594d177442e993a8ba68349a5f7cb31200Andres Morales                            mCallback.onHandoverBusy();
208b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                            break;
209b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        }
210b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
211f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        // 2) convert to handover response
212b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        HandoverDataParser.IncomingHandoverData handoverData
213b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                                = mHandoverDataParser.getIncomingHandoverData(handoverRequestMsg);
214b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        if (handoverData == null) {
215f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            Log.e(TAG, "Failed to create handover response");
216f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            break;
217f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
218f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
219f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        // 3) send handover response
220f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        int offset = 0;
221b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        byte[] buffer = handoverData.handoverSelect.toByteArray();
222f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        int remoteMiu = mSock.getRemoteMiu();
223f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        while (offset < buffer.length) {
224f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            int length = Math.min(buffer.length - offset, remoteMiu);
225f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            byte[] tmpBuffer = Arrays.copyOfRange(buffer, offset, offset+length);
226f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            mSock.send(tmpBuffer);
227f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                            offset += length;
228f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        }
229f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        // We're done
230f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        mCallback.onHandoverRequestReceived();
231b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        if (!beamManager.startBeamReceive(mContext, handoverData.handoverData)) {
232aaca83594d177442e993a8ba68349a5f7cb31200Andres Morales                            mCallback.onHandoverBusy();
233aaca83594d177442e993a8ba68349a5f7cb31200Andres Morales                            break;
234b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales                        }
235be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                        // We can process another handover transfer
236be1939b4b6003ac7a65fcb95a3912f5e1ce8e75fThe Android Open Source Project                        byteStream = new ByteArrayOutputStream();
237f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
238f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
239f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    synchronized (HandoverServer.this) {
240f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                        running = mServerRunning;
241f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    }
242f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
243f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen
244f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            } catch (IOException e) {
245f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                if (DBG) Log.d(TAG, "IOException");
246f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            } finally {
247f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                try {
248f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    if (DBG) Log.d(TAG, "about to close");
249f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    mSock.close();
250f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                } catch (IOException e) {
251f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    // ignore
252f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
253f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                try {
254f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    byteStream.close();
255f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                } catch (IOException e) {
256f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                    // ignore
257f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen                }
258f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            }
259f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen            if (DBG) Log.d(TAG, "finished connection thread");
260f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen        }
261f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen    }
262f5a196b643c654c7ea98a5e2935e3bff6683399bMartijn Coenen}
263b82d80d891077ccd74729e4143925a66eb89eef2Andres Morales
264