如何在Android上以编程方式使用蓝牙发送文件?

发布于 2024-10-16 08:34:46 字数 121 浏览 3 评论 0原文

我需要将文件发送到计算机而不是另一个 Android 应用程序。我查看了蓝牙 api,但它只允许作为客户端-服务器连接。就我而言,我不知道计算机上的 UUId 是什么。我需要看obex吗?我以前没用过它。所以任何帮助都是有益的。

I need to send file to a computer instead of another android application. I have looked at the bluetooth api, but it only allow connection as client-server. In my case I dont know what UUId would be on the computer. Do I need to look at obex. I haven't used it before. So any help would be benficial.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

玻璃人 2024-10-23 08:34:46

试试这个。
我可以使用此代码发送文件。

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;

}

Try this.
I can send a file using this code.

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);

Code of 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;

}
亣腦蒛氧 2024-10-23 08:34:46

对于冰淇淋三明治,此代码不起作用,因此您必须使用此代码

            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);
            }

For Ice Cream Sandwich this code is not working so you have to use this code

            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);
            }
虫児飞 2024-10-23 08:34:46

您可以使用 obex 库。看来android没有提供obex库,但我解决了问题,解决方案发布此处

进一步说明(如果您很忙,请从此处开始阅读)

  1. 我试图创建一个 Android 手机远程控制器(以及类似于 telnet 服务器的东西),它有助于控制使用我的旧功能手机远程打电话。

