summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRicardo Cerqueira <cyanogenmod@cerqueira.org>2014-06-03 15:39:10 +0100
committerRicardo Cerqueira <cyanogenmod@cerqueira.org>2014-06-03 15:39:10 +0100
commitad1d781dd189ad35f46f8755feb6164a882126cd (patch)
treec20ff91652ba053ae744e30cac802e7c1b6750cc
parent15bb907b0a1ccdcec7617778371748bee5f7eeb3 (diff)
parent24dce60783d85fbf49f550203a383b2f4a171a52 (diff)
downloadandroid_packages_apps_Bluetooth-ad1d781dd189ad35f46f8755feb6164a882126cd.tar.gz
android_packages_apps_Bluetooth-ad1d781dd189ad35f46f8755feb6164a882126cd.tar.bz2
android_packages_apps_Bluetooth-ad1d781dd189ad35f46f8755feb6164a882126cd.zip
Merge tag 'android-4.4.3_r1' into HEAD
Android 4.4.3 release 1 Conflicts: src/com/android/bluetooth/a2dp/Avrcp.java src/com/android/bluetooth/btservice/AdapterService.java src/com/android/bluetooth/gatt/GattService.java src/com/android/bluetooth/map/BluetoothMapContent.java src/com/android/bluetooth/map/BluetoothMapObexServer.java src/com/android/bluetooth/map/BluetoothMapService.java src/com/android/bluetooth/map/BluetoothMnsObexClient.java src/com/android/bluetooth/pbap/BluetoothPbapService.java Change-Id: I94ec3d09e5ff3644ed02206e28f9b90cd28aac37
-rw-r--r--AndroidManifest.xml3
-rw-r--r--jni/com_android_bluetooth_gatt.cpp36
-rw-r--r--res/values-da/strings.xml2
-rw-r--r--res/values-sk/strings.xml2
-rw-r--r--src/com/android/bluetooth/a2dp/A2dpService.java6
-rw-r--r--src/com/android/bluetooth/a2dp/A2dpStateMachine.java8
-rw-r--r--src/com/android/bluetooth/a2dp/Avrcp.java40
-rw-r--r--src/com/android/bluetooth/btservice/AdapterService.java2
-rw-r--r--src/com/android/bluetooth/btservice/ProfileService.java2
-rw-r--r--src/com/android/bluetooth/gatt/GattService.java366
-rw-r--r--src/com/android/bluetooth/gatt/HandleMap.java17
-rw-r--r--src/com/android/bluetooth/gatt/ServiceDeclaration.java23
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapContent.java53
-rwxr-xr-xtests/AndroidManifest.xml2
14 files changed, 478 insertions, 84 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4fad501c1..d33f2dae7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -76,7 +77,7 @@
android:exported="true"
android:process="@string/process">
<path-permission
- android:path="/btopp"
+ android:pathPrefix="/btopp"
android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
</provider>
<service
diff --git a/jni/com_android_bluetooth_gatt.cpp b/jni/com_android_bluetooth_gatt.cpp
index 854a92500..1ffac4e08 100644
--- a/jni/com_android_bluetooth_gatt.cpp
+++ b/jni/com_android_bluetooth_gatt.cpp
@@ -158,7 +158,7 @@ static jmethodID method_onGetDescriptor;
static jmethodID method_onGetIncludedService;
static jmethodID method_onRegisterForNotifications;
static jmethodID method_onReadRemoteRssi;
-static jmethodID method_onClientListen;
+static jmethodID method_onAdvertiseCallback;
/**
* Server callback methods
@@ -423,11 +423,10 @@ void btgattc_remote_rssi_cb(int client_if,bt_bdaddr_t* bda, int rssi, int status
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}
-void btgattc_listen_cb(int status, int client_if)
+void btgattc_advertise_cb(int status, int client_if)
{
CHECK_CALLBACK_ENV
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientListen
- , status, client_if);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAdvertiseCallback, status, client_if);
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}
@@ -449,7 +448,7 @@ static const btgatt_client_callbacks_t sGattClientCallbacks = {
btgattc_write_descriptor_cb,
btgattc_execute_write_cb,
btgattc_remote_rssi_cb,
- btgattc_listen_cb
+ btgattc_advertise_cb
};
@@ -680,7 +679,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
method_onAttributeRead= env->GetMethodID(clazz, "onAttributeRead", "(Ljava/lang/String;IIIIZ)V");
method_onAttributeWrite= env->GetMethodID(clazz, "onAttributeWrite", "(Ljava/lang/String;IIIIIZZ[B)V");
method_onExecuteWrite= env->GetMethodID(clazz, "onExecuteWrite", "(Ljava/lang/String;III)V");
- method_onClientListen = env->GetMethodID(clazz, "onClientListen", "(II)V");
+ method_onAdvertiseCallback = env->GetMethodID(clazz, "onAdvertiseCallback", "(II)V");
info("classInitNative: Success!");
}
@@ -1058,7 +1057,7 @@ static void gattClientReadRemoteRssiNative(JNIEnv* env, jobject object, jint cli
sGattIf->client->read_remote_rssi(clientif, &bda);
}
-static void gattClientListenNative(JNIEnv *env, jobject object,
+static void gattAdvertiseNative(JNIEnv *env, jobject object,
jint client_if, jboolean start)
{
if (!sGattIf) return;
@@ -1066,17 +1065,28 @@ static void gattClientListenNative(JNIEnv *env, jobject object,
}
static void gattSetAdvDataNative(JNIEnv *env, jobject object, jint client_if, jboolean setScanRsp,
- jboolean inclName, jboolean inclTxPower, jint minInterval, jint maxInterval, jint appearance,
- jbyteArray manufacturerData)
+ jboolean inclName, jboolean inclTxPower, jint minInterval, jint maxInterval,
+ jint appearance, jbyteArray manufacturerData, jbyteArray serviceData,
+ jbyteArray serviceUuid)
{
if (!sGattIf) return;
- jbyte* arr_data = env->GetByteArrayElements(manufacturerData, 0);
+ jbyte* arr_data = env->GetByteArrayElements(manufacturerData, NULL);
uint16_t arr_len = (uint16_t) env->GetArrayLength(manufacturerData);
+ jbyte* service_data = env->GetByteArrayElements(serviceData, NULL);
+ uint16_t service_data_len = (uint16_t) env->GetArrayLength(serviceData);
+
+ jbyte* service_uuid = env->GetByteArrayElements(serviceUuid, NULL);
+ uint16_t service_uuid_len = (uint16_t) env->GetArrayLength(serviceUuid);
+
sGattIf->client->set_adv_data(client_if, setScanRsp, inclName, inclTxPower,
- minInterval, maxInterval, appearance, arr_len, (char*)arr_data);
+ minInterval, maxInterval, appearance, arr_len, (char*)arr_data,
+ service_data_len, (char*)service_data, service_uuid_len,
+ (char*)service_uuid);
env->ReleaseByteArrayElements(manufacturerData, arr_data, JNI_ABORT);
+ env->ReleaseByteArrayElements(serviceData, service_data, JNI_ABORT);
+ env->ReleaseByteArrayElements(serviceUuid, service_uuid, JNI_ABORT);
}
@@ -1292,7 +1302,7 @@ static JNINativeMethod sMethods[] = {
{"gattClientExecuteWriteNative", "(IZ)V", (void *) gattClientExecuteWriteNative},
{"gattClientRegisterForNotificationsNative", "(ILjava/lang/String;IIJJIJJZ)V", (void *) gattClientRegisterForNotificationsNative},
{"gattClientReadRemoteRssiNative", "(ILjava/lang/String;)V", (void *) gattClientReadRemoteRssiNative},
- {"gattClientListenNative", "(IZ)V", (void *) gattClientListenNative},
+ {"gattAdvertiseNative", "(IZ)V", (void *) gattAdvertiseNative},
{"gattServerRegisterAppNative", "(JJ)V", (void *) gattServerRegisterAppNative},
{"gattServerUnregisterAppNative", "(I)V", (void *) gattServerUnregisterAppNative},
@@ -1309,7 +1319,7 @@ static JNINativeMethod sMethods[] = {
{"gattServerSendNotificationNative", "(III[B)V", (void *) gattServerSendNotificationNative},
{"gattServerSendResponseNative", "(IIIIII[BI)V", (void *) gattServerSendResponseNative},
- {"gattSetAdvDataNative", "(IZZZIII[B)V", (void *) gattSetAdvDataNative},
+ {"gattSetAdvDataNative", "(IZZZIII[B[B[B)V", (void *) gattSetAdvDataNative},
{"gattTestNative", "(IJJLjava/lang/String;IIIII)V", (void *) gattTestNative},
};
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9649bc4fc..ef515d314 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -78,7 +78,7 @@
<string name="not_exist_file_desc" msgid="4059531573790529229">"Filen findes ikke. \n"</string>
<string name="enabling_progress_title" msgid="436157952334723406">"Vent..."</string>
<string name="enabling_progress_content" msgid="4601542238119927904">"Aktiverer Bluetooth..."</string>
- <string name="bt_toast_1" msgid="972182708034353383">"Filen modtages. Se status i meddelelsespanelet."</string>
+ <string name="bt_toast_1" msgid="972182708034353383">"Filen modtages. Se status i underretningspanelet."</string>
<string name="bt_toast_2" msgid="8602553334099066582">"Filen kan ikke modtages."</string>
<string name="bt_toast_3" msgid="6707884165086862518">"Modtagelse af fil fra \"<xliff:g id="SENDER">%1$s</xliff:g>\" stoppet"</string>
<string name="bt_toast_4" msgid="4678812947604395649">"Sender filen til \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 7e3522f2e..198bbb78b 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Získať prístup k správcovi preberania."</string>
+ <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Získať prístup k správcovi sťahovania."</string>
<string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Umožňuje aplikácii pristupovať k Správcovi BluetoothShare a použiť ho na prenos súborov."</string>
<string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Prístup povoleného zariadenia Bluetooth."</string>
<string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Umožňuje aplikácii dočasne povoliť zariadenie Bluetooth, čím sa povolí zariadeniu odosielať súbory do tohto zariadenia bez potvrdenia používateľa."</string>
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 22c6081b9..9b36f27ac 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -60,8 +60,8 @@ public class A2dpService extends ProfileService {
}
protected boolean start() {
- mStateMachine = A2dpStateMachine.make(this, this);
mAvrcp = Avrcp.make(this);
+ mStateMachine = A2dpStateMachine.make(this, this);
setA2dpService(this);
return true;
}
@@ -236,6 +236,10 @@ public class A2dpService extends ProfileService {
mAvrcp.setAbsoluteVolume(volume);
}
+ public void setAvrcpAudioState(int state) {
+ mAvrcp.setA2dpAudioState(state);
+ }
+
synchronized boolean isA2dpPlaying(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index b88e7b11c..cba3d0d54 100644
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -656,14 +656,16 @@ final class A2dpStateMachine extends StateMachine {
switch (state) {
case AUDIO_STATE_STARTED:
if (mPlayingA2dpDevice == null) {
- mPlayingA2dpDevice = device;
- broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
- BluetoothA2dp.STATE_NOT_PLAYING);
+ mPlayingA2dpDevice = device;
+ mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING);
+ broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
+ BluetoothA2dp.STATE_NOT_PLAYING);
}
break;
case AUDIO_STATE_STOPPED:
if (mPlayingA2dpDevice != null) {
mPlayingA2dpDevice = null;
+ mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING);
broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
BluetoothA2dp.STATE_PLAYING);
}
diff --git a/src/com/android/bluetooth/a2dp/Avrcp.java b/src/com/android/bluetooth/a2dp/Avrcp.java
index f2d559801..d4098d384 100644
--- a/src/com/android/bluetooth/a2dp/Avrcp.java
+++ b/src/com/android/bluetooth/a2dp/Avrcp.java
@@ -20,6 +20,7 @@ import java.util.Timer;
import java.util.TimerTask;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothA2dp;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -124,8 +125,9 @@ final class Avrcp {
private static final int MESSAGE_FAST_FORWARD = 10;
private static final int MESSAGE_REWIND = 11;
private static final int MESSAGE_CHANGE_PLAY_POS = 12;
+ private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
- private static final int MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT = 13;
+ private static final int MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT = 14;
private static final int SET_ADDR_PLAYER_TIMEOUT = 2000;
private int mAddressedPlayerChangedNT;
@@ -777,10 +779,24 @@ final class Avrcp {
int isAvailable = msg.arg2;
processRCCStateChange(callingPackageName, isFocussed, isAvailable);
break;
+
+ case MESSAGE_SET_A2DP_AUDIO_STATE:
+ if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1);
+ updateA2dpAudioState(msg.arg1);
+ break;
}
}
}
+ private void updateA2dpAudioState(int state) {
+ boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
+ if (isPlaying != isPlayingState(mCurrentPlayState)) {
+ updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING :
+ RemoteControlClient.PLAYSTATE_PAUSED,
+ RemoteControlClient.PLAYBACK_POSITION_INVALID);
+ }
+ }
+
private void updatePlayPauseState(int state, long currentPosMs) {
if (DEBUG) Log.v(TAG,"updatePlayPauseState");
boolean oldPosValid = (mCurrentPosMs !=
@@ -1399,6 +1415,20 @@ final class Avrcp {
return playStatus;
}
+ private boolean isPlayingState(int playState) {
+ boolean isPlaying = false;
+ switch (playState) {
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ isPlaying = true;
+ break;
+ default:
+ isPlaying = false;
+ break;
+ }
+ return isPlaying;
+ }
+
/**
* This is called from AudioService. It will return whether this device supports abs volume.
* NOT USED AT THE MOMENT.
@@ -1587,6 +1617,14 @@ private void updateLocalPlayerSettings( byte[] data) {
mHandler.sendMessageDelayed(msg, 130);
}
+ /**
+ * This is called from A2dpStateMachine to set A2dp audio state.
+ */
+ public void setA2dpAudioState(int state) {
+ Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0);
+ mHandler.sendMessage(msg);
+ }
+
// Do not modify without updating the HAL bt_rc.h files.
// match up with btrc_play_status_t enum of bt_rc.h
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index a62a20729..30321f499 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -504,7 +504,7 @@ public class AdapterService extends Service {
/**
* The Binder implementation must be declared to be a static class, with
* the AdapterService instance passed in the constructor. Furthermore,
- * when the AdapterService shuts down, the reference to the AdapterService
+ * when the AdapterService shuts down, the reference to the AdapterService
* must be explicitly removed.
*
* Otherwise, a memory leak can occur from repeated starting/stopping the
diff --git a/src/com/android/bluetooth/btservice/ProfileService.java b/src/com/android/bluetooth/btservice/ProfileService.java
index e3d9196fe..0c1b70e3f 100644
--- a/src/com/android/bluetooth/btservice/ProfileService.java
+++ b/src/com/android/bluetooth/btservice/ProfileService.java
@@ -38,6 +38,8 @@ public abstract class ProfileService extends Service {
public static final String BLUETOOTH_ADMIN_PERM =
android.Manifest.permission.BLUETOOTH_ADMIN;
public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ public static final String BLUETOOTH_PRIVILEGED =
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED;
public static interface IProfileServiceBinder extends IBinder {
public boolean cleanup();
diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java
index 8c94e5285..fa05c984d 100644
--- a/src/com/android/bluetooth/gatt/GattService.java
+++ b/src/com/android/bluetooth/gatt/GattService.java
@@ -20,28 +20,29 @@ import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothGattCallback;
import android.bluetooth.IBluetoothGattServerCallback;
import android.content.Intent;
import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
+import com.android.bluetooth.btservice.ProfileService;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.android.bluetooth.LeScanRequestArbitrator;
-import com.android.bluetooth.btservice.ProfileService;
-import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
/**
* Provides Bluetooth Gatt profile, as a service in
@@ -51,7 +52,30 @@ import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
public class GattService extends ProfileService {
private static final boolean DBG = GattServiceConfig.DBG;
private static final String TAG = GattServiceConfig.TAG_PREFIX + "GattService";
- BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ private static final int DEFAULT_SCAN_INTERVAL_MILLIS = 200;
+
+ /**
+ * Max packet size for ble advertising, defined in Bluetooth Specification Version 4.0 [Vol 3].
+ */
+ private static final int ADVERTISING_PACKET_MAX_BYTES = 31;
+ /**
+ * Size overhead for advertising flag.
+ */
+ private static final int ADVERTISING_FLAGS_BYTES = 3;
+ /**
+ * Size overhead per field. Usually it's just one byte of field length and one byte of
+ * field type.
+ */
+ private static final int FIELD_OVERHEAD_BYTES = 2;
+
+ /**
+ * Byte size of 16 bit service uuid.
+ */
+ private static final int SHORT_UUID_BYTES = 2;
+ /**
+ * Byte size of 128 bit service uuid.
+ */
+ private static final int FULL_UUID_BYTES = 16;
/**
* Search queue to serialize remote onbject inspection.
@@ -75,6 +99,15 @@ public class GattService extends ProfileService {
* Server handle map.
*/
HandleMap mHandleMap = new HandleMap();
+ private List<UUID> mAdvertisingServiceUuids = new ArrayList<UUID>();
+
+ private int mAdvertisingClientIf = 0;
+
+ private byte[] mServiceData = new byte[0];
+ private int mManufacturerCode = -1;
+ private byte[] mManufacturerData = new byte[0];
+ private Integer mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
+ private final Object mLock = new Object();
/**
* Pending service declaration queue
@@ -112,7 +145,7 @@ public class GattService extends ProfileService {
}
/**
- * List of clients intereste in scan results.
+ * List of clients interested in scan results.
*/
private List<ScanClient> mScanQueue = new ArrayList<ScanClient>();
@@ -127,7 +160,7 @@ public class GattService extends ProfileService {
private void removeScanClient(int appIf, boolean isServer) {
for(ScanClient client : mScanQueue) {
- if (client.appIf == appIf && client.isServer == isServer) {
+ if (client.appIf == appIf && client.isServer == isServer) {
mScanQueue.remove(client);
break;
}
@@ -197,7 +230,11 @@ public class GattService extends ProfileService {
public void binderDied() {
if (DBG) Log.d(TAG, "Binder is dead - unregistering client (" + mAppIf + ")!");
- stopScan(mAppIf, false);
+ if (mAdvertisingClientIf == mAppIf) {
+ stopAdvertising(true); // force stop advertising.
+ } else {
+ stopScan(mAppIf, false);
+ }
unregisterClient(mAppIf);
}
}
@@ -288,12 +325,6 @@ public class GattService extends ProfileService {
service.clientDisconnect(clientIf, address);
}
- public void clientListen(int clientIf, boolean start) {
- GattService service = getService();
- if (service == null) return;
- service.clientListen(clientIf, start);
- }
-
public void refreshDevice(int clientIf, String address) {
GattService service = getService();
if (service == null) return;
@@ -412,11 +443,11 @@ public class GattService extends ProfileService {
public void beginServiceDeclaration(int serverIf, int srvcType,
int srvcInstanceId, int minHandles,
- ParcelUuid srvcId) {
+ ParcelUuid srvcId, boolean advertisePreferred) {
GattService service = getService();
if (service == null) return;
service.beginServiceDeclaration(serverIf, srvcType, srvcInstanceId,
- minHandles, srvcId.getUuid());
+ minHandles, srvcId.getUuid(), advertisePreferred);
}
public void addIncludedService(int serverIf, int srvcType,
@@ -479,13 +510,68 @@ public class GattService extends ProfileService {
srvcId.getUuid(), charInstanceId, charId.getUuid(), confirm, value);
}
- public void setAdvData(int serverIf, boolean setScanRsp, boolean inclName,
- boolean inclTxPower, int minInterval, int maxInterval,
- int appearance, byte[] manufacturerData) {
+ @Override
+ public void startAdvertising(int appIf) throws RemoteException {
+ GattService service = getService();
+ if (service == null) return;
+ service.startAdvertising(appIf);
+ }
+
+ @Override
+ public boolean isAdvertising() {
+ GattService service = getService();
+ if (service == null) return false;
+ return service.isAdvertising();
+ }
+
+ @Override
+ public void stopAdvertising() throws RemoteException {
GattService service = getService();
if (service == null) return;
- service.setAdvData(serverIf, setScanRsp, inclName, inclTxPower,
- minInterval, maxInterval, appearance, manufacturerData);
+ service.stopAdvertising();
+ }
+
+ @Override
+ public boolean setAdvServiceData(byte[] serviceData) throws RemoteException {
+ GattService service = getService();
+ if (service == null) return false;
+ return service.setAdvServiceData(serviceData);
+ }
+
+ @Override
+ public byte[] getAdvServiceData() throws RemoteException {
+ GattService service = getService();
+ if (service == null) return null;
+ return service.getAdvServiceData();
+ }
+
+ @Override
+ public boolean setAdvManufacturerCodeAndData(int manufactureCode, byte[] manufacturerData)
+ throws RemoteException {
+ GattService service = getService();
+ if (service == null) return false;
+ return service.setAdvManufacturerCodeAndData(manufactureCode, manufacturerData);
+ }
+
+ @Override
+ public byte[] getAdvManufacturerData() throws RemoteException {
+ GattService service = getService();
+ if (service == null) return null;
+ return service.getAdvManufacturerData();
+ }
+
+ @Override
+ public List<ParcelUuid> getAdvServiceUuids() throws RemoteException {
+ GattService service = getService();
+ if (service == null) return null;
+ return service.getAdvServiceUuids();
+ }
+
+ @Override
+ public void removeAdvManufacturerCodeAndData(int manufacturerCode) throws RemoteException {
+ GattService service = getService();
+ if (service == null) return;
+ service.removeAdvManufacturerCodeAndData(manufacturerCode);
}
};
@@ -836,14 +922,52 @@ public class GattService extends ProfileService {
}
}
- void onClientListen(int status, int clientIf)
- throws RemoteException {
+ void onAdvertiseCallback(int status, int clientIf) throws RemoteException {
if (DBG) Log.d(TAG, "onClientListen() status=" + status);
+ synchronized (mLock) {
+ if (DBG) Log.d(TAG, "state" + mAdvertisingState);
+ // Invalid advertising state
+ if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STARTED ||
+ mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STOPPED) {
+ Log.e(TAG, "invalid callback state " + mAdvertisingState);
+ return;
+ }
- ClientMap.App app = mClientMap.getById(clientIf);
- if (app == null) return;
+ // Force stop advertising, no callback.
+ if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_FORCE_STOPPING) {
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
+ mAdvertisingClientIf = 0;
+ sendBroadcast(new Intent(
+ BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STOPPED));
+ return;
+ }
- app.callback.onListen(status);
+ if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STARTING) {
+ if (status == 0) {
+ mAdvertisingClientIf = clientIf;
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STARTED;
+ sendBroadcast(new Intent(
+ BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STARTED));
+ } else {
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
+ }
+ } else if (mAdvertisingState == BluetoothAdapter.STATE_ADVERTISE_STOPPING) {
+ if (status == 0) {
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPED;
+ sendBroadcast(new Intent(
+ BluetoothAdapter.ACTION_BLUETOOTH_ADVERTISING_STOPPED));
+ mAdvertisingClientIf = 0;
+ } else {
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STARTED;
+ }
+ }
+ }
+ ClientMap.App app = mClientMap.getById(clientIf);
+ if (app == null || app.callback == null) {
+ Log.e(TAG, "app or callback is null");
+ return;
+ }
+ app.callback.onAdvertiseStateChange(mAdvertisingState, status);
}
/**************************************************************************
@@ -987,9 +1111,137 @@ public class GattService extends ProfileService {
gattClientDisconnectNative(clientIf, address, connId != null ? connId : 0);
}
- void clientListen(int clientIf, boolean start) {
- if (DBG) Log.d(TAG, "clientListen() - start=" + start);
- gattClientListenNative(clientIf, start);
+ synchronized boolean setAdvServiceData(byte[] serviceData) {
+ enforcePrivilegedPermission();
+ if (serviceData == null) return false;
+ // Calculate how many more bytes are needed for advertising service data field.
+ int extraBytes = (mServiceData == null) ?
+ FIELD_OVERHEAD_BYTES + serviceData.length :
+ serviceData.length - mServiceData.length;
+ if (getAvailableSize() < extraBytes) {
+ Log.e(TAG, "cannot set service data, available size " + getAvailableSize());
+ return false;
+ }
+ mServiceData = serviceData;
+ return true;
+ }
+
+ byte[] getAdvServiceData() {
+ enforcePrivilegedPermission();
+ return mServiceData;
+ }
+
+ synchronized boolean setAdvManufacturerCodeAndData(
+ int manufacturerCode, byte[] manufacturerData) {
+ enforcePrivilegedPermission();
+ if (manufacturerCode <= 0 || manufacturerData == null) {
+ return false;
+ }
+ if (mManufacturerCode > 0 && mManufacturerData != null) {
+ Log.e(TAG, "manufacture data is already set");
+ return false;
+ }
+ if (getAvailableSize() <
+ FIELD_OVERHEAD_BYTES + manufacturerData.length) {
+ Log.e(TAG, "cannot set manu data, available size " + getAvailableSize());
+ return false;
+ }
+ this.mManufacturerCode = manufacturerCode;
+ this.mManufacturerData = manufacturerData;
+ return true;
+ }
+
+ void removeAdvManufacturerCodeAndData(int manufacturerCode) {
+ enforcePrivilegedPermission();
+ if (mManufacturerCode != manufacturerCode) {
+ return;
+ }
+ mManufacturerCode = -1;
+ mManufacturerData = new byte[0];
+ }
+
+ byte[] getAdvManufacturerData() {
+ enforcePrivilegedPermission();
+ return mManufacturerData;
+ }
+
+ synchronized List<ParcelUuid> getAdvServiceUuids() {
+ enforcePrivilegedPermission();;
+ boolean fullUuidFound = false;
+ List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
+ for (HandleMap.Entry entry : mHandleMap.mEntries) {
+ if (entry.advertisePreferred) {
+ ParcelUuid parcelUuid = new ParcelUuid(entry.uuid);
+ if (BluetoothUuid.isShortUuid(parcelUuid)) {
+ serviceUuids.add(parcelUuid);
+ } else {
+ // Allow at most one 128 bit service uuid to be advertised.
+ if (!fullUuidFound) {
+ fullUuidFound = true;
+ serviceUuids.add(parcelUuid);
+ }
+ }
+ }
+ }
+ return serviceUuids;
+ }
+
+ boolean isAdvertising() {
+ enforcePrivilegedPermission();
+ return mAdvertisingState != BluetoothAdapter.STATE_ADVERTISE_STOPPED;
+ }
+
+ void startAdvertising(int clientIf) {
+ enforcePrivilegedPermission();
+ if (DBG) Log.d(TAG, "start advertising for app - " + clientIf);
+ List<ParcelUuid> serviceUuids = getAdvServiceUuids();
+ int advertisingServiceUuidLength = serviceUuids == null ? 0 : serviceUuids.size();
+
+ // Note according to Bluetooth Spec Version 4.0, for advertising and scan response data
+ // "all numerical multi-byte entities and values shall use little-endian byte order".
+ ByteBuffer advertisingUuidBytes = ByteBuffer.allocate(advertisingServiceUuidLength * 16)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ for (ParcelUuid parcelUuid : serviceUuids) {
+ UUID uuid = parcelUuid.getUuid();
+ // Least signifcant bits first as the advertising uuid should be in little-endian.
+ advertisingUuidBytes.putLong(uuid.getLeastSignificantBits())
+ .putLong(uuid.getMostSignificantBits());
+ }
+
+ // Set advertising data.
+ gattSetAdvDataNative(clientIf,
+ false, // not scan response data
+ false, // no device name
+ false, // no tx power included
+ DEFAULT_SCAN_INTERVAL_MILLIS,
+ DEFAULT_SCAN_INTERVAL_MILLIS,
+ 0, // no appearance limit
+ mManufacturerData,
+ mServiceData,
+ advertisingUuidBytes.array());
+
+ // Start advertising if advertising is not already started.
+ if (!isAdvertising()) {
+ gattAdvertiseNative(clientIf, true);
+ mAdvertisingClientIf = clientIf;
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STARTING;
+ }
+ }
+
+ void stopAdvertising() {
+ stopAdvertising(false);
+ }
+
+ void stopAdvertising(boolean forceStop) {
+ enforcePrivilegedPermission();
+ gattAdvertiseNative(mAdvertisingClientIf, false);
+ synchronized (mLock) {
+ if (forceStop) {
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_FORCE_STOPPING;
+ } else {
+ mAdvertisingState = BluetoothAdapter.STATE_ADVERTISE_STOPPING;
+ }
+ }
}
List<String> getConnectedDevices() {
@@ -1151,15 +1403,6 @@ public class GattService extends ProfileService {
gattClientReadRemoteRssiNative(clientIf, address);
}
- void setAdvData(int serverIf, boolean setScanRsp, boolean inclName,
- boolean inclTxPower, int minInterval, int maxInterval,
- int appearance, byte[] manufacturerData) {
- if (DBG) Log.d(TAG, "setAdvData() - setScanRsp=" + setScanRsp);
- if (minInterval == 0) maxInterval = 0;
- gattSetAdvDataNative(serverIf, setScanRsp, inclName, inclTxPower,
- minInterval, maxInterval, appearance, manufacturerData);
- }
-
/**************************************************************************
* Callback functions - SERVER
*************************************************************************/
@@ -1183,8 +1426,11 @@ public class GattService extends ProfileService {
UUID uuid = new UUID(srvcUuidMsb, srvcUuidLsb);
if (DBG) Log.d(TAG, "onServiceAdded() UUID=" + uuid + ", status=" + status
+ ", handle=" + srvcHandle);
- if (status == 0)
- mHandleMap.addService(serverIf, srvcHandle, uuid, srvcType, srvcInstId);
+ if (status == 0) {
+ mHandleMap.addService(serverIf, srvcHandle, uuid, srvcType, srvcInstId,
+ mAdvertisingServiceUuids.remove(uuid));
+ }
+
continueServiceDeclaration(serverIf, status, srvcHandle);
}
@@ -1415,12 +1661,13 @@ public class GattService extends ProfileService {
}
void beginServiceDeclaration(int serverIf, int srvcType, int srvcInstanceId,
- int minHandles, UUID srvcUuid) {
+ int minHandles, UUID srvcUuid, boolean advertisePreferred) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (DBG) Log.d(TAG, "beginServiceDeclaration() - uuid=" + srvcUuid);
ServiceDeclaration serviceDeclaration = addDeclaration();
- serviceDeclaration.addService(srvcUuid, srvcType, srvcInstanceId, minHandles);
+ serviceDeclaration.addService(srvcUuid, srvcType, srvcInstanceId, minHandles,
+ advertisePreferred);
}
void addIncludedService(int serverIf, int srvcType, int srvcInstanceId,
@@ -1529,6 +1776,33 @@ public class GattService extends ProfileService {
return type;
}
+ private synchronized int getAvailableSize() {
+ enforcePrivilegedPermission();
+ int availableSize = ADVERTISING_PACKET_MAX_BYTES - ADVERTISING_FLAGS_BYTES;
+
+ for (ParcelUuid parcelUuid : getAdvServiceUuids()) {
+ if (BluetoothUuid.isShortUuid(parcelUuid)) {
+ availableSize -= FIELD_OVERHEAD_BYTES + SHORT_UUID_BYTES;
+ } else {
+ availableSize -= FIELD_OVERHEAD_BYTES + FULL_UUID_BYTES;
+ }
+ }
+ if (mManufacturerCode > 0 && mManufacturerData != null) {
+ availableSize -= (FIELD_OVERHEAD_BYTES + mManufacturerData.length);
+ }
+ if (mServiceData != null) {
+ availableSize -= (FIELD_OVERHEAD_BYTES + mServiceData.length);
+ }
+ return availableSize;
+ }
+
+ // Enforce caller has BLUETOOTH_PRIVILEGED permission. A {@link SecurityException} will be
+ // thrown if the caller app does not have BLUETOOTH_PRIVILEGED permission.
+ private void enforcePrivilegedPermission() {
+ enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+ "Need BLUETOOTH_PRIVILEGED permission");
+ }
+
private void continueSearch(int connId, int status) throws RemoteException {
Log.d(TAG, "continueSearch() - connid=" + connId + ", status=" + status);
@@ -1577,6 +1851,9 @@ public class GattService extends ProfileService {
+ entry.type);
switch(entry.type) {
case ServiceDeclaration.TYPE_SERVICE:
+ if (entry.advertisePreferred) {
+ mAdvertisingServiceUuids.add(entry.uuid);
+ }
gattServerAddServiceNative(serverIf, entry.serviceType,
entry.instance,
entry.uuid.getLeastSignificantBits(),
@@ -1621,6 +1898,7 @@ public class GattService extends ProfileService {
ServerMap.App app = mServerMap.getById(serverIf);
if (app != null) {
HandleMap.Entry serviceEntry = mHandleMap.getByHandle(srvcHandle);
+
if (serviceEntry != null) {
app.callback.onServiceAdded(status, serviceEntry.serviceType,
serviceEntry.instance, new ParcelUuid(serviceEntry.uuid));
@@ -1801,11 +2079,11 @@ public class GattService extends ProfileService {
private native void gattClientReadRemoteRssiNative(int clientIf,
String address);
- private native void gattClientListenNative(int client_if, boolean start);
+ private native void gattAdvertiseNative(int client_if, boolean start);
private native void gattSetAdvDataNative(int serverIf, boolean setScanRsp, boolean inclName,
boolean inclTxPower, int minInterval, int maxInterval,
- int appearance, byte[] manufacturerData);
+ int appearance, byte[] manufacturerData, byte[] serviceData, byte[] serviceUuid);
private native void gattServerRegisterAppNative(long app_uuid_lsb,
long app_uuid_msb);
diff --git a/src/com/android/bluetooth/gatt/HandleMap.java b/src/com/android/bluetooth/gatt/HandleMap.java
index 5d45654b9..187625a43 100644
--- a/src/com/android/bluetooth/gatt/HandleMap.java
+++ b/src/com/android/bluetooth/gatt/HandleMap.java
@@ -42,6 +42,7 @@ class HandleMap {
int serviceHandle = 0;
int charHandle = 0;
boolean started = false;
+ boolean advertisePreferred = false;
Entry(int serverIf, int handle, UUID uuid, int serviceType, int instance) {
this.serverIf = serverIf;
@@ -52,6 +53,17 @@ class HandleMap {
this.serviceType = serviceType;
}
+ Entry(int serverIf, int handle, UUID uuid, int serviceType, int instance,
+ boolean advertisePreferred) {
+ this.serverIf = serverIf;
+ this.type = TYPE_SERVICE;
+ this.handle = handle;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.serviceType = serviceType;
+ this.advertisePreferred = advertisePreferred;
+ }
+
Entry(int serverIf, int type, int handle, UUID uuid, int serviceHandle) {
this.serverIf = serverIf;
this.type = type;
@@ -86,8 +98,9 @@ class HandleMap {
mRequestMap.clear();
}
- void addService(int serverIf, int handle, UUID uuid, int serviceType, int instance) {
- mEntries.add(new Entry(serverIf, handle, uuid, serviceType, instance));
+ void addService(int serverIf, int handle, UUID uuid, int serviceType, int instance,
+ boolean advertisePreferred) {
+ mEntries.add(new Entry(serverIf, handle, uuid, serviceType, instance, advertisePreferred));
}
void addCharacteristic(int serverIf, int handle, UUID uuid, int serviceHandle) {
diff --git a/src/com/android/bluetooth/gatt/ServiceDeclaration.java b/src/com/android/bluetooth/gatt/ServiceDeclaration.java
index 0c9a51b2f..1d0bc4e90 100644
--- a/src/com/android/bluetooth/gatt/ServiceDeclaration.java
+++ b/src/com/android/bluetooth/gatt/ServiceDeclaration.java
@@ -39,6 +39,7 @@ class ServiceDeclaration {
int properties = 0;
int serviceType = 0;
int serviceHandle = 0;
+ boolean advertisePreferred = false;
Entry(UUID uuid, int serviceType, int instance) {
this.type = TYPE_SERVICE;
@@ -47,6 +48,14 @@ class ServiceDeclaration {
this.serviceType = serviceType;
}
+ Entry(UUID uuid, int serviceType, int instance, boolean advertisePreferred) {
+ this.type = TYPE_SERVICE;
+ this.uuid = uuid;
+ this.instance = instance;
+ this.serviceType = serviceType;
+ this.advertisePreferred = advertisePreferred;
+ }
+
Entry(UUID uuid, int properties, int permissions, int instance) {
this.type = TYPE_CHARACTERISTIC;
this.uuid = uuid;
@@ -69,8 +78,9 @@ class ServiceDeclaration {
mEntries = new ArrayList<Entry>();
}
- void addService(UUID uuid, int serviceType, int instance, int minHandles) {
- mEntries.add(new Entry(uuid, serviceType, instance));
+ void addService(UUID uuid, int serviceType, int instance, int minHandles,
+ boolean advertisePreferred) {
+ mEntries.add(new Entry(uuid, serviceType, instance, advertisePreferred));
if (minHandles == 0) {
++mNumHandles;
} else {
@@ -102,6 +112,15 @@ class ServiceDeclaration {
return entry;
}
+ boolean isServiceAdvertisePreferred(UUID uuid) {
+ for (Entry entry : mEntries) {
+ if (entry.uuid.equals(uuid)) {
+ return entry.advertisePreferred;
+ }
+ }
+ return false;
+ }
+
int getNumHandles() {
return mNumHandles;
}
diff --git a/src/com/android/bluetooth/map/BluetoothMapContent.java b/src/com/android/bluetooth/map/BluetoothMapContent.java
index 19447980c..d30512af9 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContent.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContent.java
@@ -50,6 +50,7 @@ import com.android.emailcommon.provider.EmailContent.SyncColumns;
import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
import com.google.android.mms.pdu.CharacterSets;
+import com.google.android.mms.pdu.PduHeaders;
import android.database.sqlite.SQLiteException;
import java.util.List;
import java.util.ArrayList;
@@ -561,8 +562,15 @@ public class BluetoothMapContent {
private void setPriority(BluetoothMapMessageListingElement e, Cursor c,
FilterInfo fi, BluetoothMapAppParams ap) {
+ String priority = "no";
if ((ap.getParameterMask() & MASK_PRIORITY) != 0) {
- String priority = "no";
+ int pri = 0;
+ if (fi.msgType == FilterInfo.TYPE_MMS) {
+ pri = c.getInt(c.getColumnIndex(Mms.PRIORITY));
+ }
+ if (pri == PduHeaders.PRIORITY_HIGH) {
+ priority = "yes";
+ }
if (D) Log.d(TAG, "setPriority: " + priority);
e.setPriority(priority);
}
@@ -1499,6 +1507,23 @@ public class BluetoothMapContent {
return where;
}
+ private String setWhereFilterPriority(BluetoothMapAppParams ap, FilterInfo fi) {
+ String where = "";
+ int pri = ap.getFilterPriority();
+ /*only MMS have priority info */
+ if(fi.msgType == FilterInfo.TYPE_MMS)
+ {
+ if(pri == 0x0002)
+ {
+ where += " AND " + Mms.PRIORITY + "<=" +
+ Integer.toString(PduHeaders.PRIORITY_NORMAL);
+ }else if(pri == 0x0001) {
+ where += " AND " + Mms.PRIORITY + "=" +
+ Integer.toString(PduHeaders.PRIORITY_HIGH);
+ }
+ }
+ return where;
+ }
private String setWhereFilterRecipient(BluetoothMapAppParams ap,
FilterInfo fi) {
@@ -1565,6 +1590,7 @@ public class BluetoothMapContent {
where += setWhereFilterFolderType(folder, fi);
where += setWhereFilterReadStatus(ap, fi);
where += setWhereFilterPeriod(ap, fi);
+ where += setWhereFilterPriority(ap,fi);
/* where += setWhereFilterOriginator(ap, fi); */
/* where += setWhereFilterRecipient(ap, fi); */
@@ -2055,21 +2081,22 @@ public class BluetoothMapContent {
if (smsSelected(fi, ap)) {
fi.msgType = FilterInfo.TYPE_SMS;
+ if(ap.getFilterPriority() != 1){ /*SMS cannot have high priority*/
+ String where = setWhereFilter(folder, fi, ap);
- String where = setWhereFilter(folder, fi, ap);
-
- Cursor c = mResolver.query(Sms.CONTENT_URI,
- SMS_PROJECTION, where, null, "date DESC");
+ Cursor c = mResolver.query(Sms.CONTENT_URI,
+ SMS_PROJECTION, where, null, "date DESC");
- if (c != null) {
- while (c.moveToNext()) {
- if (matchAddresses(c, fi, ap)) {
- printSms(c);
- e = element(c, fi, ap);
- bmList.add(e);
+ if (c != null) {
+ while (c.moveToNext()) {
+ if (matchAddresses(c, fi, ap)) {
+ printSms(c);
+ e = element(c, fi, ap);
+ bmList.add(e);
+ }
}
+ c.close();
}
- c.close();
}
}
@@ -2156,7 +2183,7 @@ public class BluetoothMapContent {
where += " AND read=0 ";
where += setWhereFilterPeriod(ap, fi);
Cursor c = mResolver.query(Sms.CONTENT_URI,
- SMS_PROJECTION, where, null, "date DESC");
+ SMS_PROJECTION, where, null, "date DESC");
if (c != null) {
cnt = c.getCount();
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index a9fc19c6f..c022b22cc 100755
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -49,7 +49,7 @@
<application>
<uses-library android:name="android.test.runner" />
<uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
- <uses-permission android:permission="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" />
+ <uses-permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" />
<path-permission
android:path="/btopp"
android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />