Android 蓝牙 OPP传输文件(下)

接上篇。
上篇最后有提到连接connect,这里接着向下看。

1 BluetoothOppObexClientSession
private void connect(int numShares) {
    if (D) {
        Log.d(TAG, "Create ClientSession with transport " + mTransport1.toString());
    }
    try {
        mCs = new ClientSession(mTransport1);
        mConnected = true;
    } catch (IOException e1) {
        Log.e(TAG, "OBEX session create error");
    }
    if (mConnected) {
        mConnected = false;
        HeaderSet hs = new HeaderSet();
        hs.setHeader(HeaderSet.COUNT, (long) numShares);
        synchronized (this) {
            mWaitingForRemote = true;
        }
        try {
            mCs.connect(hs);
            if (D) {
                Log.d(TAG, "OBEX session created");
            }
            mConnected = true;
        } catch (IOException e) {
            Log.e(TAG, "OBEX session connect error");
        }
    }
    synchronized (this) {
        mWaitingForRemote = false;
    }
}
2 ClientSession
public HeaderSet connect(final HeaderSet header) throws IOException {
	...
	
    /*
    * Write the OBEX CONNECT packet to the server.
    * Byte 0: 0x80
    * Byte 1&2: Connect Packet Length
    * Byte 3: OBEX Version Number (Presently, 0x10)
    * Byte 4: Flags (For TCP 0x00)
    * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
    * Byte 7 to n: headers
    */
    byte[] requestPacket = new byte[totalLength];
    int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
    // We just need to start at  byte 3 since the sendRequest() method will
    // handle the length and 0x80.
    requestPacket[0] = (byte)0x10;
    requestPacket[1] = (byte)0x00;
    requestPacket[2] = (byte)(maxRxPacketSize >> 8);
    requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
    if (head != null) {
        System.arraycopy(head, 0, requestPacket, 4, head.length);
    }

    // Since we are not yet connected, the peer max packet size is unknown,
    // hence we are only guaranteed the server will use the first 7 bytes.
    if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
        throw new IOException("Packet size exceeds max packet size for connect");
    }

    HeaderSet returnHeaderSet = new HeaderSet();
    sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);

    /*
    * Read the response from the OBEX server.
    * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
    * Byte 1&2: Packet Length
    * Byte 3: OBEX Version Number
    * Byte 4: Flags3
    * Byte 5&6: Max OBEX packet Length
    * Byte 7 to n: Optional HeaderSet
    */
    if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
        mObexConnected = true;
    }
    setRequestInactive();

    return returnHeaderSet;
}

这里的注释很清楚:

  • Byte 0: 0x80
  • Byte 1&2: Connect Packet Length
  • Byte 3: OBEX Version Number (Presently, 0x10)
  • Byte 4: Flags (For TCP 0x00)
  • Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
  • Byte 7 to n: headers

其中requestPacket为Obex cmd的Byte 3后面的数据,前两个Byte在sendRequest中添加。
ObexHelper.OBEX_OPCODE_CONNECT = 0x80

public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
        PrivateInputStream privateInput, boolean srmActive) throws IOException {
    ...
    
    int bytesReceived;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    out.write((byte)opCode);

    // Determine if there are any headers to send
    if (head == null) {
        out.write(0x00);
        out.write(0x03);
    } else {
        out.write((byte)((head.length + 3) >> 8));
        out.write((byte)(head.length + 3));
        out.write(head);
    }

    if (!skipSend) {
        // Write the request to the output stream and flush the stream
        mOutput.write(out.toByteArray());
        // TODO: is this really needed? if this flush is implemented
        //       correctly, we will get a gap between each obex packet.
        //       which is kind of the idea behind SRM to avoid.
        //  Consider offloading to another thread (async action)
        mOutput.flush();
    }
    
	...
}

这里将0x80和length写进前两位,再通过mOutput写入。

3 mOutput是什么呢?

在ClientSession构造函数中mOutput = trans.openOutputStream()

public ClientSession(final ObexTransport trans) throws IOException {
    mInput = trans.openInputStream();
    mOutput = trans.openOutputStream();
    mOpen = true;
    mRequestActive = false;
    mLocalSrmSupported = trans.isSrmSupported();
    mTransport = trans;
}

trans是上篇中提到的 transport = new BluetoothObexTransport(mBtSocket);

public OutputStream openOutputStream() throws IOException {
    return mSocket.getOutputStream();
}

mSocket就是之前BluetoothOppTransfer中的mBtSocket = mDevice.createInsecureL2capSocket(mL2cChannel);

BluetoothSocket.java

public OutputStream getOutputStream() throws IOException {
    return mOutputStream;
}

所以mOutput就是BluetoothSocket中的mOutputStream

