109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly/*
209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * Copyright (c) 2008-2009, Motorola, Inc.
309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly *
409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * All rights reserved.
509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly *
609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * Redistribution and use in source and binary forms, with or without
709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * modification, are permitted provided that the following conditions are met:
809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly *
909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * - Redistributions of source code must retain the above copyright notice,
1009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * this list of conditions and the following disclaimer.
1109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly *
1209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * - Redistributions in binary form must reproduce the above copyright notice,
1309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * this list of conditions and the following disclaimer in the documentation
1409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * and/or other materials provided with the distribution.
1509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly *
1609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * - Neither the name of the Motorola, Inc. nor the names of its contributors
1709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * may be used to endorse or promote products derived from this software
1809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * without specific prior written permission.
1909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly *
2009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * POSSIBILITY OF SUCH DAMAGE.
3109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly */
3209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
3309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellypackage com.android.bluetooth.opp;
3409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
3509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport javax.obex.ClientOperation;
3609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport javax.obex.ClientSession;
3709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport javax.obex.HeaderSet;
3809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport javax.obex.ObexTransport;
3909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport javax.obex.ResponseCodes;
4009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.content.ContentValues;
4209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.content.Context;
4309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.net.Uri;
4409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.os.Handler;
4509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.os.Message;
4609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.os.PowerManager;
4752236de777c23788df8147de15912a57e8bc36ddTao Liejunimport android.os.PowerManager.WakeLock;
4809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.os.Process;
4909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport android.util.Log;
5009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
5109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport java.io.BufferedInputStream;
5209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport java.io.IOException;
5309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport java.io.InputStream;
5409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport java.io.OutputStream;
5509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellyimport java.lang.Thread;
5609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
5709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly/**
5809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly * This class runs as an OBEX client
5909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly */
6009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pellypublic class BluetoothOppObexClientSession implements BluetoothOppObexSession {
6109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
6274ae04c73312403e89db0f8e9bd9601d403b4783fredc    private static final String TAG = "BtOppObexClient";
63ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly    private static final boolean D = Constants.DEBUG;
64ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly    private static final boolean V = Constants.VERBOSE;
6509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
6609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private ClientThread mThread;
6709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
6809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private ObexTransport mTransport;
6909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
7009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private Context mContext;
7109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
7209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private volatile boolean mInterrupted;
7309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
7409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private volatile boolean mWaitingForRemote;
7509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
7609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private Handler mCallback;
7709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
7809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    public BluetoothOppObexClientSession(Context context, ObexTransport transport) {
7909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        if (transport == null) {
8009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            throw new NullPointerException("transport is null");
8109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
8209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mContext = context;
8309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mTransport = transport;
8409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
8509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
8609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    public void start(Handler handler) {
87ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly        if (D) Log.d(TAG, "Start!");
8809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mCallback = handler;
8909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mThread = new ClientThread(mContext, mTransport);
9009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mThread.start();
9109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
9209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
9309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    public void stop() {
94ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly        if (D) Log.d(TAG, "Stop!");
9509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        if (mThread != null) {
9609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            mInterrupted = true;
9709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
9809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mThread.interrupt();
99ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "waiting for thread to terminate");
10009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mThread.join();
10109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mThread = null;
10209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (InterruptedException e) {
103ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "Interrupted waiting for thread to join");
10409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
10509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
10609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mCallback = null;
10709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
10809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
10909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    public void addShare(BluetoothOppShareInfo share) {
11009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mThread.addShare(share);
11109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
11209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
1130ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang    private static int readFully(InputStream is, byte[] buffer, int size) throws IOException {
1140ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang        int done = 0;
1150ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang        while (done < size) {
1160ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang            int got = is.read(buffer, done, size - done);
1170ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang            if (got <= 0) break;
1180ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang            done += got;
1190ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang        }
1200ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang        return done;
1210ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang    }
1220ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang
12309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    private class ClientThread extends Thread {
12452236de777c23788df8147de15912a57e8bc36ddTao Liejun
1251ac5507790a87810061a19dadec36eb328a222eaTao Liejun        private static final int sSleepTime = 500;
12609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
1276769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun        private Context mContext1;
12809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
12909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private BluetoothOppShareInfo mInfo;
13009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
13109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private volatile boolean waitingForShare;
13209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
1336769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun        private ObexTransport mTransport1;
13409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
13509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private ClientSession mCs;
13609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
13752236de777c23788df8147de15912a57e8bc36ddTao Liejun        private WakeLock wakeLock;
13809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
1396769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun        private BluetoothOppSendFileInfo mFileInfo = null;
14009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
1411ac5507790a87810061a19dadec36eb328a222eaTao Liejun        private boolean mConnected = false;
1421ac5507790a87810061a19dadec36eb328a222eaTao Liejun
1431ac5507790a87810061a19dadec36eb328a222eaTao Liejun        public ClientThread(Context context, ObexTransport transport) {
1441ac5507790a87810061a19dadec36eb328a222eaTao Liejun            super("BtOpp ClientThread");
1451ac5507790a87810061a19dadec36eb328a222eaTao Liejun            mContext1 = context;
1461ac5507790a87810061a19dadec36eb328a222eaTao Liejun            mTransport1 = transport;
1471ac5507790a87810061a19dadec36eb328a222eaTao Liejun            waitingForShare = true;
1481ac5507790a87810061a19dadec36eb328a222eaTao Liejun            mWaitingForRemote = false;
1491ac5507790a87810061a19dadec36eb328a222eaTao Liejun
1501ac5507790a87810061a19dadec36eb328a222eaTao Liejun            PowerManager pm = (PowerManager)mContext1.getSystemService(Context.POWER_SERVICE);
1511ac5507790a87810061a19dadec36eb328a222eaTao Liejun            wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
1521ac5507790a87810061a19dadec36eb328a222eaTao Liejun        }
1531ac5507790a87810061a19dadec36eb328a222eaTao Liejun
15409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        public void addShare(BluetoothOppShareInfo info) {
15509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            mInfo = info;
15609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            mFileInfo = processShareInfo();
15709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            waitingForShare = false;
15809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
15909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
16009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        @Override
16109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        public void run() {
16209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
16309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
164ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly            if (V) Log.v(TAG, "acquire partial WakeLock");
16509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            wakeLock.acquire();
16652236de777c23788df8147de15912a57e8bc36ddTao Liejun
16709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
16809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Thread.sleep(100);
16909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (InterruptedException e1) {
170ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "Client thread was interrupted (1), exiting");
17109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mInterrupted = true;
17209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
17309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            if (!mInterrupted) {
17409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                connect();
17509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
1761ac5507790a87810061a19dadec36eb328a222eaTao Liejun
17709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            while (!mInterrupted) {
17809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!waitingForShare) {
17909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    doSend();
18009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } else {
18109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    try {
182ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                        if (D) Log.d(TAG, "Client thread waiting for next share, sleep for "
1831ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                    + sSleepTime);
1841ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        Thread.sleep(sSleepTime);
18509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } catch (InterruptedException e) {
18609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
18709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
18809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
18909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
19009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            disconnect();
1916769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun
1921ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (wakeLock.isHeld()) {
193ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "release partial WakeLock");
1941ac5507790a87810061a19dadec36eb328a222eaTao Liejun                wakeLock.release();
19509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
19609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            Message msg = Message.obtain(mCallback);
19709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            msg.what = BluetoothOppObexSession.MSG_SESSION_COMPLETE;
19809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            msg.obj = mInfo;
19909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            msg.sendToTarget();
2001ac5507790a87810061a19dadec36eb328a222eaTao Liejun
20109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
20209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
20309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private void disconnect() {
20409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
20509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mCs != null) {
20609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mCs.disconnect(null);
20709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
20809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mCs = null;
209ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (D) Log.d(TAG, "OBEX session disconnected");
21009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (IOException e) {
2111ac5507790a87810061a19dadec36eb328a222eaTao Liejun                Log.w(TAG, "OBEX session disconnect error" + e);
21209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
21309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
21409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mCs != null) {
215ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (D) Log.d(TAG, "OBEX session close mCs");
21609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mCs.close();
217ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (D) Log.d(TAG, "OBEX session closed");
21809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
21909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (IOException e) {
2201ac5507790a87810061a19dadec36eb328a222eaTao Liejun                Log.w(TAG, "OBEX session close error" + e);
22109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
2226769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            if (mTransport1 != null) {
22309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
2246769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    mTransport1.close();
22509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (IOException e) {
22609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.e(TAG, "mTransport.close error");
22709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
22809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
22909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
23009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
23109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
23209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private void connect() {
233ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly            if (D) Log.d(TAG, "Create ClientSession with transport " + mTransport1.toString());
2346769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            try {
2356769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                mCs = new ClientSession(mTransport1);
2361ac5507790a87810061a19dadec36eb328a222eaTao Liejun                mConnected = true;
2376769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            } catch (IOException e1) {
2386769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                Log.e(TAG, "OBEX session create error");
23909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
2401ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (mConnected) {
2411ac5507790a87810061a19dadec36eb328a222eaTao Liejun                mConnected = false;
2421ac5507790a87810061a19dadec36eb328a222eaTao Liejun                HeaderSet hs = new HeaderSet();
2431ac5507790a87810061a19dadec36eb328a222eaTao Liejun                synchronized (this) {
2441ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    mWaitingForRemote = true;
2451ac5507790a87810061a19dadec36eb328a222eaTao Liejun                }
2461ac5507790a87810061a19dadec36eb328a222eaTao Liejun                try {
2471ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    mCs.connect(hs);
248ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (D) Log.d(TAG, "OBEX session created");
2491ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    mConnected = true;
2501ac5507790a87810061a19dadec36eb328a222eaTao Liejun                } catch (IOException e) {
2511ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    Log.e(TAG, "OBEX session connect error");
25209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
25309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
25409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            synchronized (this) {
25509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mWaitingForRemote = false;
25609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
25709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
25809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
25909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private void doSend() {
26009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
26109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            int status = BluetoothShare.STATUS_SUCCESS;
26209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
26309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            /* connection is established too fast to get first mInfo */
26409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            while (mFileInfo == null) {
26509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
26609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Thread.sleep(50);
26709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (InterruptedException e) {
26809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = BluetoothShare.STATUS_CANCELED;
26909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
27009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
2711ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (!mConnected) {
2721ac5507790a87810061a19dadec36eb328a222eaTao Liejun                // Obex connection error
2731ac5507790a87810061a19dadec36eb328a222eaTao Liejun                status = BluetoothShare.STATUS_CONNECTION_ERROR;
2741ac5507790a87810061a19dadec36eb328a222eaTao Liejun            }
2751ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (status == BluetoothShare.STATUS_SUCCESS) {
27609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                /* do real send */
27709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mFileInfo.mFileName != null) {
27809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = sendFile(mFileInfo);
27909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } else {
28009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    /* this is invalid request */
28109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = mFileInfo.mStatus;
28209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
2831ac5507790a87810061a19dadec36eb328a222eaTao Liejun                waitingForShare = true;
2841ac5507790a87810061a19dadec36eb328a222eaTao Liejun            } else {
2851ac5507790a87810061a19dadec36eb328a222eaTao Liejun                Constants.updateShareStatus(mContext1, mInfo.mId, status);
28609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
28709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
28809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            if (status == BluetoothShare.STATUS_SUCCESS) {
28909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Message msg = Message.obtain(mCallback);
29009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE;
29109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.obj = mInfo;
29209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.sendToTarget();
29309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } else {
29409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Message msg = Message.obtain(mCallback);
29509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR;
2961ac5507790a87810061a19dadec36eb328a222eaTao Liejun                mInfo.mStatus = status;
29709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.obj = mInfo;
29809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.sendToTarget();
29909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
30009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
30109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
30209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        /*
30309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly         * Validate this ShareInfo
30409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly         */
30509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private BluetoothOppSendFileInfo processShareInfo() {
306ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly            if (V) Log.v(TAG, "Client thread processShareInfo() " + mInfo.mId);
30709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
308ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby            BluetoothOppSendFileInfo fileInfo = BluetoothOppUtility.getSendFileInfo(mInfo.mUri);
3091ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (fileInfo.mFileName == null || fileInfo.mLength == 0) {
310ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "BluetoothOppSendFileInfo get invalid file");
3116769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    Constants.updateShareStatus(mContext1, mInfo.mId, fileInfo.mStatus);
31209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
31309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } else {
314ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) {
31509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "Generate BluetoothOppSendFileInfo:");
31609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "filename  :" + fileInfo.mFileName);
31709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "length    :" + fileInfo.mLength);
31809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "mimetype  :" + fileInfo.mMimetype);
31909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
32009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
32109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                ContentValues updateValues = new ContentValues();
32209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
32309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
32409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                updateValues.put(BluetoothShare.FILENAME_HINT, fileInfo.mFileName);
32509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                updateValues.put(BluetoothShare.TOTAL_BYTES, fileInfo.mLength);
32609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                updateValues.put(BluetoothShare.MIMETYPE, fileInfo.mMimetype);
32709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
3286769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                mContext1.getContentResolver().update(contentUri, updateValues, null, null);
32909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
33009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
33109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            return fileInfo;
33209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
33309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
33409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private int sendFile(BluetoothOppSendFileInfo fileInfo) {
33509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            boolean error = false;
33609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            int responseCode = -1;
33709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            int status = BluetoothShare.STATUS_SUCCESS;
33809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
33909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            ContentValues updateValues;
34009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            HeaderSet request;
3416769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            request = new HeaderSet();
34209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            request.setHeader(HeaderSet.NAME, fileInfo.mFileName);
34309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            request.setHeader(HeaderSet.TYPE, fileInfo.mMimetype);
34409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
345ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby            applyRemoteDeviceQuirks(request, mInfo.mDestination, fileInfo.mFileName);
346fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly
3476769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            Constants.updateShareStatus(mContext1, mInfo.mId, BluetoothShare.STATUS_RUNNING);
34809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
34909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            request.setHeader(HeaderSet.LENGTH, fileInfo.mLength);
35009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            ClientOperation putOperation = null;
35109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            OutputStream outputStream = null;
35209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            InputStream inputStream = null;
35309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
35409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                synchronized (this) {
35509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mWaitingForRemote = true;
35609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
35709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
358ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (V) Log.v(TAG, "put headerset for " + fileInfo.mFileName);
35909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    putOperation = (ClientOperation)mCs.put(request);
36009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (IOException e) {
36109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
3626769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    Constants.updateShareStatus(mContext1, mInfo.mId, status);
36309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
36409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.e(TAG, "Error when put HeaderSet ");
36509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    error = true;
36609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
36709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                synchronized (this) {
36809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mWaitingForRemote = false;
36909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
37009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
37109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!error) {
37209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    try {
373ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                        if (V) Log.v(TAG, "openOutputStream " + fileInfo.mFileName);
37409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        outputStream = putOperation.openOutputStream();
37509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        inputStream = putOperation.openInputStream();
37609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } catch (IOException e) {
37709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
3786769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                        Constants.updateShareStatus(mContext1, mInfo.mId, status);
37909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        Log.e(TAG, "Error when openOutputStream");
38009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        error = true;
38109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
38209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
38309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!error) {
38409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    updateValues = new ContentValues();
38509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    updateValues.put(BluetoothShare.CURRENT_BYTES, 0);
38609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    updateValues.put(BluetoothShare.STATUS, BluetoothShare.STATUS_RUNNING);
3876769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    mContext1.getContentResolver().update(contentUri, updateValues, null, null);
38809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
38909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
39009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!error) {
39109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    int position = 0;
39209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    int readLength = 0;
39309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    boolean okToProceed = false;
39452236de777c23788df8147de15912a57e8bc36ddTao Liejun                    long timestamp = 0;
39509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    int outputBufferSize = putOperation.getMaxPacketSize();
39609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    byte[] buffer = new byte[outputBufferSize];
39709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    BufferedInputStream a = new BufferedInputStream(fileInfo.mInputStream, 0x4000);
39809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
3991ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    if (!mInterrupted && (position != fileInfo.mLength)) {
4000ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang                        readLength = readFully(a, buffer, outputBufferSize);
40109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4021ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        mCallback.sendMessageDelayed(mCallback
4031ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                .obtainMessage(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT),
4041ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                BluetoothOppObexSession.SESSION_TIMEOUT);
4051ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        synchronized (this) {
4061ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mWaitingForRemote = true;
40709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        }
4081ac5507790a87810061a19dadec36eb328a222eaTao Liejun
4091ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        // first packet will block here
41009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        outputStream.write(buffer, 0, readLength);
41109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
41252236de777c23788df8147de15912a57e8bc36ddTao Liejun                        position += readLength;
41309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4141ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        if (position != fileInfo.mLength) {
4151ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
4161ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            synchronized (this) {
4171ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                mWaitingForRemote = false;
41809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            }
4191ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        } else {
4201ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            // if file length is smaller than buffer size, only one packet
4211ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            // so block point is here
4221ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            outputStream.close();
4231ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
4241ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            synchronized (this) {
4251ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                mWaitingForRemote = false;
4261ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            }
4271ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        }
4281ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        /* check remote accept or reject */
4291ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        responseCode = putOperation.getResponseCode();
43009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4311ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        if (responseCode == ResponseCodes.OBEX_HTTP_CONTINUE
4321ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                || responseCode == ResponseCodes.OBEX_HTTP_OK) {
433ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) Log.v(TAG, "Remote accept");
4341ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            okToProceed = true;
4351ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            updateValues = new ContentValues();
4361ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            updateValues.put(BluetoothShare.CURRENT_BYTES, position);
4371ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mContext1.getContentResolver().update(contentUri, updateValues, null,
4381ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                    null);
43909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        } else {
4401ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            Log.i(TAG, "Remote reject, Response code is " + responseCode);
4411ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        }
4421ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    }
4431ac5507790a87810061a19dadec36eb328a222eaTao Liejun
4441ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    while (!mInterrupted && okToProceed && (position != fileInfo.mLength)) {
4451ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        {
446ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) timestamp = System.currentTimeMillis();
4471ac5507790a87810061a19dadec36eb328a222eaTao Liejun
4481ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            readLength = a.read(buffer, 0, outputBufferSize);
4491ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            outputStream.write(buffer, 0, readLength);
4501ac5507790a87810061a19dadec36eb328a222eaTao Liejun
45109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            /* check remote abort */
45209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            responseCode = putOperation.getResponseCode();
453ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) Log.v(TAG, "Response code is " + responseCode);
4546769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                            if (responseCode != ResponseCodes.OBEX_HTTP_CONTINUE
45509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                    && responseCode != ResponseCodes.OBEX_HTTP_OK) {
45609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                /* abort happens */
4571ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                okToProceed = false;
4581ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            } else {
4591ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                position += readLength;
460ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                                if (V) {
4611ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                    Log.v(TAG, "Sending file position = " + position
4621ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                            + " readLength " + readLength + " bytes took "
4631ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                            + (System.currentTimeMillis() - timestamp) + " ms");
46409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                }
4651ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                updateValues = new ContentValues();
4661ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                updateValues.put(BluetoothShare.CURRENT_BYTES, position);
4671ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                mContext1.getContentResolver().update(contentUri, updateValues,
4681ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                        null, null);
46909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            }
47009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        }
47109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
47209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4731ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    if (responseCode == ResponseCodes.OBEX_HTTP_FORBIDDEN
4741ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            || responseCode == ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
47552236de777c23788df8147de15912a57e8bc36ddTao Liejun                        Log.i(TAG, "Remote reject file " + fileInfo.mFileName + " length "
47652236de777c23788df8147de15912a57e8bc36ddTao Liejun                                + fileInfo.mLength);
47709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_FORBIDDEN;
47809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } else if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
47952236de777c23788df8147de15912a57e8bc36ddTao Liejun                        Log.i(TAG, "Remote reject file type " + fileInfo.mMimetype);
48009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
48109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } else if (!mInterrupted && position == fileInfo.mLength) {
48252236de777c23788df8147de15912a57e8bc36ddTao Liejun                        Log.i(TAG, "SendFile finished send out file " + fileInfo.mFileName
48352236de777c23788df8147de15912a57e8bc36ddTao Liejun                                + " length " + fileInfo.mLength);
48409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        outputStream.close();
48509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } else {
48609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        error = true;
48709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_CANCELED;
48809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        putOperation.abort();
48909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        /* interrupted */
4901ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        Log.i(TAG, "SendFile interrupted when send out file " + fileInfo.mFileName
4911ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                + " at " + position + " of " + fileInfo.mLength);
49209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
49309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
49409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (IOException e) {
495a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue                handleSendException(e.toString());
496a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            } catch (NullPointerException e) {
497a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue                handleSendException(e.toString());
498a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            } catch (IndexOutOfBoundsException e) {
499a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue                handleSendException(e.toString());
50009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } finally {
50109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
502ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby                    // Close InputStream and remove SendFileInfo from map
503ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby                    BluetoothOppUtility.closeSendFileInfo(mInfo.mUri);
50409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    if (!error) {
50509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        responseCode = putOperation.getResponseCode();
50609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        if (responseCode != -1) {
507ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) Log.v(TAG, "Get response code " + responseCode);
50852236de777c23788df8147de15912a57e8bc36ddTao Liejun                            if (responseCode != ResponseCodes.OBEX_HTTP_OK) {
5091ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                Log.i(TAG, "Response error code is " + responseCode);
51009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                status = BluetoothShare.STATUS_UNHANDLED_OBEX_CODE;
51109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
51209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                    status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
51309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                }
5141ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                if (responseCode == ResponseCodes.OBEX_HTTP_FORBIDDEN
5151ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                        || responseCode == ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
51609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                    status = BluetoothShare.STATUS_FORBIDDEN;
51709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                }
51809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            }
51909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        } else {
52052236de777c23788df8147de15912a57e8bc36ddTao Liejun                            // responseCode is -1, which means connection error
52109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            status = BluetoothShare.STATUS_CONNECTION_ERROR;
52209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        }
52309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
52409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
5256769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    Constants.updateShareStatus(mContext1, mInfo.mId, status);
52609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
52709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    if (inputStream != null) {
52809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        inputStream.close();
52909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
53009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    if (putOperation != null) {
53109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        putOperation.close();
53209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
53309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (IOException e) {
53409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.e(TAG, "Error when closing stream after send");
53509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
53609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
53709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            return status;
53809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
53909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
540a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue        private void handleSendException(String exception) {
541a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            Log.e(TAG, "Error when sending file: " + exception);
542a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            int status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
543a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            Constants.updateShareStatus(mContext1, mInfo.mId, status);
544a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
545a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue        }
546a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue
54709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        @Override
54809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        public void interrupt() {
54909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            super.interrupt();
55009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            synchronized (this) {
55109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mWaitingForRemote) {
552ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (V) Log.v(TAG, "Interrupted when waitingForRemote");
55309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    try {
5546769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                        mTransport1.close();
55509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } catch (IOException e) {
55609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        Log.e(TAG, "mTransport.close error");
55709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
55809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Message msg = Message.obtain(mCallback);
55909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    msg.what = BluetoothOppObexSession.MSG_SHARE_INTERRUPTED;
5601ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    if (mInfo != null) {
5611ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        msg.obj = mInfo;
5621ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    }
56309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    msg.sendToTarget();
56409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
56509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
56609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
56709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
56809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
569ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby    public static void applyRemoteDeviceQuirks(HeaderSet request, String address, String filename) {
570fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        if (address == null) {
571fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            return;
572fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        }
573fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        if (address.startsWith("00:04:48")) {
574fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            // Poloroid Pogo
575fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            // Rejects filenames with more than one '.'. Rename to '_'.
576fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            // for example: 'a.b.jpg' -> 'a_b.jpg'
577fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            //              'abc.jpg' NOT CHANGED
578fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            char[] c = filename.toCharArray();
579fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            boolean firstDot = true;
580fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            boolean modified = false;
581fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            for (int i = c.length - 1; i >= 0; i--) {
582fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                if (c[i] == '.') {
583fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                    if (!firstDot) {
584fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                        modified = true;
585fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                        c[i] = '_';
586fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                    }
587fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                    firstDot = false;
588fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                }
589fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            }
590fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly
591fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            if (modified) {
592fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                String newFilename = new String(c);
593fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                request.setHeader(HeaderSet.NAME, newFilename);
594fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                Log.i(TAG, "Sending file \"" + filename + "\" as \"" + newFilename +
595fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                        "\" to workaround Poloroid filename quirk");
596fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            }
597fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        }
598fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly    }
599fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly
60009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    public void unblock() {
6011ac5507790a87810061a19dadec36eb328a222eaTao Liejun        // Not used for client case
60209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
60309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
60409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly}
605