Bluetooth: Support MAP Client role on Bluedroid.
Implementation changes to support MAP client and PBAP client role on Bluedroid stack. Change-Id: I1733a67bf5256bd7b181bd5e68e40b476994ebfd
This commit is contained in:
committed by
Mike Lockwood
parent
e19a4fe32f
commit
8949bfb90c
@@ -306,6 +306,11 @@ public final class BluetoothDevice implements Parcelable {
|
||||
public static final String ACTION_UUID =
|
||||
"android.bluetooth.device.action.UUID";
|
||||
|
||||
/** @hide */
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_MAS_INSTANCE =
|
||||
"android.bluetooth.device.action.MAS_INSTANCE";
|
||||
|
||||
/**
|
||||
* Broadcast Action: Indicates a failure to retrieve the name of a remote
|
||||
* device.
|
||||
@@ -522,14 +527,17 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* Prefer BR/EDR transport for GATT connections to remote dual-mode devices
|
||||
* @hide
|
||||
*/
|
||||
public static final int TRANSPORT_BREDR = 1;
|
||||
public static final int TRANSPORT_BREDR = 1;
|
||||
|
||||
/**
|
||||
* Prefer LE transport for GATT connections to remote dual-mode devices
|
||||
* @hide
|
||||
*/
|
||||
public static final int TRANSPORT_LE = 2;
|
||||
public static final int TRANSPORT_LE = 2;
|
||||
|
||||
/** @hide */
|
||||
public static final String EXTRA_MAS_INSTANCE =
|
||||
"android.bluetooth.device.extra.MAS_INSTANCE";
|
||||
|
||||
/**
|
||||
* Lazy initialization. Guaranteed final after first object constructed, or
|
||||
@@ -995,6 +1003,18 @@ public final class BluetoothDevice implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean fetchMasInstances() {
|
||||
if (sService == null) {
|
||||
Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return sService.fetchRemoteMasInstances(this);
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int getServiceChannel(ParcelUuid uuid) {
|
||||
//TODO(BT)
|
||||
|
||||
103
core/java/android/bluetooth/BluetoothMasInstance.java
Normal file
103
core/java/android/bluetooth/BluetoothMasInstance.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.bluetooth;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/** @hide */
|
||||
public final class BluetoothMasInstance implements Parcelable {
|
||||
private final int mId;
|
||||
private final String mName;
|
||||
private final int mChannel;
|
||||
private final int mMsgTypes;
|
||||
|
||||
public BluetoothMasInstance(int id, String name, int channel, int msgTypes) {
|
||||
mId = id;
|
||||
mName = name;
|
||||
mChannel = channel;
|
||||
mMsgTypes = msgTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof BluetoothMasInstance) {
|
||||
return mId == ((BluetoothMasInstance)o).mId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mId + (mChannel << 8) + (mMsgTypes << 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" +
|
||||
Integer.toHexString(mMsgTypes);
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<BluetoothMasInstance> CREATOR =
|
||||
new Parcelable.Creator<BluetoothMasInstance>() {
|
||||
public BluetoothMasInstance createFromParcel(Parcel in) {
|
||||
return new BluetoothMasInstance(in.readInt(), in.readString(),
|
||||
in.readInt(), in.readInt());
|
||||
}
|
||||
public BluetoothMasInstance[] newArray(int size) {
|
||||
return new BluetoothMasInstance[size];
|
||||
}
|
||||
};
|
||||
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(mId);
|
||||
out.writeString(mName);
|
||||
out.writeInt(mChannel);
|
||||
out.writeInt(mMsgTypes);
|
||||
}
|
||||
|
||||
public static final class MessageType {
|
||||
public static final int EMAIL = 0x01;
|
||||
public static final int SMS_GSM = 0x02;
|
||||
public static final int SMS_CDMA = 0x04;
|
||||
public static final int MMS = 0x08;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public int getChannel() {
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
public int getMsgTypes() {
|
||||
return mMsgTypes;
|
||||
}
|
||||
|
||||
public boolean msgSupported(int msg) {
|
||||
return (mMsgTypes & msg) != 0;
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ interface IBluetooth
|
||||
int getRemoteClass(in BluetoothDevice device);
|
||||
ParcelUuid[] getRemoteUuids(in BluetoothDevice device);
|
||||
boolean fetchRemoteUuids(in BluetoothDevice device);
|
||||
boolean fetchRemoteMasInstances(in BluetoothDevice device);
|
||||
|
||||
boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode);
|
||||
boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[]
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<protected-broadcast android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
|
||||
<protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
|
||||
<protected-broadcast android:name="android.bluetooth.device.action.UUID" />
|
||||
<protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
|
||||
<protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
|
||||
<protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
|
||||
<protected-broadcast android:name="android.bluetooth.device.action.DISAPPEARED" />
|
||||
@@ -396,6 +397,15 @@
|
||||
android:label="@string/permlab_receiveWapPush"
|
||||
android:description="@string/permdesc_receiveWapPush" />
|
||||
|
||||
<!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
|
||||
or perform processing on them. -->
|
||||
<!-- @hide -->
|
||||
<permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
|
||||
android:permissionGroup="android.permission-group.MESSAGES"
|
||||
android:protectionLevel="dangerous"
|
||||
android:label="@string/permlab_receiveBluetoothMap"
|
||||
android:description="@string/permdesc_receiveBluetoothMap" />
|
||||
|
||||
<!-- =============================================================== -->
|
||||
<!-- Permissions for accessing social info (contacts and social) -->
|
||||
<!-- =============================================================== -->
|
||||
|
||||
@@ -760,6 +760,13 @@
|
||||
WAP messages. This permission includes the ability to monitor or delete
|
||||
messages sent to you without showing them to you.</string>
|
||||
|
||||
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permlab_receiveBluetoothMap">receive Bluetooth messages (MAP)</string>
|
||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permdesc_receiveBluetoothMap">Allows the app to receive and process Bluetooth MAP
|
||||
messages. This means the app could monitor or delete messages sent to your
|
||||
device without showing them to you.</string>
|
||||
|
||||
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permlab_getTasks">retrieve running apps</string>
|
||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The Android Open Source Project
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
@@ -66,6 +67,8 @@ public final class ClientOperation implements Operation, BaseStream {
|
||||
|
||||
private boolean mGetOperation;
|
||||
|
||||
private boolean mGetFinalFlag;
|
||||
|
||||
private HeaderSet mRequestHeader;
|
||||
|
||||
private HeaderSet mReplyHeader;
|
||||
@@ -90,6 +93,7 @@ public final class ClientOperation implements Operation, BaseStream {
|
||||
mOperationDone = false;
|
||||
mMaxPacketSize = maxSize;
|
||||
mGetOperation = type;
|
||||
mGetFinalFlag = false;
|
||||
|
||||
mPrivateInputOpen = false;
|
||||
mPrivateOutputOpen = false;
|
||||
@@ -130,6 +134,15 @@ public final class ClientOperation implements Operation, BaseStream {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set flag which will force GET to be always sent as single packet request with
|
||||
* final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
|
||||
* require requests to be sent this way.
|
||||
*/
|
||||
public void setGetFinalFlag(boolean flag) {
|
||||
mGetFinalFlag = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an ABORT message to the server. By calling this method, the
|
||||
* corresponding input and output streams will be closed along with this
|
||||
@@ -551,15 +564,25 @@ public final class ClientOperation implements Operation, BaseStream {
|
||||
|
||||
if (mGetOperation) {
|
||||
if (!mOperationDone) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
more = sendRequest(0x03);
|
||||
}
|
||||
if (!mGetFinalFlag) {
|
||||
mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
|
||||
while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
|
||||
more = sendRequest(0x03);
|
||||
}
|
||||
|
||||
if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
|
||||
}
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
}
|
||||
} else {
|
||||
more = sendRequest(0x83);
|
||||
|
||||
if (more) {
|
||||
throw new IOException("FINAL_GET forced but data did not fit into single packet!");
|
||||
}
|
||||
|
||||
if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
|
||||
}
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
}
|
||||
}
|
||||
@@ -613,7 +636,16 @@ public final class ClientOperation implements Operation, BaseStream {
|
||||
if (mPrivateInput == null) {
|
||||
mPrivateInput = new PrivateInputStream(this);
|
||||
}
|
||||
sendRequest(0x03);
|
||||
|
||||
if (!mGetFinalFlag) {
|
||||
sendRequest(0x03);
|
||||
} else {
|
||||
sendRequest(0x83);
|
||||
|
||||
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
|
||||
mOperationDone = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (mOperationDone) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The Android Open Source Project
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
@@ -181,6 +182,8 @@ public final class HeaderSet {
|
||||
|
||||
private String mName; // null terminated Unicode text string
|
||||
|
||||
private boolean mEmptyName;
|
||||
|
||||
private String mType; // null terminated ASCII text string
|
||||
|
||||
private Long mLength; // 4 byte unsigend integer
|
||||
@@ -234,6 +237,25 @@ public final class HeaderSet {
|
||||
mRandom = new SecureRandom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flag for special "value" of NAME header which should be empty. This
|
||||
* is not the same as NAME header with empty string in which case it will
|
||||
* have length of 5 bytes. It should be 3 bytes with only header id and
|
||||
* length field.
|
||||
*/
|
||||
public void setEmptyNameHeader() {
|
||||
mName = null;
|
||||
mEmptyName = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets flag for special "value" of NAME header which should be empty. See
|
||||
* above.
|
||||
*/
|
||||
public boolean getEmptyNameHeader() {
|
||||
return mEmptyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the header identifier to the value provided. The type
|
||||
* of object must correspond to the Java type defined in the description of
|
||||
@@ -269,6 +291,7 @@ public final class HeaderSet {
|
||||
if ((headerValue != null) && (!(headerValue instanceof String))) {
|
||||
throw new IllegalArgumentException("Name must be a String");
|
||||
}
|
||||
mEmptyName = false;
|
||||
mName = (String)headerValue;
|
||||
break;
|
||||
case TYPE:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright (c) 2008-2009, Motorola, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
@@ -387,6 +388,11 @@ public final class ObexHelper {
|
||||
if (nullOut) {
|
||||
headImpl.setHeader(HeaderSet.NAME, null);
|
||||
}
|
||||
} else if (headImpl.getEmptyNameHeader()) {
|
||||
out.write((byte) HeaderSet.NAME);
|
||||
lengthArray[0] = (byte) 0x00;
|
||||
lengthArray[1] = (byte) 0x03;
|
||||
out.write(lengthArray);
|
||||
}
|
||||
|
||||
// Type Header
|
||||
|
||||
Reference in New Issue
Block a user