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
868eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen    public void start(Handler handler, int numShares) {
87ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly        if (D) Log.d(TAG, "Start!");
8809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        mCallback = handler;
898eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen        mThread = new ClientThread(mContext, mTransport, numShares);
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
1438eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen        private int mNumShares;
1448eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen
1458eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen        public ClientThread(Context context, ObexTransport transport, int initialNumShares) {
1461ac5507790a87810061a19dadec36eb328a222eaTao Liejun            super("BtOpp ClientThread");
1471ac5507790a87810061a19dadec36eb328a222eaTao Liejun            mContext1 = context;
1481ac5507790a87810061a19dadec36eb328a222eaTao Liejun            mTransport1 = transport;
1491ac5507790a87810061a19dadec36eb328a222eaTao Liejun            waitingForShare = true;
1501ac5507790a87810061a19dadec36eb328a222eaTao Liejun            mWaitingForRemote = false;
1518eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen            mNumShares = initialNumShares;
1521ac5507790a87810061a19dadec36eb328a222eaTao Liejun            PowerManager pm = (PowerManager)mContext1.getSystemService(Context.POWER_SERVICE);
1531ac5507790a87810061a19dadec36eb328a222eaTao Liejun            wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
1541ac5507790a87810061a19dadec36eb328a222eaTao Liejun        }
1551ac5507790a87810061a19dadec36eb328a222eaTao Liejun
15609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        public void addShare(BluetoothOppShareInfo info) {
15709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            mInfo = info;
15809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            mFileInfo = processShareInfo();
15909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            waitingForShare = false;
16009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
16109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
16209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        @Override
16309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        public void run() {
16409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
16509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
166ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly            if (V) Log.v(TAG, "acquire partial WakeLock");
16709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            wakeLock.acquire();
16852236de777c23788df8147de15912a57e8bc36ddTao Liejun
16909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
17009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Thread.sleep(100);
17109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (InterruptedException e1) {
172ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "Client thread was interrupted (1), exiting");
17309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mInterrupted = true;
17409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
17509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            if (!mInterrupted) {
1768eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen                connect(mNumShares);
17709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
1781ac5507790a87810061a19dadec36eb328a222eaTao Liejun
17909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            while (!mInterrupted) {
18009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!waitingForShare) {
18109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    doSend();
18209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } else {
18309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    try {
184ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                        if (D) Log.d(TAG, "Client thread waiting for next share, sleep for "
1851ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                    + sSleepTime);
1861ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        Thread.sleep(sSleepTime);
18709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } catch (InterruptedException e) {
18809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
18909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
19009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
19109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
19209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            disconnect();
1936769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun
1941ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (wakeLock.isHeld()) {
195ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "release partial WakeLock");
1961ac5507790a87810061a19dadec36eb328a222eaTao Liejun                wakeLock.release();
19709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
19809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            Message msg = Message.obtain(mCallback);
19909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            msg.what = BluetoothOppObexSession.MSG_SESSION_COMPLETE;
20009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            msg.obj = mInfo;
20109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            msg.sendToTarget();
2021ac5507790a87810061a19dadec36eb328a222eaTao Liejun
20309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
20409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
20509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private void disconnect() {
20609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
20709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mCs != null) {
20809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mCs.disconnect(null);
20909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
21009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mCs = null;
211ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (D) Log.d(TAG, "OBEX session disconnected");
21209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (IOException e) {
2131ac5507790a87810061a19dadec36eb328a222eaTao Liejun                Log.w(TAG, "OBEX session disconnect error" + e);
21409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
21509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
21609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mCs != null) {
217ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (D) Log.d(TAG, "OBEX session close mCs");
21809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mCs.close();
219ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (D) Log.d(TAG, "OBEX session closed");
22009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
22109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (IOException e) {
2221ac5507790a87810061a19dadec36eb328a222eaTao Liejun                Log.w(TAG, "OBEX session close error" + e);
22309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
2246769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            if (mTransport1 != null) {
22509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
2266769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    mTransport1.close();
22709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (IOException e) {
22809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.e(TAG, "mTransport.close error");
22909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
23009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
23109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
23209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
23309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
2348eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen        private void connect(int numShares) {
235ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly            if (D) Log.d(TAG, "Create ClientSession with transport " + mTransport1.toString());
2366769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            try {
2376769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                mCs = new ClientSession(mTransport1);
2381ac5507790a87810061a19dadec36eb328a222eaTao Liejun                mConnected = true;
2396769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            } catch (IOException e1) {
2406769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                Log.e(TAG, "OBEX session create error");
24109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
2421ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (mConnected) {
2431ac5507790a87810061a19dadec36eb328a222eaTao Liejun                mConnected = false;
2441ac5507790a87810061a19dadec36eb328a222eaTao Liejun                HeaderSet hs = new HeaderSet();
2458eb70f8bdf4e8c970810b3400aba8d08d14ce222Martijn Coenen                hs.setHeader(HeaderSet.COUNT, (long) numShares);
2461ac5507790a87810061a19dadec36eb328a222eaTao Liejun                synchronized (this) {
2471ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    mWaitingForRemote = true;
2481ac5507790a87810061a19dadec36eb328a222eaTao Liejun                }
2491ac5507790a87810061a19dadec36eb328a222eaTao Liejun                try {
2501ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    mCs.connect(hs);
251ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (D) Log.d(TAG, "OBEX session created");
2521ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    mConnected = true;
2531ac5507790a87810061a19dadec36eb328a222eaTao Liejun                } catch (IOException e) {
2541ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    Log.e(TAG, "OBEX session connect error");
25509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
25609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
25709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            synchronized (this) {
25809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                mWaitingForRemote = false;
25909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
26009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
26109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
26209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private void doSend() {
26309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
26409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            int status = BluetoothShare.STATUS_SUCCESS;
26509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
26609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            /* connection is established too fast to get first mInfo */
26709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            while (mFileInfo == null) {
26809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
26909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Thread.sleep(50);
27009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (InterruptedException e) {
27109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = BluetoothShare.STATUS_CANCELED;
27209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
27309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
2741ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (!mConnected) {
2751ac5507790a87810061a19dadec36eb328a222eaTao Liejun                // Obex connection error
2761ac5507790a87810061a19dadec36eb328a222eaTao Liejun                status = BluetoothShare.STATUS_CONNECTION_ERROR;
2771ac5507790a87810061a19dadec36eb328a222eaTao Liejun            }
2781ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (status == BluetoothShare.STATUS_SUCCESS) {
27909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                /* do real send */
28009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mFileInfo.mFileName != null) {
28109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = sendFile(mFileInfo);
28209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } else {
28309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    /* this is invalid request */
28409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = mFileInfo.mStatus;
28509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
2861ac5507790a87810061a19dadec36eb328a222eaTao Liejun                waitingForShare = true;
2871ac5507790a87810061a19dadec36eb328a222eaTao Liejun            } else {
2881ac5507790a87810061a19dadec36eb328a222eaTao Liejun                Constants.updateShareStatus(mContext1, mInfo.mId, status);
28909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
29009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
29109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            if (status == BluetoothShare.STATUS_SUCCESS) {
29209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Message msg = Message.obtain(mCallback);
29309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE;
29409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.obj = mInfo;
29509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.sendToTarget();
29609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } else {
29709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Message msg = Message.obtain(mCallback);
29809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR;
2991ac5507790a87810061a19dadec36eb328a222eaTao Liejun                mInfo.mStatus = status;
30009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.obj = mInfo;
30109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                msg.sendToTarget();
30209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
30309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
30409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
30509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        /*
30609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly         * Validate this ShareInfo
30709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly         */
30809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private BluetoothOppSendFileInfo processShareInfo() {
309ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly            if (V) Log.v(TAG, "Client thread processShareInfo() " + mInfo.mId);
31009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
311ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby            BluetoothOppSendFileInfo fileInfo = BluetoothOppUtility.getSendFileInfo(mInfo.mUri);
3121ac5507790a87810061a19dadec36eb328a222eaTao Liejun            if (fileInfo.mFileName == null || fileInfo.mLength == 0) {
313ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) Log.v(TAG, "BluetoothOppSendFileInfo get invalid file");
3146769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    Constants.updateShareStatus(mContext1, mInfo.mId, fileInfo.mStatus);
31509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
31609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } else {
317ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                if (V) {
31809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "Generate BluetoothOppSendFileInfo:");
31909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "filename  :" + fileInfo.mFileName);
32009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "length    :" + fileInfo.mLength);
32109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.v(TAG, "mimetype  :" + fileInfo.mMimetype);
32209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
32309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
32409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                ContentValues updateValues = new ContentValues();
32509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
32609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
32709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                updateValues.put(BluetoothShare.FILENAME_HINT, fileInfo.mFileName);
32809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                updateValues.put(BluetoothShare.TOTAL_BYTES, fileInfo.mLength);
32909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                updateValues.put(BluetoothShare.MIMETYPE, fileInfo.mMimetype);
33009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
3316769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                mContext1.getContentResolver().update(contentUri, updateValues, null, null);
33209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
33309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
33409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            return fileInfo;
33509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
33609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
33709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        private int sendFile(BluetoothOppSendFileInfo fileInfo) {
33809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            boolean error = false;
33909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            int responseCode = -1;
34009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            int status = BluetoothShare.STATUS_SUCCESS;
34109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
34209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            ContentValues updateValues;
34309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            HeaderSet request;
3446769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            request = new HeaderSet();
34509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            request.setHeader(HeaderSet.NAME, fileInfo.mFileName);
34609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            request.setHeader(HeaderSet.TYPE, fileInfo.mMimetype);
34709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
348ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby            applyRemoteDeviceQuirks(request, mInfo.mDestination, fileInfo.mFileName);
349fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly
3506769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun            Constants.updateShareStatus(mContext1, mInfo.mId, BluetoothShare.STATUS_RUNNING);
35109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
35209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            request.setHeader(HeaderSet.LENGTH, fileInfo.mLength);
35309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            ClientOperation putOperation = null;
35409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            OutputStream outputStream = null;
35509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            InputStream inputStream = null;
35609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            try {
35709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                synchronized (this) {
35809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mWaitingForRemote = true;
35909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
36009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
361ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (V) Log.v(TAG, "put headerset for " + fileInfo.mFileName);
36209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    putOperation = (ClientOperation)mCs.put(request);
36309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (IOException e) {
36409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
3656769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    Constants.updateShareStatus(mContext1, mInfo.mId, status);
36609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
36709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.e(TAG, "Error when put HeaderSet ");
36809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    error = true;
36909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
37009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                synchronized (this) {
37109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    mWaitingForRemote = false;
37209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
37309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
37409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!error) {
37509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    try {
376ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                        if (V) Log.v(TAG, "openOutputStream " + fileInfo.mFileName);
37709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        outputStream = putOperation.openOutputStream();
37809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        inputStream = putOperation.openInputStream();
37909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } catch (IOException e) {
38009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
3816769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                        Constants.updateShareStatus(mContext1, mInfo.mId, status);
38209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        Log.e(TAG, "Error when openOutputStream");
38309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        error = true;
38409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
38509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
38609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!error) {
38709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    updateValues = new ContentValues();
38809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    updateValues.put(BluetoothShare.CURRENT_BYTES, 0);
38909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    updateValues.put(BluetoothShare.STATUS, BluetoothShare.STATUS_RUNNING);
3906769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    mContext1.getContentResolver().update(contentUri, updateValues, null, null);
39109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
39209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
39309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (!error) {
39409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    int position = 0;
39509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    int readLength = 0;
39609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    boolean okToProceed = false;
39752236de777c23788df8147de15912a57e8bc36ddTao Liejun                    long timestamp = 0;
39809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    int outputBufferSize = putOperation.getMaxPacketSize();
39909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    byte[] buffer = new byte[outputBufferSize];
40009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    BufferedInputStream a = new BufferedInputStream(fileInfo.mInputStream, 0x4000);
40109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4021ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    if (!mInterrupted && (position != fileInfo.mLength)) {
4030ac98162ff293fdaa23f93f9839aaad5428af537Chih-Chung Chang                        readLength = readFully(a, buffer, outputBufferSize);
40409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4051ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        mCallback.sendMessageDelayed(mCallback
4061ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                .obtainMessage(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT),
4071ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                BluetoothOppObexSession.SESSION_TIMEOUT);
4081ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        synchronized (this) {
4091ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mWaitingForRemote = true;
41009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        }
4111ac5507790a87810061a19dadec36eb328a222eaTao Liejun
4121ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        // first packet will block here
41309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        outputStream.write(buffer, 0, readLength);
41409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
41552236de777c23788df8147de15912a57e8bc36ddTao Liejun                        position += readLength;
41609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4171ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        if (position != fileInfo.mLength) {
4181ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
4191ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            synchronized (this) {
4201ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                mWaitingForRemote = false;
42109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            }
4221ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        } else {
4231ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            // if file length is smaller than buffer size, only one packet
4241ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            // so block point is here
4251ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            outputStream.close();
4261ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
4271ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            synchronized (this) {
4281ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                mWaitingForRemote = false;
4291ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            }
4301ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        }
4311ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        /* check remote accept or reject */
4321ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        responseCode = putOperation.getResponseCode();
43309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4341ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        if (responseCode == ResponseCodes.OBEX_HTTP_CONTINUE
4351ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                || responseCode == ResponseCodes.OBEX_HTTP_OK) {
436ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) Log.v(TAG, "Remote accept");
4371ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            okToProceed = true;
4381ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            updateValues = new ContentValues();
4391ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            updateValues.put(BluetoothShare.CURRENT_BYTES, position);
4401ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            mContext1.getContentResolver().update(contentUri, updateValues, null,
4411ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                    null);
44209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        } else {
4431ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            Log.i(TAG, "Remote reject, Response code is " + responseCode);
4441ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        }
4451ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    }
4461ac5507790a87810061a19dadec36eb328a222eaTao Liejun
4471ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    while (!mInterrupted && okToProceed && (position != fileInfo.mLength)) {
4481ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        {
449ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) timestamp = System.currentTimeMillis();
4501ac5507790a87810061a19dadec36eb328a222eaTao Liejun
4511ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            readLength = a.read(buffer, 0, outputBufferSize);
4521ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            outputStream.write(buffer, 0, readLength);
4531ac5507790a87810061a19dadec36eb328a222eaTao Liejun
45409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            /* check remote abort */
45509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            responseCode = putOperation.getResponseCode();
456ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) Log.v(TAG, "Response code is " + responseCode);
4576769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                            if (responseCode != ResponseCodes.OBEX_HTTP_CONTINUE
45809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                    && responseCode != ResponseCodes.OBEX_HTTP_OK) {
45909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                /* abort happens */
4601ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                okToProceed = false;
4611ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            } else {
4621ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                position += readLength;
463ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                                if (V) {
4641ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                    Log.v(TAG, "Sending file position = " + position
4651ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                            + " readLength " + readLength + " bytes took "
4661ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                            + (System.currentTimeMillis() - timestamp) + " ms");
46709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                }
4681ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                updateValues = new ContentValues();
4691ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                updateValues.put(BluetoothShare.CURRENT_BYTES, position);
4701ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                mContext1.getContentResolver().update(contentUri, updateValues,
4711ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                        null, null);
47209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            }
47309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        }
47409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
47509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
4761ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    if (responseCode == ResponseCodes.OBEX_HTTP_FORBIDDEN
4771ac5507790a87810061a19dadec36eb328a222eaTao Liejun                            || responseCode == ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
47852236de777c23788df8147de15912a57e8bc36ddTao Liejun                        Log.i(TAG, "Remote reject file " + fileInfo.mFileName + " length "
47952236de777c23788df8147de15912a57e8bc36ddTao Liejun                                + fileInfo.mLength);
48009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_FORBIDDEN;
48109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } else if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
48252236de777c23788df8147de15912a57e8bc36ddTao Liejun                        Log.i(TAG, "Remote reject file type " + fileInfo.mMimetype);
48309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
48409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } else if (!mInterrupted && position == fileInfo.mLength) {
48552236de777c23788df8147de15912a57e8bc36ddTao Liejun                        Log.i(TAG, "SendFile finished send out file " + fileInfo.mFileName
48652236de777c23788df8147de15912a57e8bc36ddTao Liejun                                + " length " + fileInfo.mLength);
48709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        outputStream.close();
48809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } else {
48909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        error = true;
49009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        status = BluetoothShare.STATUS_CANCELED;
49109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        putOperation.abort();
49209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        /* interrupted */
4931ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        Log.i(TAG, "SendFile interrupted when send out file " + fileInfo.mFileName
4941ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                + " at " + position + " of " + fileInfo.mLength);
49509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
49609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
49709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } catch (IOException e) {
498a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue                handleSendException(e.toString());
499a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            } catch (NullPointerException e) {
500a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue                handleSendException(e.toString());
501a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            } catch (IndexOutOfBoundsException e) {
502a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue                handleSendException(e.toString());
50309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            } finally {
50409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                try {
505ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby                    // Close InputStream and remove SendFileInfo from map
506ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby                    BluetoothOppUtility.closeSendFileInfo(mInfo.mUri);
50709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    if (!error) {
50809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        responseCode = putOperation.getResponseCode();
50909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        if (responseCode != -1) {
510ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                            if (V) Log.v(TAG, "Get response code " + responseCode);
51152236de777c23788df8147de15912a57e8bc36ddTao Liejun                            if (responseCode != ResponseCodes.OBEX_HTTP_OK) {
5121ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                Log.i(TAG, "Response error code is " + responseCode);
51309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                status = BluetoothShare.STATUS_UNHANDLED_OBEX_CODE;
51409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
51509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                    status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
51609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                }
5171ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                if (responseCode == ResponseCodes.OBEX_HTTP_FORBIDDEN
5181ac5507790a87810061a19dadec36eb328a222eaTao Liejun                                        || responseCode == ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
51909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                    status = BluetoothShare.STATUS_FORBIDDEN;
52009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                                }
52109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            }
52209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        } else {
52352236de777c23788df8147de15912a57e8bc36ddTao Liejun                            // responseCode is -1, which means connection error
52409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                            status = BluetoothShare.STATUS_CONNECTION_ERROR;
52509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        }
52609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
52709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
5286769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                    Constants.updateShareStatus(mContext1, mInfo.mId, status);
52909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
53009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    if (inputStream != null) {
53109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        inputStream.close();
53209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
53309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    if (putOperation != null) {
53409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        putOperation.close();
53509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
53609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                } catch (IOException e) {
53709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Log.e(TAG, "Error when closing stream after send");
53809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
53909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
54009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            return status;
54109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
54209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
543a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue        private void handleSendException(String exception) {
544a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            Log.e(TAG, "Error when sending file: " + exception);
545a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            int status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
546a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            Constants.updateShareStatus(mContext1, mInfo.mId, status);
547a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
548a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue        }
549a4508589f298c67fda54c344760ae39f0f375c11Lixin Yue
55009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        @Override
55109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        public void interrupt() {
55209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            super.interrupt();
55309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            synchronized (this) {
55409e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                if (mWaitingForRemote) {
555ce4d93666275df294cb073fe41de5b85932570a8Nick Pelly                    if (V) Log.v(TAG, "Interrupted when waitingForRemote");
55609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    try {
5576769b59d715ea98bd72eafcfea9acd2714a887daTao Liejun                        mTransport1.close();
55809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    } catch (IOException e) {
55909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                        Log.e(TAG, "mTransport.close error");
56009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    }
56109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    Message msg = Message.obtain(mCallback);
56209e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    msg.what = BluetoothOppObexSession.MSG_SHARE_INTERRUPTED;
5631ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    if (mInfo != null) {
5641ac5507790a87810061a19dadec36eb328a222eaTao Liejun                        msg.obj = mInfo;
5651ac5507790a87810061a19dadec36eb328a222eaTao Liejun                    }
56609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                    msg.sendToTarget();
56709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly                }
56809e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly            }
56909e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly        }
57009e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
57109e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
572ee52ddf33a0ce2cf89cc028136f60ae600c45de5Jake Hamby    public static void applyRemoteDeviceQuirks(HeaderSet request, String address, String filename) {
573fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        if (address == null) {
574fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            return;
575fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        }
576fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        if (address.startsWith("00:04:48")) {
577fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            // Poloroid Pogo
578fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            // Rejects filenames with more than one '.'. Rename to '_'.
579fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            // for example: 'a.b.jpg' -> 'a_b.jpg'
580fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            //              'abc.jpg' NOT CHANGED
581fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            char[] c = filename.toCharArray();
582fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            boolean firstDot = true;
583fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            boolean modified = false;
584fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            for (int i = c.length - 1; i >= 0; i--) {
585fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                if (c[i] == '.') {
586fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                    if (!firstDot) {
587fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                        modified = true;
588fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                        c[i] = '_';
589fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                    }
590fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                    firstDot = false;
591fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                }
592fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            }
593fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly
594fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            if (modified) {
595fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                String newFilename = new String(c);
596fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                request.setHeader(HeaderSet.NAME, newFilename);
597fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                Log.i(TAG, "Sending file \"" + filename + "\" as \"" + newFilename +
598fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly                        "\" to workaround Poloroid filename quirk");
599fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly            }
600fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly        }
601fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly    }
602fa5d402906010cd17c8ed7de0dbcbfdcb78dab20Nick Pelly
60309e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    public void unblock() {
6041ac5507790a87810061a19dadec36eb328a222eaTao Liejun        // Not used for client case
60509e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly    }
60609e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly
60709e9cba205af60b3f42e7a4d891a7d1392e1f2a5Nick Pelly}
608