主要内容:蓝牙FTP客户端

  1. 我的第一个计划是让应用程序检查我的功能手机目录中的文件列表。

  2. 但我不知道如何连接到功能手机的 ftp 服务器。

  3. 我在 google 上搜索了很多关于如何通过蓝牙连接到 ftp 服务器的信息,但我只能找到 Bluetoorh FTP 服务器使用 OBEX 协议

  4. 我在 SO 线程中找到了有用的材料(PDF 文件),并研究了 OBEX 连接请求、放置和获取操作。

  5. 所以我最终编写了一些尝试连接到蓝牙FTP服务器的代码。我想向你展示它们,但我丢失了它:(这些代码就像直接将字节序列写入输出流。

  6. 我也很难找出 UUID 使应用程序连接为但我尝试了使用下面的代码检索的每个 UUID。

    字符串包裹=“”;
    ParcelUuid[] uuids=mBtDevice.getUuids();
    整数i=0;
    对于 (ParcelUuid p:uuids)
    {
         包裹 += "UUID UUID" + new Integer(i).toString() + "=UUID.fromString((\"" + p.getUuid().toString() + "\"));\n\n" ;
         ++我;
    }
    
  7. 似乎没有什么能让我得到我想要的答案。所以我在谷歌上进行了更多搜索,发现我不仅应该使用 UUID 00001106-0000-1000-8000-00805f9b34fb 来连接到 OBEX FTP 服务器,而且还应该传输目标标头 **发送 OBEX 连接请求时使用 UUID **F9EC7BC4-953C-11D2-984E-525400DC9E09
    下面的代码显示了如何作为客户端连接到蓝牙 FTP 服务器。

    <前><代码>尝试
    {
    mBtSocket = mBtDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(" 00001106-0000-1000-8000-00805f9b34fb"));
    }
    捕获(异常 e)
    {
    //e.printStackTrace();
    }

    线程线程=new Thread(new Runnable() {
    公共无效运行()
    {
    UUID uuid=UUID.fromString("F9EC7BC4-953C-11D2-984E-525400DC9E09");
    ByteBuffer bb = ByteBuffer.wrap(新字节[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    byte[] bytes=bb.array();
    操作 putOperation=null;
    操作 getOperation=null;
    尝试
    {
    // 连接套接字
    mBtSocket.connect();
    //下面我会解释
    mSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));

    HeaderSet headerset = new HeaderSet();
    headerset.setHeader(HeaderSet.TARGET, 字节);

    headerset = mSession.connect(headerset);

    if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK)
    {
    mConnected = true;
    }
    别的
    {
    mSession.disconnect(头集);
    }
    ...

然后,您现在已作为 FTP 客户端进行连接,并准备好使用 OBEX 操作来发送文件、请求文件、列出目录等。

  1. 但是,我不想等待一个小时才能将命令发送到我的 Android 手机。 (如果我增加轮询的频率,效率会很低,就像所有轮询方法一样。)

如果你很忙,请从这里开始阅读
主要内容:OBEX OPP

由于我上面提到的原因,我贪婪地寻找从OBEX文档中发现的操作OPP的方法。

您可能希望正常地通过蓝牙将文件传输到您的计算机(无需定义协议并为其构建新的桌面应用程序),对吧?然后发送到在桌面 Windows 计算机上本机运行的 OBEX OPP 收件箱服务是最佳解决方案。那么我们如何连接OPP(Obex Object Push)收件箱服务呢?

  1. 设置 OBEX 库
    import javax.obex; 添加到源代码中。
    如果您的编译器不支持 OBEX 库,请从 这里
  2. 实施ObexTransport
    使用时,您应该向库提供一个实现 ObexTransport 的类。它定义了库应如何发送数据(例如通过 RFCOMM、TCP 等)。示例实现为 在这里。这可能会导致一些运行时或编译错误,例如没有方法。但您可以通过将方法调用替换为 return 4096 等常量来部分修复这些问题,而不是 return mSocket.getMaxTransmitPacketSize();,从而超越 if 的注释public int getMaxTransmitPacketSize() 的语句。或者您可以尝试使用反射来运行这些方法。
  3. 获取蓝牙套接字
    使用 mBtDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(" 00001105-0000-1000-8000-00805f9b34fb" )); 获取蓝牙套接字并调用 connect() 。
  4. 创建客户端会话
    创建 ObexTransport 实现的实例,并创建一个新的 ClientSession,例如 mSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));
  5. 将 OBEX 连接请求发送到您的计算机 OPP 收件箱服务。

    HeaderSet headerset = new HeaderSet();
    // headerset.setHeader(HeaderSet.COUNT,n);
    headerset = mSession.connect(null);
    if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK)
    {
         mConnected = true;
    }
    
  6. 使用 ClientSession 发送 OBEX put 请求。

    protected boolean Put(ClientSession session, byte[] bytes, String as, String type)
    {
        // TODO: 实现这个方法
        //byte[]字节;
        字符串文件名=as;
        布尔重试=true;
        整数次=0;
        while(重试&&次数<4)
        {        
            操作 putOperation=null;
            输出流 mOutput = null;
            //ClientSession mSession = null;
            //ArrayUtils.reverse(字节);
            尝试
            {    
                // 将带有元数据的文件发送到服务器
                最终 HeaderSet hs = new HeaderSet();
                hs.setHeader(HeaderSet.NAME, 文件名);
                hs.setHeader(HeaderSet.TYPE, 类型);
                hs.setHeader(HeaderSet.LENGTH, new Long((long)bytes.length));
                Log.v(标签,文件名);
                //Log.v(TAG,类型);
                Log.v(TAG,bytes.toString());
                putOperation = session.put(hs);
    
                mOutput = putOperation.openOutputStream();
                mOutput.write(字节);
                mOutput.close();
                putOperation.close();
            }
            捕获(异常 e)
            {
                Log.e(TAG, "放置失败", e);
                重试=真;
                次++;
                继续;
                //e.printStackTrace();
            }
            最后
            {
                尝试
                {
    
                    if(mOutput!=null)
                        mOutput.close();
                    if(putOperation!=null)
                        putOperation.close();
                }
                捕获(异常 e)
                {
                    Log.e(TAG, "最后放", e);
                    重试=真;
                    次++;
                    继续;
                }
                //updateStatus("[CLIENT] 连接已关闭");
            }
            重试=假;
            返回真;
        }
        返回假;
    }
    
  7. 最后,断开连接。

    private void FinishBatch(ClientSession mSession) 抛出 IOException
    {
        mSession.disconnect(null);
       尝试
       {
             Thread.sleep((长)500);
       }
       捕获(InterruptedException e)
       {}
        mBtSocket.close();
    }
    
  8. 这是一个包装类。

    导入 android.bluetooth.*;
    导入 android.util.*;
    导入java.io.*;
    导入 java.util.*;
    导入 javax.obex.*;
    
    公共类BluetoothOPPHelper
    {
        字符串地址;
        蓝牙适配器mBtadapter;
        蓝牙设备;
        ClientSession 会话;
        蓝牙套接字 mBtSocket;
        受保护的最终 UUID OPPUUID=UUID.fromString(("00001105-0000-1000-8000-00805f9b34fb"));
    
        私有字符串TAG =“蓝牙OPPHelper”;
    
    公共蓝牙OPPHelper(字符串地址)
    {
        mBtadapter=蓝牙适配器.getDefaultAdapter();
        设备=mBtadapter.getRemoteDevice(地址);
        尝试
        {
            mBtSocket = device.createRfcommSocketToServiceRecord(OPPUUID);
        }
        捕获(IOException e)
        {
            抛出新的运行时异常(e);
    
        }
    }
    公共 ClientSession StartBatch(int n)
    {
        ClientSession mSession = null;
    
        // TODO: 实现这个方法
        布尔重试=true;
        整数次=0;
        while(重试&&次数<4)
        {
            //BluetoothConnector.BluetoothSocketWrapper bttmp=null;
            尝试
            {
                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();
                 }*/
            }
            捕获(异常 e)
            {
                Log.e(TAG, "opp 失败袜子 " + e.getMessage());
                重试=真;
                次++;
                继续;
            }        
    
            尝试
            {
                //mBtSocket=bttmp.getUnderlyingSocket();
                // mBtSocket.connect();
                蓝牙ObexTransport 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)
                {
                    布尔值 mConnected = true;
                }
                别的
                {
                    Log.e(TAG, "OPP 发送被拒绝;");
                    mSession.disconnect(头集);
                    次++;
                    继续;
                }
    
            }
            捕获(异常 e)
            {
                Log.e(TAG, "opp 失败;" , e);
                重试=真;
                次++;
                继续;
                //e.rintStackTrace();
            }
            重试=假;
        }
        返回mSession;
    
    }
    
    protected boolean Put(ClientSession session, byte[] bytes, String as, String type)
    {
        // TODO: 实现这个方法
        //byte[]字节;
        字符串文件名=as;
        布尔重试=true;
        整数次=0;
        while(重试&&次数<4)
        {        
            操作 putOperation=null;
            输出流 mOutput = null;
            //ClientSession mSession = null;
            //ArrayUtils.reverse(字节);
            尝试
            {    
                // 将带有元数据的文件发送到服务器
                最终 HeaderSet hs = new HeaderSet();
                hs.setHeader(HeaderSet.NAME, 文件名);
                hs.setHeader(HeaderSet.TYPE, 类型);
                hs.setHeader(HeaderSet.LENGTH, new Long((long)bytes.length));
                Log.v(标签,文件名);
                //Log.v(TAG,类型);
                Log.v(TAG,bytes.toString());
                putOperation = session.put(hs);
    
                mOutput = putOperation.openOutputStream();
                mOutput.write(字节);
                mOutput.close();
                putOperation.close();
            }
            捕获(异常 e)
            {
                Log.e(TAG, "放置失败", e);
                重试=真;
                次++;
                继续;
                //e.printStackTrace();
            }
            最后
            {
                尝试
                {
    
                    if(mOutput!=null)
                        mOutput.close();
                    if(putOperation!=null)
                        putOperation.close();
                }
                捕获(异常 e)
                {
                    Log.e(TAG, "最后放", e);
                    重试=真;
                    次++;
                    继续;
                }
                //updateStatus("[CLIENT] 连接已关闭");
            }
            重试=假;
            返回真;
        }
        返回假;
    }
    
    protected boolean Put(ClientSession s, OPPBatchInfo 信息)
    {
        return Put(s,info.data,info.as,info.type);
    }
    
    private void FinishBatch(ClientSession mSession) 抛出 IOException
    {
        mSession.disconnect(null);
        尝试
        {
            Thread.sleep((长)500);
        }
        捕获(InterruptedException e)
        {}
        mBtSocket.close();
    }
    
    公共布尔刷新()抛出IOException
    {
        if (sendQueue.isEmpty())
        {
            返回真;
        }
        尝试
        {
            Thread.sleep((长)2000);
        }
        捕获(InterruptedException e)
        {}
        ClientSession会话=StartBatch(sendQueue.size());
        如果(会话==空)
        {
            返回假;
        }
        while (!sendQueue.isEmpty())
        {
            if (Put(会话, sendQueue.remove()) == false)
            {
                Log.e(TAG, "放置失败");
            }
        }
        完成批处理(会话);
        返回真;
    }
    队列发送队列;
    公共布尔AddTransfer(字符串为,字符串mimetype,字节[]数据)
    {
        return sendQueue.add(new OPPBatchInfo(as,mimetype,data));
    }
    OPPBatchInfo 类
    {
        字符串为;
        字符串类型;
        字节[]数据;
        public OPPBatchInfo(String as,字符串类型,byte[]数据)
            {
                这.as=as;
                this.data=数据;
                this.type=类型;
            }
        }
    }
    