BluetoothOutputStream.java

public void write(byte[] b, int offset, int count) throws IOException {
    if (b == null) {
        throw new NullPointerException("buffer is null");
    }
    if ((offset | count) < 0 || count > b.length - offset) {
        throw new IndexOutOfBoundsException("invalid offset or length");
    }
    mSocket.write(b, offset, count);
}
BluetoothSocket.java
/*package*/ int write(byte[] b, int offset, int length) throws IOException {

    //TODO: Since bindings can exist between the SDU size and the
    //      protocol, we might need to throw an exception instead of just
    //      splitting the write into multiple smaller writes.
    //      Rfcomm uses dynamic allocation, and should not have any bindings
    //      to the actual message length.
    if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
    if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) {
        if (length <= mMaxTxPacketSize) {
            mSocketOS.write(b, offset, length);
        } else {
            if (DBG) {
                Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
                        + "Packet will be divided into SDU packets of size "
                        + mMaxTxPacketSize);
            }
            int tmpOffset = offset;
            int bytesToWrite = length;
            while (bytesToWrite > 0) {
                int tmpLength = (bytesToWrite > mMaxTxPacketSize)
                        ? mMaxTxPacketSize
                        : bytesToWrite;
                mSocketOS.write(b, tmpOffset, tmpLength);
                tmpOffset += tmpLength;
                bytesToWrite -= tmpLength;
            }
        }
    } else {
        mSocketOS.write(b, offset, length);
    }
    // There is no good way to confirm since the entire process is asynchronous anyway
    if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
    return length;
}
4 mSocketOS是什么

之前在BluetoothOppTransfer.java中有调用mBtSocket.connect();

public void connect() throws IOException {
    if (mDevice == null) throw new IOException("Connect is called on null device");

    try {
        if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
        IBluetooth bluetoothProxy =
                BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
        if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
        mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
                mUuid, mPort, getSecurityFlags());
        synchronized (this) {
            if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
            if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
            if (mPfd == null) throw new IOException("bt socket connect failed");
            FileDescriptor fd = mPfd.getFileDescriptor();
            mSocket = LocalSocket.createConnectedLocalSocket(fd);
            mSocketIS = mSocket.getInputStream();
            mSocketOS = mSocket.getOutputStream();
        }
        int channel = readInt(mSocketIS);
        if (channel <= 0) {
            throw new IOException("bt socket connect failed");
        }
        mPort = channel;
        waitSocketSignal(mSocketIS);
        synchronized (this) {
            if (mSocketState == SocketState.CLOSED) {
                throw new IOException("bt socket closed");
            }
            mSocketState = SocketState.CONNECTED;
        }
    } catch (RemoteException e) {
        Log.e(TAG, Log.getStackTraceString(new Throwable()));
        throw new IOException("unable to send RPC: " + e.getMessage());
    }
}

IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
通过binder调用蓝牙AdapterService接口。
mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
mUuid, mPort, getSecurityFlags());

IBluetoothSocketManager getSocketManager() {
    android.os.IBinder obj = getSocketManagerNative();
    if (obj == null) {
        return null;
    }

    return IBluetoothSocketManager.Stub.asInterface(obj);
}
Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp
static jobject getSocketManagerNative(JNIEnv* env) {
  std::lock_guard<std::mutex> lock(sSocketManagerMutex);
  if (!sSocketManager.get()) {
    sSocketManager =
        new BluetoothSocketManagerBinderServer(sBluetoothSocketInterface);
  }
  return javaObjectForIBinder(env, IInterface::asBinder(sSocketManager));
}
Status BluetoothSocketManagerBinderServer::connectSocket(
    const BluetoothDevice& device, int32_t type,
    const std::unique_ptr<ParcelUuid>& uuid, int32_t port, int32_t flag,
    std::unique_ptr<ParcelFileDescriptor>* _aidl_return) {
  if (!isCallerActiveUserOrManagedProfile()) {
    LOG(WARNING) << "connectSocket() - Not allowed for non-active users";
    return Status::fromExceptionCode(
        Status::EX_SECURITY, String8("Not allowed for non-active users"));
  }

  ENFORCE_PERMISSION(PERMISSION_BLUETOOTH);

  IPCThreadState* ipc = IPCThreadState::self();

  int socket_fd = -1;
  bt_status_t status = socketInterface->connect(
      &device.address, (btsock_type_t)type, uuid ? &uuid->uuid : nullptr, port,
      &socket_fd, flag, ipc->getCallingUid());
  if (status != BT_STATUS_SUCCESS) {
    LOG(ERROR) << "Socket connection failed: " << +status;
    socket_fd = -1;
  }

  if (socket_fd < 0) {
    LOG(ERROR) << "Fail to create file descriptor on socket fd";
    return Status::ok();
  }

  _aidl_return->reset(
      new ParcelFileDescriptor(android::base::unique_fd(socket_fd)));
  return Status::ok();
}

