Как отправить файл с помощью Bluetooth на Android программно? - PullRequest
5 голосов
/ 07 февраля 2011

Мне нужно отправить файл на компьютер вместо другого приложения для Android.Я посмотрел на API Bluetooth, но он разрешает только подключение в качестве клиента-сервера.В моем случае я не знаю, какой UUId будет на компьютере.Нужно ли смотреть на obex.Я не использовал это раньше.Так что любая помощь будет благотворной.

Ответы [ 4 ]

10 голосов
/ 30 июня 2011

Попробуй это. Я могу отправить файл, используя этот код.

ContentValues values = new ContentValues();
values.put(BluetoothShare.URI, "file:///sdcard/refresh.txt");
values.put(BluetoothShare.DESTINATION, deviceAddress);
values.put(BluetoothShare.DIRECTION, BluetoothShare.DIRECTION_OUTBOUND);
Long ts = System.currentTimeMillis();
values.put(BluetoothShare.TIMESTAMP, ts);
getContentResolver().insert(BluetoothShare.CONTENT_URI, values);

Код BluetoothShare.java

import android.provider.BaseColumns;
import android.net.Uri;

/**
 * Exposes constants used to interact with the Bluetooth Share manager's content
 * provider.
 */

public final class BluetoothShare implements BaseColumns {
private BluetoothShare() {
}
/**
 * The permission to access the Bluetooth Share Manager
 */
public static final String PERMISSION_ACCESS = "android.permission.ACCESS_BLUETOOTH_SHARE";

/**
 * The content:// URI for the data table in the provider
 */
public static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.opp/btopp");

/**
 * Broadcast Action: this is sent by the Bluetooth Share component to
 * transfer complete. The request detail could be retrieved by app * as _ID
 * is specified in the intent's data.
 */
public static final String TRANSFER_COMPLETED_ACTION = "android.btopp.intent.action.TRANSFER_COMPLETE";

/**
 * This is sent by the Bluetooth Share component to indicate there is an
 * incoming file need user to confirm.
 */
public static final String INCOMING_FILE_CONFIRMATION_REQUEST_ACTION = "android.btopp.intent.action.INCOMING_FILE_NOTIFICATION";

/**
 * This is sent by the Bluetooth Share component to indicate there is an
 * incoming file request timeout and need update UI.
 */
public static final String USER_CONFIRMATION_TIMEOUT_ACTION = "android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT";

/**
 * The name of the column containing the URI of the file being
 * sent/received.
 */
public static final String URI = "uri";

/**
 * The name of the column containing the filename that the incoming file
 * request recommends. When possible, the Bluetooth Share manager will
 * attempt to use this filename, or a variation, as the actual name for the
 * file.
 */
public static final String FILENAME_HINT = "hint";

/**
 * The name of the column containing the filename where the shared file was
 * actually stored.
 */
public static final String _DATA = "_data";

/**
 * The name of the column containing the MIME type of the shared file.
 */
public static final String MIMETYPE = "mimetype";

/**
 * The name of the column containing the direction (Inbound/Outbound) of the
 * transfer. See the DIRECTION_* constants for a list of legal values.
 */
public static final String DIRECTION = "direction";

/**
 * The name of the column containing Bluetooth Device Address that the
 * transfer is associated with.
 */
public static final String DESTINATION = "destination";

/**
 * The name of the column containing the flags that controls whether the
 * transfer is displayed by the UI. See the VISIBILITY_* constants for a
 * list of legal values.
 */
public static final String VISIBILITY = "visibility";

/**
 * The name of the column containing the current user confirmation state of
 * the transfer. Applications can write to this to confirm the transfer. the
 * USER_CONFIRMATION_* constants for a list of legal values.
 */
public static final String USER_CONFIRMATION = "confirm";

/**
 * The name of the column containing the current status of the transfer.
 * Applications can read this to follow the progress of each download. See
 * the STATUS_* constants for a list of legal values.
 */
public static final String STATUS = "status";

/**
 * The name of the column containing the total size of the file being
 * transferred.
 */
public static final String TOTAL_BYTES = "total_bytes";

/**
 * The name of the column containing the size of the part of the file that
 * has been transferred so far.
 */
public static final String CURRENT_BYTES = "current_bytes";

/**
 * The name of the column containing the timestamp when the transfer is
 * initialized.
 */
public static final String TIMESTAMP = "timestamp";

/**
 * This transfer is outbound, e.g. share file to other device.
 */
public static final int DIRECTION_OUTBOUND = 0;

/**
 * This transfer is inbound, e.g. receive file from other device.
 */
public static final int DIRECTION_INBOUND = 1;

/**
 * This transfer is waiting for user confirmation.
 */
public static final int USER_CONFIRMATION_PENDING = 0;

/**
 * This transfer is confirmed by user.
 */
public static final int USER_CONFIRMATION_CONFIRMED = 1;

/**
 * This transfer is auto-confirmed per previous user confirmation.
 */
public static final int USER_CONFIRMATION_AUTO_CONFIRMED = 2;

/**
 * This transfer is denied by user.
 */
public static final int USER_CONFIRMATION_DENIED = 3;

/**
 * This transfer is timeout before user action.
 */
public static final int USER_CONFIRMATION_TIMEOUT = 4;

/**
 * This transfer is visible and shows in the notifications while in progress
 * and after completion.
 */
public static final int VISIBILITY_VISIBLE = 0;

/**
 * This transfer doesn't show in the notifications.
 */
public static final int VISIBILITY_HIDDEN = 1;

/**
 * Returns whether the status is informational (i.e. 1xx).
 */
public static boolean isStatusInformational(int status) {
    return (status >= 100 && status < 200);
}

/**
 * Returns whether the transfer is suspended. (i.e. whether the transfer
 * won't complete without some action from outside the transfer manager).
 */
public static boolean isStatusSuspended(int status) {
    return (status == STATUS_PENDING);
}

/**
 * Returns whether the status is a success (i.e. 2xx).
 */
public static boolean isStatusSuccess(int status) {
    return (status >= 200 && status < 300);
}

/**
 * Returns whether the status is an error (i.e. 4xx or 5xx).
 */
public static boolean isStatusError(int status) {
    return (status >= 400 && status < 600);
}

/**
 * Returns whether the status is a client error (i.e. 4xx).
 */
public static boolean isStatusClientError(int status) {
    return (status >= 400 && status < 500);
}

/**
 * Returns whether the status is a server error (i.e. 5xx).
 */
public static boolean isStatusServerError(int status) {
    return (status >= 500 && status < 600);
}

/**
 * Returns whether the transfer has completed (either with success or
 * error).
 */
public static boolean isStatusCompleted(int status) {
    return (status >= 200 && status < 300) || (status >= 400 && status < 600);
}

/**
 * This transfer hasn't stated yet
 */
public static final int STATUS_PENDING = 190;

/**
 * This transfer has started
 */
public static final int STATUS_RUNNING = 192;

/**
 * This transfer has successfully completed. Warning: there might be other
 * status values that indicate success in the future. Use isSucccess() to
 * capture the entire category.
 */
public static final int STATUS_SUCCESS = 200;

/**
 * This request couldn't be parsed. This is also used when processing
 * requests with unknown/unsupported URI schemes.
 */
public static final int STATUS_BAD_REQUEST = 400;

/**
 * This transfer is forbidden by target device.
 */
public static final int STATUS_FORBIDDEN = 403;

/**
 * This transfer can't be performed because the content cannot be handled.
 */
public static final int STATUS_NOT_ACCEPTABLE = 406;

/**
 * This transfer cannot be performed because the length cannot be determined
 * accurately. This is the code for the HTTP error "Length Required", which
 * is typically used when making requests that require a content length but
 * don't have one, and it is also used in the client when a response is
 * received whose length cannot be determined accurately (therefore making
 * it impossible to know when a transfer completes).
 */
public static final int STATUS_LENGTH_REQUIRED = 411;

/**
 * This transfer was interrupted and cannot be resumed. This is the code for
 * the OBEX error "Precondition Failed", and it is also used in situations
 * where the client doesn't have an ETag at all.
 */
public static final int STATUS_PRECONDITION_FAILED = 412;

/**
 * This transfer was canceled
 */
public static final int STATUS_CANCELED = 490;

/**
 * This transfer has completed with an error. Warning: there will be other
 * status values that indicate errors in the future. Use isStatusError() to
 * capture the entire category.
 */
public static final int STATUS_UNKNOWN_ERROR = 491;

/**
 * This transfer couldn't be completed because of a storage issue.
 * Typically, that's because the file system is missing or full.
 */
public static final int STATUS_FILE_ERROR = 492;

/**
 * This transfer couldn't be completed because of no sdcard.
 */
public static final int STATUS_ERROR_NO_SDCARD = 493;

/**
 * This transfer couldn't be completed because of sdcard full.
 */
public static final int STATUS_ERROR_SDCARD_FULL = 494;

/**
 * This transfer couldn't be completed because of an unspecified un-handled
 * OBEX code.
 */
public static final int STATUS_UNHANDLED_OBEX_CODE = 495;

/**
 * This transfer couldn't be completed because of an error receiving or
 * processing data at the OBEX level.
 */
public static final int STATUS_OBEX_DATA_ERROR = 496;

/**
 * This transfer couldn't be completed because of an error when establishing
 * connection.
 */
public static final int STATUS_CONNECTION_ERROR = 497;

}
2 голосов
/ 04 марта 2013