You can use the obex library. It seemed that android didn't provide the obex library, but I solved the problem and the solution is posted here.

Further Explanation (please start reading from here if you're busy)

  1. I was trying to create an android phone remote controller (and something similar to telnet server) which helps controlling the phone remotely with my old feature phone.

Main content :Bluetooth FTP client

  1. My first plan was to make the app check the list of files of my feature phone's directory.

  2. But I didn't know how to connect to my feature phone's ftp server.

  3. I googled a lot about how to connect to a ftp server via bluetooth but I could only find that Bluetoorh FTP server used the OBEX Protocol.

  4. I found a useful material (PDF file) in a SO thread and studied about OBEX connect requests, put and get operations.

  5. So I finally wrote some codes that tries to connect to the Bluetooth FTP server. I want to show them to you, but I lost it :( The codes were like just directly writting byte sequences to the output stream.

  6. I also had difficult time finding out what UUID makes the app connect as FTP client. But I tried every UUIDs retrieved using the code below.

    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;
    }
    
  7. Nothing seemed to bring me to the answer I wanted. So I googled more and found out that I not only should I use UUID 00001106-0000-1000-8000-00805f9b34fb to connect to OBEX FTP server, but also shold I transmit target header ** with UUID **F9EC7BC4-953C-11D2-984E-525400DC9E09 when sending OBEX connect request.
    The code below shows how to connect to a bluetooth FTP server as a client.

    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);
                    }
        ...
    