socketInterface是new BluetoothSocketManagerBinderServer(sBluetoothSocketInterface);中的sBluetoothSocketInterface

com_android_bluetooth_btservice_AdapterService.cpp
static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
                       jboolean isSingleUserMode) {
	sBluetoothSocketInterface =
	    (btsock_interface_t*)sBluetoothInterface->get_profile_interface(
	        BT_PROFILE_SOCKETS_ID);
}

因此socketInterface->connect就调用了btif_sock.cc中的btsock_connect。

static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type,
                                  const Uuid* uuid, int channel, int* sock_fd,
                                  int flags, int app_uid) {
  switch (type) {
    case BTSOCK_RFCOMM:
      status =
          btsock_rfc_connect(bd_addr, uuid, channel, sock_fd, flags, app_uid);
      break;

    case BTSOCK_L2CAP:
      status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
      break;
    case BTSOCK_L2CAP_LE:
      ...
    case BTSOCK_SCO:
      ...
    default:
      ...
      break;
  }
  return status;
}

这里应该是BTSOCK_L2CAP。

bt_status_t btsock_l2cap_connect(const RawAddress* bd_addr, int channel,
                                 int* sock_fd, int flags, int app_uid) {
  return btsock_l2cap_listen_or_connect(NULL, bd_addr, channel, sock_fd, flags,
                                        0, app_uid);
}
static bt_status_t btsock_l2cap_listen_or_connect(const char* name,
                                                  const RawAddress* addr,
                                                  int channel, int* sock_fd,
                                                  int flags, char listen,
                                                  int app_uid) {
  ...
  sock = btsock_l2cap_alloc_l(name, addr, listen, flags);
  ...

  if (stat == BT_STATUS_SUCCESS) {
    *sock_fd = sock->app_fd;
    ...
  } else {
    btsock_l2cap_free_l(sock);
  }

  return stat;
}

sock_fd即mPfd.getFileDescriptor()的返回值

static l2cap_socket* btsock_l2cap_alloc_l(const char* name,
                                          const RawAddress* addr,
                                          char is_server, int flags) {
  ...
  if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, fds)) {
    APPL_TRACE_ERROR("socketpair failed, errno:%d", errno);
    goto fail_sockpair;
  }

  sock->our_fd = fds[0];
  sock->app_fd = fds[1];
  ...

发送代码就追到这里

从log看应该是用btif_sock_l2cap.cc中的btsock_l2cap_signaled接收socket传来的数据。

void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id) {
  char drop_it = false;

  /* We use MSG_DONTWAIT when sending data to JAVA, hence it can be accepted to
   * hold the lock. */
  std::unique_lock<std::mutex> lock(state_lock);
  l2cap_socket* sock = btsock_l2cap_find_by_id_l(user_id);
  if (!sock) return;

  if ((flags & SOCK_THREAD_FD_RD) && !sock->server) {
    // app sending data
    if (sock->connected) {
      int size = 0;
      bool ioctl_success = ioctl(sock->our_fd, FIONREAD, &size) == 0;
      ...
        OSI_NO_INTR(count = recv(fd, buffer, size,
                                 MSG_NOSIGNAL | MSG_DONTWAIT | MSG_TRUNC));
        ...
          if (BTA_JvL2capWrite(sock->handle, PTR_TO_UINT(buffer), buffer, count,
                               user_id) != BTA_JV_SUCCESS) {
    }
  }
}
bt/bta/jv/bta_jv_api.cc
tBTA_JV_STATUS BTA_JvL2capWrite(uint32_t handle, uint32_t req_id,
                                uint8_t* p_data, uint16_t len,
                                uint32_t user_id) {
  tBTA_JV_STATUS status = BTA_JV_FAILURE;

  APPL_TRACE_API("%s", __func__);

  if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback) {
    tBTA_JV_API_L2CAP_WRITE* p_msg =
        (tBTA_JV_API_L2CAP_WRITE*)osi_malloc(sizeof(tBTA_JV_API_L2CAP_WRITE));
    p_msg->hdr.event = BTA_JV_API_L2CAP_WRITE_EVT;
    p_msg->handle = handle;
    p_msg->req_id = req_id;
    p_msg->p_data = p_data;
    p_msg->p_cb = &bta_jv_cb.l2c_cb[handle];
    p_msg->len = len;
    p_msg->user_id = user_id;

    bta_sys_sendmsg(p_msg);

    status = BTA_JV_SUCCESS;
  }

  return status;
}

之后就是bluedroid中进行出来,最后发送cmd。

你可能感兴趣的:(Bluetooth)