Для Ice Cream Sandwich этот код не работает, поэтому вы должны использовать этот код

            int currentapiVersion = android.os.Build.VERSION.SDK_INT;
            if (currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                Intent sharingIntent = new Intent(
                        android.content.Intent.ACTION_SEND);
                sharingIntent.setType("image/jpeg");
                sharingIntent
                        .setComponent(new ComponentName(
                                "com.android.bluetooth",
                                "com.android.bluetooth.opp.BluetoothOppLauncherActivity"));
                sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
                startActivity(sharingIntent);
            } else {
                ContentValues values = new ContentValues();
                values.put(BluetoothShare.URI, uri.toString());
                Toast.makeText(getBaseContext(), "URi : " + uri,
                        Toast.LENGTH_LONG).show();
                values.put(BluetoothShare.DESTINATION, deviceAddress);
                values.put(BluetoothShare.DIRECTION,
                        BluetoothShare.DIRECTION_OUTBOUND);
                Long ts = System.currentTimeMillis();
                values.put(BluetoothShare.TIMESTAMP, ts);
                getContentResolver().insert(BluetoothShare.CONTENT_URI,
                        values);
            }
1 голос
/ 01 марта 2018

Вы можете использовать библиотеку obex.Казалось, что Android не предоставляет библиотеку obex, но я решил проблему, и решение выложено здесь .

Дальнейшее объяснение (пожалуйста, начните читать с здесь если вы заняты)

  1. Я пытался создать удаленный контроллер телефона Android (и что-то похожее на сервер telnet), который поможет удаленно управлять телефоном с помощью моего старого телефона.

Основное содержание: Bluetooth FTP-клиент

Мой первый план состоял в том, чтобы приложение проверяло список файлов в каталоге моего телефонного аппарата.

Но я не знал, как подключиться к телефонам моего функционального телефона.FTP-сервер.

Я много гуглил о том, как подключиться к FTP-серверу через Bluetooth, но я мог только обнаружить, что FTP-сервер Bluetoorh использовал OBEX Protocol.

Я нашел полезный материал (файл PDF) в потоке SO и изучил запросы OBEX connect, операции put и get.

Итак, я наконец написал несколько кодов, которыепытается подключиться к серверу Bluetooth FTP.Я хочу показать их вам, но я потерял это :( Коды походили на прямую запись байтовых последовательностей в выходной поток.

Мне также было трудно узнать, что делает UUIDприложение подключается как FTP-клиент. Но я попробовал все UUID, извлеченные с использованием приведенного ниже кода.

String parcels="";
ParcelUuid[] uuids=mBtDevice.getUuids();
int i=0;
for (ParcelUuid p:uuids)
{
     parcels += "UUID UUID" + new Integer(i).toString() + "=UUID.fromString((\"" + p.getUuid().toString() + "\"));\n\n";
     ++i;
}

Казалось, ничто не может привести к ответу, который я хотел. Поэтому я поискал больше и нашеля должен не только использовать UUID 00001106-0000-1000-8000-00805f9b34fb для подключения к FTP-серверу OBEX, но также должен передать целевой заголовок ** с UUID ** F9EC7BC4-953C-11D2-984E-525400DC9E09 при отправке запроса OBEX connect. Приведенный ниже код показывает, как подключиться к FTP-серверу Bluetooth в качестве клиента.

try
{
    mBtSocket = mBtDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(" 00001106-0000-1000-8000-00805f9b34fb"));
}
catch (Exception e)
{
    //e.printStackTrace();
}