Then you are now connected as FTP client and are ready to use OBEX operations to send files, request for files, list directories, etc.

  1. However I didn't want to wait an hour to send my command to my android phone. (And it would be inefficient if I increase frequency of polling, as every polling methods are.)

Start reading from here if you are busy
Main content: OBEX OPP

For the reason I mentioned above, I greedly searched for ways to manipulate OPP which I discovered from the OBEX documentation.

You may want to transfer files via bluetooth normally (without defining your protocol and building a new desktop application just for it) to your computer, right? Then sending to OBEX OPP inbox service that is running natively on your desktop windows computer is the best solution. So how can we connect to the OPP (Obex Object Push) inbox service?

  1. Setup OBEX library
    Add import javax.obex; to your source code.
    If your compiler doesn't support OBEX library, download sources and add to your project from here.
  2. Implement ObexTransport
    You should provide a class that implements ObexTransport to the library when you use it. It defines how the library should send data (like by RFCOMM, TCP,...). A sample implementation is here. This can cause some runtime or compilation errors such as there's no method. But you can partially fix those by replacing the method calls to constants like return 4096 instead of return mSocket.getMaxTransmitPacketSize();, outcommenting the if statements of public int getMaxTransmitPacketSize(). Or you can try using reflection to get those methods runtime.
  3. Get BluetoothSocket
    Get a bluetooth socket using mBtDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(" 00001105-0000-1000-8000-00805f9b34fb" )); And call connect().
  4. Create ClientSession
    Create a instance of your ObexTransport implementation, and create a new ClientSession like mSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));.
  5. Send OBEX connect request to your computer OPP inbox service.

    HeaderSet headerset = new HeaderSet();
    //    headerset.setHeader(HeaderSet.COUNT,n);
    headerset = mSession.connect(null);
    if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK)
    {
         mConnected = true;
    }
    
  6. Send OBEX put requests using the 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. Finally, disconnect.

    private void FinishBatch(ClientSession mSession) throws IOException
    {
        mSession.disconnect(null);
       try
       {
             Thread.sleep((long)500);
       }
       catch (InterruptedException e)
       {}
        mBtSocket.close();
    }
    
  8. Then here is a wrapper class.

    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;
            }
        }
    }
    
七分※倦醒 2024-10-23 08:34:46

您需要通过 OBEX 实施 FTP。一旦您实现了标准协议和配置文件,您的 Android FTP 实现将与几乎任何蓝牙 FTP 服务器进行互操作。您还需要实施 OPP 以获得最大的互操作性。 OBEX 协议实施起来并不困难,而且规范是免费提供的。

You need to implement FTP over OBEX. Once you implement the standard protocol and profile, your Android FTP implementation will inter-operate with virtually any Bluetooth FTP server. You'll also need to implement OPP for maximum inter-operability. The OBEX protocol is not so difficult to implement and the specs is freely available.

青朷 2024-10-23 08:34:46

我知道这个问题已经很老了,但对于仍然需要处理这个问题的任何人来说:

使用这个库,您可以通过 OBEX 发送文件并通过 RFCOMM 发送命令:
https://github.com/ddibiasi/Funker

连接到目标设备后,您可以对其进行操作文件系统。

以下示例发送一个文件:

val rxOBEX = RxObex(device)
rxOBEX
    .putFile("rubberduck.txt", "text/plain", "oh hi mark".toByteArray(), "example/directory")  // Name of file, mimetype, bytes of file, directory
    .subscribeBy(
        onComplete = {
            Log.d(TAG, "Succesfully sent a testfile to device")
        },
        onError = { e ->
            Log.e(TAG, "Received error!")
        }
    )

该库基于 Rx 构建,因此所有调用都是非阻塞的。

I know this question is old, but for anyone having to deal with this still:

With this library you can send files via OBEX and commands via RFCOMM:
https://github.com/ddibiasi/Funker

Once connected to your target device, you can manipulate its filesystem.

The following example sends a file:

val rxOBEX = RxObex(device)
rxOBEX
    .putFile("rubberduck.txt", "text/plain", "oh hi mark".toByteArray(), "example/directory")  // Name of file, mimetype, bytes of file, directory
    .subscribeBy(
        onComplete = {
            Log.d(TAG, "Succesfully sent a testfile to device")
        },
        onError = { e ->
            Log.e(TAG, "Received error!")
        }
    )

The library is built on Rx, so all calls are non blocking.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文