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.
5 に答える
これを試して。このコードを使用してファイルを送信できます。
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;
}
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);
}
obex ライブラリを使用できます。android は obex ライブラリを提供していないようでしたが、問題を解決し、解決策がここに掲載されています。
詳しい解説(お忙しい方はこちらからどうぞ)
- 古いフィーチャーフォンで電話をリモートで制御するのに役立つAndroid電話リモートコントローラー(およびtelnetサーバーに似たもの)を作成しようとしていました。
主な内容 :Bluetooth FTP クライアント
私の最初の計画は、フィーチャーフォンのディレクトリのファイルのリストをアプリにチェックさせることでした。
しかし、フィーチャーフォンの ftp サーバーへの接続方法がわかりませんでした。
Bluetooth経由でftpサーバーに接続する方法についてよくグーグルで検索しましたが、Bluetoorh FTPサーバーが
OBEX Protocol
.SOスレッドで役立つ資料(PDFファイル)を見つけて、OBEXのconnectリクエスト、put、get操作について勉強しました。
Bluetooth FTP
そこで、サーバーに接続しようとするコードをいくつか書きました。それらをあなたに見せたいのですが、私はそれを失いました:(コードは、バイトシーケンスを出力ストリームに直接書き込むようなものでした.また、アプリを FTP クライアントとして接続させる UUID を見つけるのにも苦労しました。しかし、以下のコードを使用して取得したすべての 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 を使用して OBEX FTP サーバーに接続するだけでなく、ターゲット ヘッダー ** を UUID **F9EC7BC4-953C-11D2- で送信する必要があることがわかりました。リクエスト送信時は984E-525400DC9E09
OBEX connect
。以下のコードは、Bluetooth FTP サーバーにクライアントとして接続する方法を示しています。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 操作を使用してファイルの送信、ファイルの要求、ディレクトリの一覧表示などを行う準備が整いました。
- ただし、コマンドを Android フォンに送信するのに 1 時間も待ちたくありませんでした。(そして、すべてのポーリング方法がそうであるように、ポーリングの頻度を増やすと非効率になります。)
お忙しい方はこちらからお読みください 主な内容:OBEX OPP
上記の理由から、OBEX ドキュメントから発見した OPP を操作する方法を貪欲に検索しました。
Bluetooth 経由でファイルを通常どおり (プロトコルを定義したり、専用の新しいデスクトップ アプリケーションをビルドしたりせずに) コンピュータに転送したい場合がありますよね? 次に、デスクトップの Windows コンピューターでネイティブに実行されている OBEX OPP インボックス サービスに送信することが最適なソリューションです。では、OPP (Obex Object Push) 受信トレイ サービスに接続するにはどうすればよいでしょうか。
- OBEX ライブラリのセットアップ
import javax.obex;
ソース コードに追加します。コンパイラが OBEX ライブラリをサポートしていない場合は、ソースをダウンロードして、ここからプロジェクトに追加してください。 - 実装ライブラリを使用するときに、ライブラリに
ObexTransport
実装するクラスを提供する必要があります。ObexTransport
ライブラリがデータを送信する方法 (RFCOMM、TCP など) を定義します。サンプルの実装はこちらです。これにより、実行時エラーまたはコンパイル エラーが発生する可能性がありますthere's no method
。return 4096
ただし、メソッド呼び出しを の代わりに のような定数に置き換え、 のステートメントをreturn mSocket.getMaxTransmitPacketSize();
アウトコメントすることで、これらを部分的に修正できます。または、リフレクションを使用して、これらのメソッドのランタイムを取得することもできます。if
public int getMaxTransmitPacketSize()
- Get
BluetoothSocket
を使用して Bluetooth ソケットを取得し、 call をmBtDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(" 00001105-0000-1000-8000-00805f9b34fb" ));
呼び出しますconnect()
。 - 作成実装
ClientSession
のインスタンスをObexTransport
作成し、新しいClientSession
likeを作成しますmSession = new ClientSession((ObexTransport)(mTransport = new BluetoothObexTransport(mBtSocket)));
。 OBEX 接続要求をコンピューターの OPP インボックス サービスに送信します。
HeaderSet headerset = new HeaderSet(); // headerset.setHeader(HeaderSet.COUNT,n); headerset = mSession.connect(null); if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK) { mConnected = true; }
を使用して OBEX put リクエストを送信し
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; }
最後に、切断します。
private void FinishBatch(ClientSession mSession) throws IOException { mSession.disconnect(null); try { Thread.sleep((long)500); } catch (InterruptedException e) {} mBtSocket.close(); }
次に、ラッパー クラスを示します。
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; } } }