Thread thread=new Thread(new Runnable() {
        public void run()
        {
            UUID uuid=UUID.fromString("F9EC7BC4-953C-11D2-984E-525400DC9E09");
            ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
            bb.putLong(uuid.getMostSignificantBits());
            bb.putLong(uuid.getLeastSignificantBits());
            byte [] bytes=bb.array();
            Operation putOperation=null;
            Operation getOperation=null;
            try
            {
                // connect the socket
                mBtSocket.connect();
    //I will explain below
                mSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));

                HeaderSet headerset = new HeaderSet();
                headerset.setHeader(HeaderSet.TARGET, bytes);

                headerset = mSession.connect(headerset);

                if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK)
                {
                    mConnected = true;
                }
                else
                {
                    mSession.disconnect(headerset);
                }
    ...

Тогда вытеперь подключен как FTP-клиент и готов использовать операции OBEX для отправки файлов, запроса файлов, списков каталогов и т. д.

  1. Однако я не хотел ждать час, чтобы отправить свою команду моемутелефон Android. (И это горебудет неэффективным, если я увеличу частоту опроса, так как все методы опроса.)

Начните читать отсюда, если вы заняты Основное содержание: OBEX OPP

По вышеупомянутой причине я жадно искал способы манипулирования OPP, которые я обнаружил в документации OBEX.

Возможно, вы захотите нормально передавать файлы через Bluetooth (без определения вашего протоколаи создание нового настольного приложения только для него) на ваш компьютер, верно?Тогда отправка на почтовый ящик OBEX OPP, который работает на вашем компьютере с Windows, является лучшим решением.Итак, как мы можем подключиться к службе входящих сообщений OPP (Obex Object Push)?

  1. Настройка библиотеки OBEX Добавьте import javax.obex; в свой исходный код.Если ваш компилятор не поддерживает библиотеку OBEX, загрузите исходники и добавьте в свой проект из здесь .
  2. Реализация ObexTransport Вы должны предоставить класс, который реализует ObexTransport для библиотеки, когдавы используете его.Он определяет, как библиотека должна отправлять данные (например, RFCOMM, TCP, ...).Пример реализации здесь .Это может вызвать некоторые ошибки во время выполнения или ошибки компиляции, такие как there's no method.Но вы можете частично исправить их, заменив вызовы методов константами, такими как return 4096 вместо return mSocket.getMaxTransmitPacketSize();, превзойдя if операторы public int getMaxTransmitPacketSize().Или вы можете попробовать использовать отражение , чтобы получить эти методы выполнения.
  3. Получить BluetoothSocket Получить разъем Bluetooth с помощью mBtDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(" 00001105-0000-1000-8000-00805f9b34fb" )); И вызвать connect().
  4. Создать ClientSession Создайте экземпляр вашей реализации ObexTransport и создайте новый ClientSession, такой как mSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));.
  5. Отправьте запрос OBEX-соединения на службу входящих сообщений вашего компьютера.

  6. Отправлять OBEX-запросы на размещение, используя ClientSession.

    protected boolean Put(ClientSession session, byte[] bytes, String as, String type)
    {
        // TODO: Implement this method
        //byte [] bytes;
        String filename=as;
        boolean retry=true;
        int times=0;
        while (retry && times < 4)
        {        
            Operation putOperation=null;
            OutputStream mOutput = null;
            //ClientSession mSession = null;
            //ArrayUtils.reverse(bytes);
            try
            {    
                // Send a file with meta data to the server
                final HeaderSet hs = new HeaderSet();
                hs.setHeader(HeaderSet.NAME, filename);
                hs.setHeader(HeaderSet.TYPE, type);
                hs.setHeader(HeaderSet.LENGTH, new Long((long)bytes.length));
                Log.v(TAG,filename);
                //Log.v(TAG,type);
                Log.v(TAG,bytes.toString());
                putOperation = session.put(hs);
    
                mOutput = putOperation.openOutputStream();
                mOutput.write(bytes);
                mOutput.close();
                putOperation.close();
            }
            catch (Exception e)
            {
                Log.e(TAG, "put failed", e);
                retry = true;
                times++;
                continue;
                //e.printStackTrace();
            }
            finally
            {
                try
                {
    
                    if(mOutput!=null)
                        mOutput.close();
                    if(putOperation!=null)
                        putOperation.close();
                }
                catch (Exception e)
                {
                    Log.e(TAG, "put finally" , e);
                    retry = true;
                    times++;
                    continue;
                }
                //updateStatus("[CLIENT] Connection Closed");
            }
            retry = false;
            return true;
        }
        return false;
    }
    
  7. Наконец, отключите.

    Тогда вот класс-обёртка.

    import android.bluetooth.*;
    import android.util.*;
    import java.io.*;
    import java.util.*;
    import javax.obex.*;
    
    public class BluetoothOPPHelper
    {
        String address;
        BluetoothAdapter mBtadapter;
        BluetoothDevice device;
        ClientSession session;
        BluetoothSocket mBtSocket;
        protected final UUID OPPUUID=UUID.fromString(("00001105-0000-1000-8000-00805f9b34fb"));
    
        private String TAG="BluetoothOPPHelper";
    
    public BluetoothOPPHelper(String address)
    {
        mBtadapter=BluetoothAdapter.getDefaultAdapter();
        device=mBtadapter.getRemoteDevice(address);
        try
        {
            mBtSocket = device.createRfcommSocketToServiceRecord(OPPUUID);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
    
        }
    }
    public ClientSession StartBatch(int n)
    {
        ClientSession mSession = null;
    
        // TODO: Implement this method
        boolean retry=true;
        int times=0;
        while (retry && times < 4)
        {
            //BluetoothConnector.BluetoothSocketWrapper bttmp=null;
            try
            {
                mBtSocket.connect();
                //bttmp =  (new BluetoothConnector(device,false,BluetoothAdapter.getDefaultAdapter(),Arrays.asList(new UUID[]{OPPUUID,OPPUUID, OPPUUID}))).connect();//*/ device.createInsecureRfcommSocketToServiceRecord(OPPUUID);
                /*if(mBtSocket.isConnected())
                 {
                 mBtSocket.close();
                 }*/
            }
            catch (Exception e)
            {
                Log.e(TAG, "opp fail sock " + e.getMessage());
                retry = true;
                times++;
                continue;
            }        
    
            try
            {
                //mBtSocket=bttmp.getUnderlyingSocket();
                //    mBtSocket.connect();
                BluetoothObexTransport mTransport = null;
                mSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));
    
                HeaderSet headerset = new HeaderSet();
                //    headerset.setHeader(HeaderSet.COUNT,n);
    
                headerset = mSession.connect(null);
    
                if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK)
                {
                    boolean mConnected = true;
                }
                else
                {
                    Log.e(TAG, "SEnd by OPP denied;");
                    mSession.disconnect(headerset);
                    times++;
                    continue;
                }
    
            }
            catch (Exception e)
            {
                Log.e(TAG, "opp failed;" , e);
                retry = true;
                times++;
                continue;
                //e.rintStackTrace();
            }
            retry=false;
        }
        return mSession;
    
    }
    
    protected boolean Put(ClientSession session, byte[] bytes, String as, String type)
    {
        // TODO: Implement this method
        //byte [] bytes;
        String filename=as;
        boolean retry=true;
        int times=0;
        while (retry && times < 4)
        {        
            Operation putOperation=null;
            OutputStream mOutput = null;
            //ClientSession mSession = null;
            //ArrayUtils.reverse(bytes);
            try
            {    
                // Send a file with meta data to the server
                final HeaderSet hs = new HeaderSet();
                hs.setHeader(HeaderSet.NAME, filename);
                hs.setHeader(HeaderSet.TYPE, type);
                hs.setHeader(HeaderSet.LENGTH, new Long((long)bytes.length));
                Log.v(TAG,filename);
                //Log.v(TAG,type);
                Log.v(TAG,bytes.toString());
                putOperation = session.put(hs);
    
                mOutput = putOperation.openOutputStream();
                mOutput.write(bytes);
                mOutput.close();
                putOperation.close();
            }
            catch (Exception e)
            {
                Log.e(TAG, "put failed", e);
                retry = true;
                times++;
                continue;
                //e.printStackTrace();
            }
            finally
            {
                try
                {
    
                    if(mOutput!=null)
                        mOutput.close();
                    if(putOperation!=null)
                        putOperation.close();
                }
                catch (Exception e)
                {
                    Log.e(TAG, "put finally" , e);
                    retry = true;
                    times++;
                    continue;
                }
                //updateStatus("[CLIENT] Connection Closed");
            }
            retry = false;
            return true;
        }
        return false;
    }
    
    protected boolean Put(ClientSession s, OPPBatchInfo info)
    {
        return Put(s,info.data,info.as,info.type);
    }
    
    private void FinishBatch(ClientSession mSession) throws IOException
    {
        mSession.disconnect(null);
        try
        {
            Thread.sleep((long)500);
        }
        catch (InterruptedException e)
        {}
        mBtSocket.close();
    }
    
    public boolean flush() throws IOException
    {
        if (sendQueue.isEmpty())
        {
            return true;
        }
        try
        {
            Thread.sleep((long)2000);
        }
        catch (InterruptedException e)
        {}
        ClientSession session=StartBatch(sendQueue.size());
        if (session == null)
        {
            return false;
        }
        while (!sendQueue.isEmpty())
        {
            if (Put(session, sendQueue.remove()) == false)
            {
                Log.e(TAG, "Put failed");
            }
        }
        FinishBatch(session);
        return true;
    }
    Queue<OPPBatchInfo> sendQueue;
    public boolean AddTransfer(String as,String mimetype,byte[] data)
    {
        return sendQueue.add(new OPPBatchInfo(as,mimetype,data));
    }
    class OPPBatchInfo
    {
        String as;
        String type;
        byte[] data;
        public OPPBatchInfo(String as,String type,byte[] data)
            {
                this.as=as;
                this.data=data;
                this.type=type;
            }
        }
    }
    
0 голосов
/ 06 марта 2018

Вам необходимо реализовать FTP через OBEX.Как только вы внедрите стандартный протокол и профиль, ваша реализация Android FTP будет взаимодействовать практически с любым Bluetooth FTP-сервером.Вам также нужно будет реализовать OPP для максимальной функциональной совместимости.Протокол OBEX не так сложен в реализации и спецификации находятся в свободном доступе.

...