Merge "Improve RestrictionEntry API" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 6035322..6f0575c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2210,6 +2210,7 @@
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
field public static final java.lang.String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator";
field public static final int ERROR_CODE_BAD_ARGUMENTS = 7; // 0x7
+ field public static final int ERROR_CODE_BAD_AUTHENTICATION = 9; // 0x9
field public static final int ERROR_CODE_BAD_REQUEST = 8; // 0x8
field public static final int ERROR_CODE_CANCELED = 4; // 0x4
field public static final int ERROR_CODE_INVALID_RESPONSE = 5; // 0x5
@@ -4613,8 +4614,13 @@
method public boolean isEnabled();
method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
+ method public boolean registerCallback(android.bluetooth.BluetoothAdapterCallback);
method public boolean setName(java.lang.String);
method public boolean startDiscovery();
+ method public boolean startLeScan();
+ method public boolean startLeScan(java.util.UUID[]);
+ method public void stopLeScan();
+ method public boolean unRegisterCallback(android.bluetooth.BluetoothAdapterCallback);
field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
@@ -4645,6 +4651,14 @@
field public static final int STATE_TURNING_ON = 11; // 0xb
}
+ public abstract class BluetoothAdapterCallback {
+ ctor public BluetoothAdapterCallback();
+ method public void onCallbackRegistration(int);
+ method public void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]);
+ field public static final int CALLBACK_REGISTERED = 0; // 0x0
+ field public static final int CALLBACK_REGISTRATION_FAILURE = 1; // 0x1
+ }
+
public class BluetoothAssignedNumbers {
field public static final int ACCEL_SEMICONDUCTOR = 74; // 0x4a
field public static final int ALCATEL = 36; // 0x24
@@ -4837,6 +4851,7 @@
}
public final class BluetoothDevice implements android.os.Parcelable {
+ method public android.bluetooth.BluetoothGatt connectGattServer(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public int describeContents();
@@ -4869,6 +4884,159 @@
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
}
+ public final class BluetoothGatt implements android.bluetooth.BluetoothProfile {
+ method public void abortReliableWrite(android.bluetooth.BluetoothDevice);
+ method public boolean beginReliableWrite();
+ method public void disconnect();
+ method public boolean discoverServices();
+ method public boolean executeReliableWrite();
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
+ method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
+ method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public boolean readRemoteRssi();
+ method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ field public static final int GATT_FAILURE = 0; // 0x0
+ field public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5; // 0x5
+ field public static final int GATT_INSUFFICIENT_ENCRYPTION = 15; // 0xf
+ field public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 13; // 0xd
+ field public static final int GATT_INVALID_OFFSET = 7; // 0x7
+ field public static final int GATT_READ_NOT_PERMITTED = 2; // 0x2
+ field public static final int GATT_REQUEST_NOT_SUPPORTED = 6; // 0x6
+ field public static final int GATT_SUCCESS = 0; // 0x0
+ field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
+ }
+
+ public abstract class BluetoothGattCallback {
+ ctor public BluetoothGattCallback();
+ method public void onCharacteristicChanged(android.bluetooth.BluetoothGattCharacteristic);
+ method public void onCharacteristicRead(android.bluetooth.BluetoothGattCharacteristic, int);
+ method public void onCharacteristicWrite(android.bluetooth.BluetoothGattCharacteristic, int);
+ method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
+ method public void onDescriptorRead(android.bluetooth.BluetoothGattDescriptor, int);
+ method public void onDescriptorWrite(android.bluetooth.BluetoothGattDescriptor, int);
+ method public void onReadRemoteRssi(android.bluetooth.BluetoothDevice, int, int);
+ method public void onReliableWriteCompleted(android.bluetooth.BluetoothDevice, int);
+ method public void onServicesDiscovered(android.bluetooth.BluetoothDevice, int);
+ }
+
+ public class BluetoothGattCharacteristic {
+ ctor public BluetoothGattCharacteristic(java.util.UUID, int, int);
+ method public boolean addDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
+ method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
+ method public java.lang.Float getFloatValue(int, int);
+ method public int getInstanceId();
+ method public java.lang.Integer getIntValue(int, int);
+ method public int getPermissions();
+ method public int getProperties();
+ method public android.bluetooth.BluetoothGattService getService();
+ method public java.lang.String getStringValue(int);
+ method public java.util.UUID getUuid();
+ method public byte[] getValue();
+ method public int getWriteType();
+ method public boolean setValue(byte[]);
+ method public boolean setValue(int, int, int);
+ method public boolean setValue(int, int, int, int);
+ method public boolean setValue(java.lang.String);
+ method public void setWriteType(int);
+ field public static final int FORMAT_FLOAT = 52; // 0x34
+ field public static final int FORMAT_SFLOAT = 50; // 0x32
+ field public static final int FORMAT_SINT16 = 34; // 0x22
+ field public static final int FORMAT_SINT32 = 36; // 0x24
+ field public static final int FORMAT_SINT8 = 33; // 0x21
+ field public static final int FORMAT_UINT16 = 18; // 0x12
+ field public static final int FORMAT_UINT32 = 20; // 0x14
+ field public static final int FORMAT_UINT8 = 17; // 0x11
+ field public static final int PERMISSION_READ = 1; // 0x1
+ field public static final int PERMISSION_READ_ENCRYPTED = 2; // 0x2
+ field public static final int PERMISSION_READ_ENCRYPTED_MITM = 4; // 0x4
+ field public static final int PERMISSION_WRITE = 16; // 0x10
+ field public static final int PERMISSION_WRITE_ENCRYPTED = 32; // 0x20
+ field public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 64; // 0x40
+ field public static final int PERMISSION_WRITE_SIGNED = 128; // 0x80
+ field public static final int PERMISSION_WRITE_SIGNED_MITM = 256; // 0x100
+ field public static final int PROPERTY_BROADCAST = 1; // 0x1
+ field public static final int PROPERTY_EXTENDED_PROPS = 128; // 0x80
+ field public static final int PROPERTY_INDICATE = 32; // 0x20
+ field public static final int PROPERTY_NOTIFY = 16; // 0x10
+ field public static final int PROPERTY_READ = 2; // 0x2
+ field public static final int PROPERTY_SIGNED_WRITE = 64; // 0x40
+ field public static final int PROPERTY_WRITE = 8; // 0x8
+ field public static final int PROPERTY_WRITE_NO_RESPONSE = 4; // 0x4
+ field public static final int WRITE_TYPE_DEFAULT = 2; // 0x2
+ field public static final int WRITE_TYPE_NO_RESPONSE = 1; // 0x1
+ field public static final int WRITE_TYPE_SIGNED = 4; // 0x4
+ field protected java.util.List mDescriptors;
+ }
+
+ public class BluetoothGattDescriptor {
+ ctor public BluetoothGattDescriptor(java.util.UUID, int);
+ method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
+ method public int getPermissions();
+ method public java.util.UUID getUuid();
+ method public byte[] getValue();
+ method public boolean setValue(byte[]);
+ field public static final byte[] DISABLE_NOTIFICATION_VALUE;
+ field public static final byte[] ENABLE_INDICATION_VALUE;
+ field public static final byte[] ENABLE_NOTIFICATION_VALUE;
+ field public static final int PERMISSION_READ = 1; // 0x1
+ field public static final int PERMISSION_READ_ENCRYPTED = 2; // 0x2
+ field public static final int PERMISSION_READ_ENCRYPTED_MITM = 4; // 0x4
+ field public static final int PERMISSION_WRITE = 16; // 0x10
+ field public static final int PERMISSION_WRITE_ENCRYPTED = 32; // 0x20
+ field public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 64; // 0x40
+ field public static final int PERMISSION_WRITE_SIGNED = 128; // 0x80
+ field public static final int PERMISSION_WRITE_SIGNED_MITM = 256; // 0x100
+ }
+
+ public final class BluetoothGattServer implements android.bluetooth.BluetoothProfile {
+ method public boolean addService(android.bluetooth.BluetoothGattService);
+ method public void cancelConnection(android.bluetooth.BluetoothDevice);
+ method public void clearServices();
+ method public boolean connect(android.bluetooth.BluetoothDevice, boolean);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
+ method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
+ method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public boolean removeService(android.bluetooth.BluetoothGattService);
+ method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ }
+
+ public abstract class BluetoothGattServerCallback {
+ ctor public BluetoothGattServerCallback();
+ method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
+ method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
+ method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
+ method public void onDescriptorReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattDescriptor);
+ method public void onDescriptorWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattDescriptor, boolean, boolean, int, byte[]);
+ method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
+ method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
+ }
+
+ public class BluetoothGattService {
+ ctor public BluetoothGattService(java.util.UUID, int);
+ method public boolean addCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method public boolean addService(android.bluetooth.BluetoothGattService);
+ method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(java.util.UUID);
+ method public java.util.List<android.bluetooth.BluetoothGattCharacteristic> getCharacteristics();
+ method public java.util.List<android.bluetooth.BluetoothGattService> getIncludedServices();
+ method public int getInstanceId();
+ method public int getType();
+ method public java.util.UUID getUuid();
+ field public static final int SERVICE_TYPE_PRIMARY = 0; // 0x0
+ field public static final int SERVICE_TYPE_SECONDARY = 1; // 0x1
+ field protected java.util.List mCharacteristics;
+ field protected java.util.List mIncludedServices;
+ }
+
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -4931,6 +5099,14 @@
method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
+ public final class BluetoothManager {
+ method public android.bluetooth.BluetoothAdapter getAdapter();
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
+ method public int getConnectionState(android.bluetooth.BluetoothDevice, int);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]);
+ method public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback);
+ }
+
public abstract interface BluetoothProfile {
method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public abstract int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -4938,6 +5114,8 @@
field public static final int A2DP = 2; // 0x2
field public static final java.lang.String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE";
field public static final java.lang.String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+ field public static final int GATT = 7; // 0x7
+ field public static final int GATT_SERVER = 8; // 0x8
field public static final int HEADSET = 1; // 0x1
field public static final int HEALTH = 3; // 0x3
field public static final int STATE_CONNECTED = 2; // 0x2
@@ -5495,6 +5673,7 @@
field public static final int BIND_IMPORTANT = 64; // 0x40
field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
+ field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 313260f..bdc882a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -151,6 +151,7 @@
public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
public static final int ERROR_CODE_BAD_REQUEST = 8;
+ public static final int ERROR_CODE_BAD_AUTHENTICATION = 9;
/** @hide */
public static final int ERROR_CODE_USER_RESTRICTED = 100;
@@ -964,10 +965,10 @@
*/
@Deprecated
public AccountManagerFuture<Bundle> getAuthToken(
- final Account account, final String authTokenType,
+ final Account account, final String authTokenType,
final boolean notifyAuthFailure,
AccountManagerCallback<Bundle> callback, Handler handler) {
- return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback,
+ return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback,
handler);
}
@@ -1913,7 +1914,7 @@
* <p>
* The most common case is to call this with one account type, e.g.:
* <p>
- * <pre> newChooseAccountsIntent(null, null, new String[]{"com.google"}, false, null,
+ * <pre> newChooseAccountIntent(null, null, new String[]{"com.google"}, false, null,
* null, null, null);</pre>
* @param selectedAccount if specified, indicates that the {@link Account} is the currently
* selected one, according to the caller's definition of selected.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 734d435..459e49c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -19,7 +19,7 @@
import com.android.internal.policy.PolicyManager;
import com.android.internal.util.Preconditions;
-import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -319,7 +319,7 @@
registerService(BLUETOOTH_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- return BluetoothAdapter.getDefaultAdapter();
+ return new BluetoothManager(ctx);
}});
registerService(CLIPBOARD_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 02cf3aa..4fbca73 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -666,7 +666,9 @@
/**
* Print the Service's state into the given stream. This gets invoked if
- * you run "adb shell dumpsys activity service <yourservicename>".
+ * you run "adb shell dumpsys activity service <yourservicename>"
+ * (note that for this command to work, the service must be running, and
+ * you must specify a fully-qualified service name).
* This is distinct from "dumpsys <servicename>", which only works for
* named system services and which invokes the {@link IBinder#dump} method
* on the {@link IBinder} interface registered with ServiceManager.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b00bf09..2e9c9e3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -22,7 +22,6 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -359,6 +358,8 @@
private IBluetooth mService;
private Handler mServiceRecordHandler;
+ private BluetoothAdapterCallback mCallback;
+ private int mClientIf;
/**
* Get a handle to the default local Bluetooth adapter.
@@ -1137,7 +1138,8 @@
* Get the profile proxy object associated with the profile.
*
* <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
- * or {@link BluetoothProfile#A2DP}. Clients must implement
+ * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or
+ * {@link BluetoothProfile#GATT_SERVER}. Clients must implement
* {@link BluetoothProfile.ServiceListener} to get notified of
* the connection status and to get the proxy object.
*
@@ -1166,12 +1168,6 @@
} else if (profile == BluetoothProfile.HEALTH) {
BluetoothHealth health = new BluetoothHealth(context, listener);
return true;
- } else if (profile == BluetoothProfile.GATT) {
- BluetoothGatt gatt = new BluetoothGatt(context, listener);
- return true;
- } else if (profile == BluetoothProfile.GATT_SERVER) {
- BluetoothGattServer gattServer = new BluetoothGattServer(context, listener);
- return true;
} else {
return false;
}
@@ -1411,4 +1407,230 @@
mProxyServiceStateCallbacks.remove(cb);
}
}
+
+ /**
+ * Register an callback to receive async results, such as LE scan result.
+ *
+ * <p>This is an asynchronous call. The callback
+ * {@link BluetoothAdapterCallback#onCallbackRegistration}
+ * is used to notify success or failure if the function returns true.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks.
+ * @return If true, the callback will be called to notify success or failure,
+ * false on immediate error
+ */
+ public boolean registerCallback(BluetoothAdapterCallback callback) {
+ try {
+ IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+ mCallback = callback;
+ UUID uuid = UUID.randomUUID();
+ if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
+
+ iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+ }
+
+ /**
+ * Unregister the registered callback.
+ */
+ public boolean unRegisterCallback(BluetoothAdapterCallback callback) {
+ if (callback != mCallback) return false;
+ try {
+ IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+
+ iGatt.unregisterClient(mClientIf);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+ }
+
+ /**
+ * Starts a scan for Bluetooth LE devices.
+ *
+ * <p>Results of the scan are reported using the
+ * {@link BluetoothAdapterCallback#onLeScan} callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @return true, if the scan was started successfully
+ */
+ public boolean startLeScan() {
+ if (DBG) Log.d(TAG, "startLeScan()");
+ if (mClientIf == 0) return false;
+
+ try {
+ IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+ iGatt.startScan(mClientIf, false);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Starts a scan for Bluetooth LE devices, looking for devices that
+ * advertise given services.
+ *
+ * <p>Devices which advertise all specified services are reported using the
+ * {@link BluetoothAdapterCallback#onLeScan} callback.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param serviceUuids Array of services to look for
+ * @return true, if the scan was started successfully
+ */
+ public boolean startLeScan(UUID[] serviceUuids) {
+ if (DBG) Log.d(TAG, "startLeScan() - with UUIDs");
+ if (mClientIf == 0) return false;
+
+ try {
+ IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+ ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
+ for(int i = 0; i != uuids.length; ++i) {
+ uuids[i] = new ParcelUuid(serviceUuids[i]);
+ }
+ iGatt.startScanWithUuids(mClientIf, false, uuids);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Stops an ongoing Bluetooth LE device scan.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ */
+ public void stopLeScan() {
+ if (DBG) Log.d(TAG, "stopScan()");
+ if (mClientIf == 0) return;
+
+ try {
+ IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+ iGatt.stopScan(mClientIf, false);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Bluetooth GATT interface callbacks
+ */
+ private final IBluetoothGattCallback mBluetoothGattCallback =
+ new IBluetoothGattCallback.Stub() {
+ /**
+ * Application interface registered - app is ready to go
+ */
+ public void onClientRegistered(int status, int clientIf) {
+ if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
+ + " clientIf=" + clientIf);
+ mClientIf = clientIf;
+ mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ?
+ BluetoothAdapterCallback.CALLBACK_REGISTERED :
+ BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE);
+ }
+
+ public void onClientConnectionState(int status, int clientIf,
+ boolean connected, String address) {
+ // no op
+ }
+
+ /**
+ * Callback reporting an LE scan result.
+ * @hide
+ */
+ public void onScanResult(String address, int rssi, byte[] advData) {
+ if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+
+ try {
+ mCallback.onLeScan(getRemoteDevice(address), rssi, advData);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ public void onGetService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid) {
+ // no op
+ }
+
+ public void onGetIncludedService(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int inclSrvcType, int inclSrvcInstId,
+ ParcelUuid inclSrvcUuid) {
+ // no op
+ }
+
+ public void onGetCharacteristic(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ int charProps) {
+ // no op
+ }
+
+ public void onGetDescriptor(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descUuid) {
+ // no op
+ }
+
+ public void onSearchComplete(String address, int status) {
+ // no op
+ }
+
+ public void onCharacteristicRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid, byte[] value) {
+ // no op
+ }
+
+ public void onCharacteristicWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid) {
+ // no op
+ }
+
+ public void onNotify(String address, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ byte[] value) {
+ // no op
+ }
+
+ public void onDescriptorRead(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid, byte[] value) {
+ // no op
+ }
+
+ public void onDescriptorWrite(String address, int status, int srvcType,
+ int srvcInstId, ParcelUuid srvcUuid,
+ int charInstId, ParcelUuid charUuid,
+ ParcelUuid descrUuid) {
+ // no op
+ }
+
+ public void onExecuteWrite(String address, int status) {
+ // no op
+ }
+
+ public void onReadRemoteRssi(String address, int rssi, int status) {
+ // no op
+ }
+ };
+
}
diff --git a/core/java/android/bluetooth/BluetoothAdapterCallback.java b/core/java/android/bluetooth/BluetoothAdapterCallback.java
new file mode 100644
index 0000000..a726bc9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAdapterCallback.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 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.bluetooth.BluetoothDevice;
+
+/**
+ * This abstract class is used to implement {@link BluetoothAdapter} callbacks.
+ */
+public abstract class BluetoothAdapterCallback {
+
+ /**
+ * Indicates the callback has been registered successfully
+ */
+ public static final int CALLBACK_REGISTERED = 0;
+
+ /**
+ * Indicates the callback registration has failed
+ */
+ public static final int CALLBACK_REGISTRATION_FAILURE = 1;
+
+ /**
+ * Callback to inform change in registration state of the application.
+ *
+ * @param status Returns {@link #CALLBACK_REGISTERED} if the application
+ * was successfully registered.
+ */
+ public void onCallbackRegistration(int status) {
+ }
+
+ /**
+ * Callback reporting an LE device found during a device scan initiated
+ * by the {@link BluetoothAdapter#startLeScan} function.
+ *
+ * @param device Identifies the remote device
+ * @param rssi The RSSI value for the remote device as reported by the
+ * Bluetooth hardware. 0 if no RSSI value is available.
+ * @param scanRecord The content of the advertisement record offered by
+ * the remote device.
+ */
+ public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 4cc22b4..83e95ca 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1126,4 +1127,30 @@
return pinBytes;
}
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGattServer(Context context, boolean autoConnect,
+ BluetoothGattCallback callback) {
+ // TODO(Bluetooth) check whether platform support BLE
+ // Do the check here or in GattServer?
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetoothManager managerService = adapter.getBluetoothManager();
+ try {
+ IBluetoothGatt iGatt = managerService.getBluetoothGatt();
+ BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this);
+ gatt.connect(autoConnect, callback);
+ return gatt;
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 1e12025..f9ce6ea 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,8 +16,6 @@
package android.bluetooth;
-
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
@@ -39,42 +37,48 @@
import java.util.UUID;
/**
- * Public API for the Bluetooth Gatt Profile.
+ * Public API for the Bluetooth GATT Profile.
*
- * <p>This class provides Bluetooth Gatt functionality to enable communication
+ * <p>This class provides Bluetooth GATT functionality to enable communication
* with Bluetooth Smart or Smart Ready devices.
*
- * <p>BluetoothGatt is a proxy object for controlling the Bluetooth Service
- * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothGatt proxy object.
- *
* <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
- * and call {@link #registerApp} to register your application. Gatt capable
- * devices can be discovered using the {@link #startScan} function or the
- * regular Bluetooth device discovery process.
- * @hide
+ * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class.
+ * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
+ * scan process.
*/
public final class BluetoothGatt implements BluetoothProfile {
private static final String TAG = "BluetoothGatt";
private static final boolean DBG = true;
+ private static final boolean VDBG = true;
- private Context mContext;
- private ServiceListener mServiceListener;
- private BluetoothAdapter mAdapter;
+ private final Context mContext;
private IBluetoothGatt mService;
private BluetoothGattCallback mCallback;
private int mClientIf;
private boolean mAuthRetry = false;
+ private BluetoothDevice mDevice;
+ private boolean mAutoConnect;
+ private int mConnState;
+ private final Object mStateLock = new Object();
+
+ private static final int CONN_STATE_IDLE = 0;
+ private static final int CONN_STATE_CONNECTING = 1;
+ private static final int CONN_STATE_CONNECTED = 2;
+ private static final int CONN_STATE_DISCONNECTING = 3;
private List<BluetoothGattService> mServices;
- /** A Gatt operation completed successfully */
+ /** A GATT operation failed */
+ public static final int GATT_FAILURE = 0;
+
+ /** A GATT operation completed successfully */
public static final int GATT_SUCCESS = 0;
- /** Gatt read operation is not permitted */
+ /** GATT read operation is not permitted */
public static final int GATT_READ_NOT_PERMITTED = 0x2;
- /** Gatt write operation is not permitted */
+ /** GATT write operation is not permitted */
public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
/** Insufficient authentication for a given operation */
@@ -111,55 +115,6 @@
/*package*/ static final int AUTHENTICATION_MITM = 2;
/**
- * Bluetooth state change handlers
- */
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
- synchronized (mConnection) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- } else {
- synchronized (mConnection) {
- if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
- if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()),
- mConnection, 0)) {
- Log.e(TAG, "Could not bind to Bluetooth GATT Service");
- }
- }
- }
- }
- }
- };
-
- /**
- * Service binder handling
- */
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothGatt.Stub.asInterface(service);
- ServiceListener serviceListener = mServiceListener;
- if (serviceListener != null) {
- serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this);
- }
- }
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- ServiceListener serviceListener = mServiceListener;
- if (serviceListener != null) {
- serviceListener.onServiceDisconnected(BluetoothProfile.GATT);
- }
- }
- };
-
- /**
* Bluetooth GATT interface callbacks
*/
private final IBluetoothGattCallback mBluetoothGattCallback =
@@ -171,11 +126,27 @@
public void onClientRegistered(int status, int clientIf) {
if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
+ " clientIf=" + clientIf);
+ if (VDBG) {
+ synchronized(mStateLock) {
+ if (mConnState != CONN_STATE_CONNECTING) {
+ Log.e(TAG, "Bad connection state: " + mConnState);
+ }
+ }
+ }
mClientIf = clientIf;
+ if (status != GATT_SUCCESS) {
+ mCallback.onConnectionStateChange(mDevice, GATT_FAILURE,
+ BluetoothProfile.STATE_DISCONNECTED);
+ synchronized(mStateLock) {
+ mConnState = CONN_STATE_IDLE;
+ }
+ return;
+ }
try {
- mCallback.onAppRegistered(status);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ mService.clientConnect(mClientIf, mDevice.getAddress(),
+ !mAutoConnect); // autoConnect is inverse of "isDirect"
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
}
}
@@ -187,13 +158,24 @@
boolean connected, String address) {
if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
+ " clientIf=" + clientIf + " device=" + address);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
+ BluetoothProfile.STATE_DISCONNECTED;
try {
- mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
- connected ? BluetoothProfile.STATE_CONNECTED
- : BluetoothProfile.STATE_DISCONNECTED);
+ mCallback.onConnectionStateChange(mDevice, status, profileState);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
+
+ synchronized(mStateLock) {
+ if (connected) {
+ mConnState = CONN_STATE_CONNECTED;
+ } else {
+ mConnState = CONN_STATE_IDLE;
+ }
+ }
}
/**
@@ -201,13 +183,7 @@
* @hide
*/
public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
-
- try {
- mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
- }
+ // no op
}
/**
@@ -219,8 +195,10 @@
public void onGetService(String address, int srvcType,
int srvcInstId, ParcelUuid srvcUuid) {
if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType));
}
@@ -236,10 +214,12 @@
if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
+ " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device,
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice,
srvcUuid.getUuid(), srvcInstId, srvcType);
- BluetoothGattService includedService = getService(device,
+ BluetoothGattService includedService = getService(mDevice,
inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
if (service != null && includedService != null) {
@@ -260,8 +240,10 @@
if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
charUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service != null) {
service.addCharacteristic(new BluetoothGattCharacteristic(
@@ -281,8 +263,10 @@
ParcelUuid descUuid) {
if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service == null) return;
@@ -303,9 +287,11 @@
*/
public void onSearchComplete(String address, int status) {
if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
try {
- mCallback.onServicesDiscovered(device, status);
+ mCallback.onServicesDiscovered(mDevice, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -322,6 +308,9 @@
if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
+ " UUID=" + charUuid + " Status=" + status);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
&& mAuthRetry == false) {
@@ -338,8 +327,7 @@
mAuthRetry = false;
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service == null) return;
@@ -367,8 +355,10 @@
if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
+ " UUID=" + charUuid + " Status=" + status);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service == null) return;
@@ -411,8 +401,10 @@
byte[] value) {
if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service == null) return;
@@ -439,8 +431,10 @@
ParcelUuid descrUuid, byte[] value) {
if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service == null) return;
@@ -486,8 +480,10 @@
ParcelUuid descrUuid) {
if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+ BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
srvcInstId, srvcType);
if (service == null) return;
@@ -529,9 +525,11 @@
public void onExecuteWrite(String address, int status) {
if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
+ " status=" + status);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
try {
- mCallback.onReliableWriteCompleted(device, status);
+ mCallback.onReliableWriteCompleted(mDevice, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
@@ -544,43 +542,24 @@
public void onReadRemoteRssi(String address, int rssi, int status) {
if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
" rssi=" + rssi + " status=" + status);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
try {
- mCallback.onReadRemoteRssi(device, rssi, status);
+ mCallback.onReadRemoteRssi(mDevice, rssi, status);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
}
};
- /**
- * Create a BluetoothGatt proxy object.
- */
- /*package*/ BluetoothGatt(Context context, ServiceListener l) {
+ /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
mContext = context;
- mServiceListener = l;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mService = iGatt;
+ mDevice = device;
mServices = new ArrayList<BluetoothGattService>();
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
- }
- } else {
- Log.e(TAG, "Unable to get BluetoothManager interface.");
- throw new RuntimeException("BluetoothManager inactive");
- }
-
- //Bind to the service only if the Bluetooth is ON
- if(mAdapter.isEnabled()){
- if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
- Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
- }
- }
+ mConnState = CONN_STATE_IDLE;
}
/**
@@ -590,24 +569,6 @@
if (DBG) Log.d(TAG, "close()");
unregisterApp();
- mServiceListener = null;
-
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
- }
- }
-
- synchronized (mConnection) {
- if (mService != null) {
- mService = null;
- mContext.unbindService(mConnection);
- }
- }
}
/**
@@ -629,18 +590,18 @@
/**
- * Register an application callback to start using Gatt.
+ * Register an application callback to start using GATT.
*
- * <p>This is an asynchronous call. The callback is used to notify
- * success or failure if the function returns true.
+ * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
+ * is used to notify success or failure if the function returns true.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param callback Gatt callback handler that will receive asynchronous
- * callbacks.
- * @return true, if application was successfully registered.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @return If true, the callback will be called to notify success or failure,
+ * false on immediate error
*/
- public boolean registerApp(BluetoothGattCallback callback) {
+ private boolean registerApp(BluetoothGattCallback callback) {
if (DBG) Log.d(TAG, "registerApp()");
if (mService == null) return false;
@@ -661,7 +622,7 @@
/**
* Unregister the current application and callbacks.
*/
- public void unregisterApp() {
+ private void unregisterApp() {
if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
if (mService == null || mClientIf == 0) return;
@@ -675,77 +636,7 @@
}
/**
- * Starts a scan for Bluetooth LE devices.
- *
- * <p>Results of the scan are reported using the
- * {@link BluetoothGattCallback#onScanResult} callback.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @return true, if the scan was started successfully
- */
- public boolean startScan() {
- if (DBG) Log.d(TAG, "startScan()");
- if (mService == null || mClientIf == 0) return false;
-
- try {
- mService.startScan(mClientIf, false);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
- }
-
- /**
- * Starts a scan for Bluetooth LE devices, looking for devices that
- * advertise given services.
- *
- * <p>Devices which advertise all specified services are reported using the
- * {@link BluetoothGattCallback#onScanResult} callback.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param serviceUuids Array of services to look for
- * @return true, if the scan was started successfully
- */
- public boolean startScan(UUID[] serviceUuids) {
- if (DBG) Log.d(TAG, "startScan() - with UUIDs");
- if (mService == null || mClientIf == 0) return false;
-
- try {
- ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
- for(int i = 0; i != uuids.length; ++i) {
- uuids[i] = new ParcelUuid(serviceUuids[i]);
- }
- mService.startScanWithUuids(mClientIf, false, uuids);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
- }
-
- /**
- * Stops an ongoing Bluetooth LE device scan.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- */
- public void stopScan() {
- if (DBG) Log.d(TAG, "stopScan()");
- if (mService == null || mClientIf == 0) return;
-
- try {
- mService.stopScan(mClientIf, false);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
- }
-
- /**
- * Initiate a connection to a Bluetooth Gatt capable device.
+ * Initiate a connection to a Bluetooth GATT capable device.
*
* <p>The connection may not be established right away, but will be
* completed when the remote device is available. A
@@ -757,7 +648,7 @@
* when the remote device is in range/available. Generally, the first ever
* connection to a device should be direct (autoConnect set to false) and
* subsequent connections to known devices should be invoked with the
- * autoConnect parameter set to false.
+ * autoConnect parameter set to true.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
@@ -767,18 +658,24 @@
* device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
- public boolean connect(BluetoothDevice device, boolean autoConnect) {
- if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
- if (mService == null || mClientIf == 0) return false;
-
- try {
- mService.clientConnect(mClientIf, device.getAddress(),
- autoConnect ? false : true); // autoConnect is inverse of "isDirect"
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
+ /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
+ if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
+ synchronized(mStateLock) {
+ if (mConnState != CONN_STATE_IDLE) {
+ throw new IllegalStateException("Not idle");
+ }
+ mConnState = CONN_STATE_CONNECTING;
+ }
+ if (!registerApp(callback)) {
+ synchronized(mStateLock) {
+ mConnState = CONN_STATE_IDLE;
+ }
+ Log.e(TAG, "Failed to register callback");
return false;
}
+ // the connection will continue after successful callback registration
+ mAutoConnect = autoConnect;
return true;
}
@@ -787,18 +684,17 @@
* currently in progress.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param device Remote device
*/
- public void cancelConnection(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress());
+ public void disconnect() {
+ if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return;
try {
- mService.clientDisconnect(mClientIf, device.getAddress());
+ mService.clientDisconnect(mClientIf, mDevice.getAddress());
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
+ // TBD deregister after conneciton is torn down
}
/**
@@ -812,17 +708,16 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device to explore
* @return true, if the remote service discovery has been started
*/
- public boolean discoverServices(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress());
+ public boolean discoverServices() {
+ if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
mServices.clear();
try {
- mService.discoverServices(mClientIf, device.getAddress());
+ mService.discoverServices(mClientIf, mDevice.getAddress());
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -839,16 +734,15 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device
* @return List of services on the remote device. Returns an empty list
* if service discovery has not yet been performed.
*/
- public List<BluetoothGattService> getServices(BluetoothDevice device) {
+ public List<BluetoothGattService> getServices() {
List<BluetoothGattService> result =
new ArrayList<BluetoothGattService>();
for (BluetoothGattService service : mServices) {
- if (service.getDevice().equals(device)) {
+ if (service.getDevice().equals(mDevice)) {
result.add(service);
}
}
@@ -868,14 +762,13 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device
* @param uuid UUID of the requested service
* @return BluetoothGattService if supported, or null if the requested
* service is not offered by the remote device.
*/
- public BluetoothGattService getService(BluetoothDevice device, UUID uuid) {
+ public BluetoothGattService getService(UUID uuid) {
for (BluetoothGattService service : mServices) {
- if (service.getDevice().equals(device) &&
+ if (service.getDevice().equals(mDevice) &&
service.getUuid().equals(uuid)) {
return service;
}
@@ -923,8 +816,7 @@
}
/**
- * Writes a given characteristic and it's values to the associated remote
- * device.
+ * Writes a given characteristic and its values to the associated remote device.
*
* <p>Once the write operation has been completed, the
* {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
@@ -1061,15 +953,14 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device
* @return true, if the reliable write transaction has been initiated
*/
- public boolean beginReliableWrite(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress());
+ public boolean beginReliableWrite() {
+ if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.beginReliableWrite(mClientIf, device.getAddress());
+ mService.beginReliableWrite(mClientIf, mDevice.getAddress());
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -1089,15 +980,14 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device
* @return true, if the request to execute the transaction has been sent
*/
- public boolean executeReliableWrite(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress());
+ public boolean executeReliableWrite() {
+ if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.endReliableWrite(mClientIf, device.getAddress(), true);
+ mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -1113,15 +1003,13 @@
* operations for a given remote device.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param device Remote device
*/
- public void abortReliableWrite(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress());
+ public void abortReliableWrite(BluetoothDevice mDevice) {
+ if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return;
try {
- mService.endReliableWrite(mClientIf, device.getAddress(), false);
+ mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
@@ -1172,12 +1060,12 @@
* remote device.
* @hide
*/
- public boolean refresh(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress());
+ public boolean refresh() {
+ if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.refreshDevice(mClientIf, device.getAddress());
+ mService.refreshDevice(mClientIf, mDevice.getAddress());
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -1194,15 +1082,14 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device
* @return true, if the RSSI value has been requested successfully
*/
- public boolean readRemoteRssi(BluetoothDevice device) {
- if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress());
+ public boolean readRemoteRssi() {
+ if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
try {
- mService.readRemoteRssi(mClientIf, device.getAddress());
+ mService.readRemoteRssi(mClientIf, mDevice.getAddress());
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -1212,98 +1099,38 @@
}
/**
- * Get the current connection state of the profile.
+ * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * with {@link BluetoothProfile#GATT} as argument
*
- * <p>This is not specific to any application configuration but represents
- * the connection state of the local Bluetooth adapter for this profile.
- * This can be used by applications like status bar which would just like
- * to know the state of the local adapter.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param device Remote bluetooth device.
- * @return State of the profile connection. One of
- * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
- * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+ * @throws UnsupportedOperationException
*/
@Override
public int getConnectionState(BluetoothDevice device) {
- if (DBG) Log.d(TAG,"getConnectionState()");
- if (mService == null) return STATE_DISCONNECTED;
-
- List<BluetoothDevice> connectedDevices = getConnectedDevices();
- for(BluetoothDevice connectedDevice : connectedDevices) {
- if (device.equals(connectedDevice)) {
- return STATE_CONNECTED;
- }
- }
-
- return STATE_DISCONNECTED;
+ throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
/**
- * Get connected devices for the Gatt profile.
+ * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * with {@link BluetoothProfile#GATT} as argument
*
- * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
- *
- * <p>This is not specific to any application configuration but represents
- * the connection state of the local Bluetooth adapter for this profile.
- * This can be used by applications like status bar which would just like
- * to know the state of the local adapter.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @return List of devices. The list will be empty on error.
+ * @throws UnsupportedOperationException
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
- if (DBG) Log.d(TAG,"getConnectedDevices");
-
- List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
- if (mService == null) return connectedDevices;
-
- try {
- connectedDevices = mService.getDevicesMatchingConnectionStates(
- new int[] { BluetoothProfile.STATE_CONNECTED });
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
-
- return connectedDevices;
+ throw new UnsupportedOperationException
+ ("Use BluetoothManager#getConnectedDevices instead.");
}
/**
- * Get a list of devices that match any of the given connection
- * states.
+ * Not supported - please use
+ * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
+ * with {@link BluetoothProfile#GATT} as first argument
*
- * <p> If none of the devices match any of the given states,
- * an empty list will be returned.
- *
- * <p>This is not specific to any application configuration but represents
- * the connection state of the local Bluetooth adapter for this profile.
- * This can be used by applications like status bar which would just like
- * to know the state of the local adapter.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param states Array of states. States can be one of
- * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
- * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
- * @return List of devices. The list will be empty on error.
+ * @throws UnsupportedOperationException
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
-
- List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
- if (mService == null) return devices;
-
- try {
- devices = mService.getDevicesMatchingConnectionStates(states);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
-
- return devices;
+ throw new UnsupportedOperationException
+ ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
}
}
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index afa4539..c9e5fea 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -18,34 +18,10 @@
import android.bluetooth.BluetoothDevice;
-import android.util.Log;
-
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
- * @hide
*/
public abstract class BluetoothGattCallback {
- /**
- * Callback to inform change in registration state of the application.
- *
- * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application
- * was successfully registered.
- */
- public void onAppRegistered(int status) {
- }
-
- /**
- * Callback reporting an LE device found during a device scan initiated
- * by the {@link BluetoothGatt#startScan} function.
- *
- * @param device Identifies the remote device
- * @param rssi The RSSI value for the remote device as reported by the
- * Bluetooth hardware. 0 if no RSSI value is available.
- * @param scanRecord The content of the advertisement record offered by
- * the remote device.
- */
- public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
- }
/**
* Callback indicating when a remote device has been connected or disconnected.
@@ -61,8 +37,8 @@
}
/**
- * Callback invoked when the list of remote services, characteristics and
- * descriptors for the remote device have been updated.
+ * Callback invoked when the list of remote services, characteristics and descriptors
+ * for the remote device have been updated, ie new services have been discovered.
*
* @param device Remote device
* @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
@@ -97,7 +73,7 @@
* @param status The result of the write operation
*/
public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic,
- int status) {
+ int status) {
}
/**
@@ -113,23 +89,21 @@
* Callback reporting the result of a descriptor read operation.
*
* @param descriptor Descriptor that was read from the associated
- * remote device.
+ * remote device.
* @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
* was completed successfully
*/
- public void onDescriptorRead(BluetoothGattDescriptor descriptor,
- int status) {
+ public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) {
}
/**
* Callback indicating the result of a descriptor write operation.
*
* @param descriptor Descriptor that was writte to the associated
- * remote device.
+ * remote device.
* @param status The result of the write operation
*/
- public void onDescriptorWrite(BluetoothGattDescriptor descriptor,
- int status) {
+ public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) {
}
/**
@@ -150,7 +124,7 @@
*
* @param device Identifies the remote device
* @param rssi The RSSI value for the remote device
- * @param status 0 if the RSSI was read successfully
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
*/
public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) {
}
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index f44dc5c0..d63d97e 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -21,8 +21,7 @@
import java.util.UUID;
/**
- * Represents a Bluetooth Gatt Characteristic
- * @hide
+ * Represents a Bluetooth GATT Characteristic
*/
public class BluetoothGattCharacteristic {
@@ -119,7 +118,7 @@
public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
/**
- * Write characteristic including and authenticated signature
+ * Write characteristic including authentication signature
*/
public static final int WRITE_TYPE_SIGNED = 0x04;
@@ -219,12 +218,30 @@
protected List<BluetoothGattDescriptor> mDescriptors;
/**
+ * Create a new BluetoothGattCharacteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param uuid The UUID for this characteristic
+ * @param properties Properties of this characteristic
+ * @param permissions Permissions for this characteristic
+ */
+ public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
+ initCharacteristic(null, uuid, 0, properties, permissions);
+ }
+
+ /**
* Create a new BluetoothGattCharacteristic
* @hide
*/
/*package*/ BluetoothGattCharacteristic(BluetoothGattService service,
UUID uuid, int instanceId,
int properties, int permissions) {
+ initCharacteristic(service, uuid, instanceId, properties, permissions);
+ }
+
+ private void initCharacteristic(BluetoothGattService service,
+ UUID uuid, int instanceId,
+ int properties, int permissions) {
mUuid = uuid;
mInstance = instanceId;
mProperties = properties;
@@ -249,11 +266,16 @@
}
/**
- * Add a descriptor to this characteristic
- * @hide
+ * Adds a descriptor to this characteristic.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param descriptor Descriptor to be added to this characteristic.
+ * @return true, if the descriptor was added to the characteristic
*/
- /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) {
+ public boolean addDescriptor(BluetoothGattDescriptor descriptor) {
mDescriptors.add(descriptor);
+ descriptor.setCharacteristic(this);
+ return true;
}
/**
@@ -265,8 +287,15 @@
}
/**
+ * Sets the service associated with this device.
+ * @hide
+ */
+ /*package*/ void setService(BluetoothGattService service) {
+ mService = service;
+ }
+
+ /**
* Returns the UUID of this characteristic
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return UUID of this characteristic
*/
@@ -280,8 +309,6 @@
* <p>If a remote device offers multiple characteristics with the same UUID,
* the instance ID is used to distuinguish between characteristics.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Instance ID of this characteristic
*/
public int getInstanceId() {
@@ -294,8 +321,6 @@
* <p>The properties contain a bit mask of property flags indicating
* the features of this characteristic.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Properties of this characteristic
*/
public int getProperties() {
@@ -304,7 +329,6 @@
/**
* Returns the permissions for this characteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return Permissions of this characteristic
*/
@@ -314,7 +338,6 @@
/**
* Gets the write type for this characteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return Write type for this characteristic
*/
@@ -329,11 +352,6 @@
* {@link BluetoothGatt#writeCharacteristic} function write this
* characteristic.
*
- * <p>The default write type for a characteristic is
- * {@link #WRITE_TYPE_DEFAULT}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param writeType The write type to for this characteristic. Can be one
* of:
* {@link #WRITE_TYPE_DEFAULT},
@@ -345,8 +363,15 @@
}
/**
+ * Set the desired key size.
+ * @hide
+ */
+ public void setKeySize(int keySize) {
+ mKeySize = keySize;
+ }
+
+ /**
* Returns a list of descriptors for this characteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return Descriptors for this characteristic
*/
@@ -358,9 +383,7 @@
* Returns a descriptor with a given UUID out of the list of
* descriptors for this characteristic.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @return Gatt descriptor object or null if no descriptor with the
+ * @return GATT descriptor object or null if no descriptor with the
* given UUID was found.
*/
public BluetoothGattDescriptor getDescriptor(UUID uuid) {
@@ -376,12 +399,10 @@
* Get the stored value for this characteristic.
*
* <p>This function returns the stored value for this characteristic as
- * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached
+ * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached
* value of the characteristic is updated as a result of a read characteristic
* operation or if a characteristic update notification has been received.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Cached value of the characteristic
*/
public byte[] getValue() {
@@ -397,8 +418,6 @@
* characteristic value at the given offset are interpreted to generate the
* return value.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param formatType The format type used to interpret the characteristic
* value.
* @param offset Offset at which the integer value can be found.
@@ -436,7 +455,6 @@
/**
* Return the stored value of this characteristic.
* <p>See {@link #getValue} for details.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param formatType The format type used to interpret the characteristic
* value.
@@ -462,7 +480,7 @@
/**
* Return the stored value of this characteristic.
* <p>See {@link #getValue} for details.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
* @param offset Offset at which the string value can be found.
* @return Cached value of the characteristic
*/
@@ -481,8 +499,6 @@
* {@link BluetoothGatt#writeCharacteristic} to send the value to the
* remote device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param value New value for this characteristic
* @return true if the locally stored value has been set, false if the
* requested value could not be stored locally.
@@ -495,7 +511,6 @@
/**
* Set the locally stored value of this characteristic.
* <p>See {@link #setValue(byte[])} for details.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param value New value for this characteristic
* @param formatType Integer format type used to transform the value parameter
@@ -542,7 +557,7 @@
/**
* Set the locally stored value of this characteristic.
* <p>See {@link #setValue(byte[])} for details.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
* @param mantissa Mantissa for this characteristic
* @param exponent exponent value for this characteristic
* @param formatType Float format type used to transform the value parameter
@@ -582,7 +597,7 @@
/**
* Set the locally stored value of this characteristic.
* <p>See {@link #setValue(byte[])} for details.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
* @param value New value for this characteristic
* @return true if the locally stored value has been set
*/
@@ -593,7 +608,6 @@
/**
* Returns the size of a give value type.
- * @hide
*/
private int getTypeLen(int formatType) {
return formatType & 0xF;
@@ -601,7 +615,6 @@
/**
* Convert a signed byte to an unsigned int.
- * @hide
*/
private int unsignedByteToInt(byte b) {
return b & 0xFF;
@@ -609,7 +622,6 @@
/**
* Convert signed bytes to a 16-bit unsigned int.
- * @hide
*/
private int unsignedBytesToInt(byte b0, byte b1) {
return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8));
@@ -617,7 +629,6 @@
/**
* Convert signed bytes to a 32-bit unsigned int.
- * @hide
*/
private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) {
return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8))
@@ -626,7 +637,6 @@
/**
* Convert signed bytes to a 16-bit short float value.
- * @hide
*/
private float bytesToFloat(byte b0, byte b1) {
int mantissa = unsignedToSigned(unsignedByteToInt(b0)
@@ -637,7 +647,6 @@
/**
* Convert signed bytes to a 32-bit short float value.
- * @hide
*/
private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) {
int mantissa = unsignedToSigned(unsignedByteToInt(b0)
@@ -649,7 +658,6 @@
/**
* Convert an unsigned integer value to a two's-complement encoded
* signed value.
- * @hide
*/
private int unsignedToSigned(int unsigned, int size) {
if ((unsigned & (1 << size-1)) != 0) {
@@ -660,7 +668,6 @@
/**
* Convert an integer into the signed bits of a given length.
- * @hide
*/
private int intToSignedBits(int i, int size) {
if (i < 0) {
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index ba1f28a..6ba2db7 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -19,8 +19,7 @@
import java.util.UUID;
/**
- * Represents a Bluetooth Gatt Descriptor
- * @hide
+ * Represents a Bluetooth GATT Descriptor
*/
public class BluetoothGattDescriptor {
@@ -109,12 +108,28 @@
* Create a new BluetoothGattDescriptor.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
+ * @param uuid The UUID for this descriptor
+ * @param permissions Permissions for this descriptor
+ */
+ public BluetoothGattDescriptor(UUID uuid, int permissions) {
+ initDescriptor(null, uuid, permissions);
+ }
+
+ /**
+ * Create a new BluetoothGattDescriptor.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
* @param characteristic The characteristic this descriptor belongs to
* @param uuid The UUID for this descriptor
* @param permissions Permissions for this descriptor
*/
/*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
int permissions) {
+ initDescriptor(characteristic, uuid, permissions);
+ }
+
+ private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
+ int permissions) {
mCharacteristic = characteristic;
mUuid = uuid;
mPermissions = permissions;
@@ -129,8 +144,15 @@
}
/**
+ * Set the back-reference to the associated characteristic
+ * @hide
+ */
+ /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
+ mCharacteristic = characteristic;
+ }
+
+ /**
* Returns the UUID of this descriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return UUID of this descriptor
*/
@@ -140,7 +162,6 @@
/**
* Returns the permissions for this descriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return Permissions of this descriptor
*/
@@ -152,12 +173,10 @@
* Returns the stored value for this descriptor
*
* <p>This function returns the stored value for this descriptor as
- * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached
+ * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached
* value of the descriptor is updated as a result of a descriptor read
* operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Cached value of the descriptor
*/
public byte[] getValue() {
@@ -172,8 +191,6 @@
* {@link BluetoothGatt#writeDescriptor} to send the value to the
* remote device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param value New value for this descriptor
* @return true if the locally stored value has been set, false if the
* requested value could not be stored locally.
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 6b69377..d1f4b82 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -38,88 +38,30 @@
import java.util.UUID;
/**
- * Public API for the Bluetooth Gatt Profile server role.
+ * Public API for the Bluetooth GATT Profile server role.
*
- * <p>This class provides Bluetooth Gatt server role functionality,
+ * <p>This class provides Bluetooth GATT server role functionality,
* allowing applications to create and advertise Bluetooth Smart services
* and characteristics.
*
* <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
* via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
* BluetoothGatt proxy object.
- * @hide
*/
public final class BluetoothGattServer implements BluetoothProfile {
private static final String TAG = "BluetoothGattServer";
private static final boolean DBG = true;
- private Context mContext;
- private ServiceListener mServiceListener;
+ private final Context mContext;
private BluetoothAdapter mAdapter;
private IBluetoothGatt mService;
private BluetoothGattServerCallback mCallback;
- private int mServerIf;
+ private Object mServerIfLock = new Object();
+ private int mServerIf;
private List<BluetoothGattService> mServices;
- /**
- * Bluetooth state change handlers
- */
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG,"",re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
- if (!mContext.bindService(new
- Intent(IBluetoothGatt.class.getName()),
- mConnection, 0)) {
- Log.e(TAG, "Could not bind to Bluetooth GATT Service");
- }
- }
- } catch (Exception re) {
- Log.e(TAG,"",re);
- }
- }
- }
- }
- };
-
- /**
- * Service binder handling
- */
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothGatt.Stub.asInterface(service);
- ServiceListener serviceListner = mServiceListener;
- if (serviceListner != null) {
- serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER,
- BluetoothGattServer.this);
- }
- }
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- ServiceListener serviceListner = mServiceListener;
- if (serviceListner != null) {
- serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER);
- }
- }
- };
+ private static final int CALLBACK_REG_TIMEOUT = 10000;
/**
* Bluetooth GATT interface callbacks
@@ -133,11 +75,14 @@
public void onServerRegistered(int status, int serverIf) {
if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
+ " serverIf=" + serverIf);
- mServerIf = serverIf;
- try {
- mCallback.onAppRegistered(status);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
+ synchronized(mServerIfLock) {
+ if (mCallback != null) {
+ mServerIf = serverIf;
+ mServerIfLock.notify();
+ } else {
+ // registration timeout
+ Log.e(TAG, "onServerRegistered: mCallback is null");
+ }
}
}
@@ -147,13 +92,7 @@
*/
public void onScanResult(String address, int rssi, byte[] advData) {
if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
-
- try {
- mCallback.onScanResult(mAdapter.getRemoteDevice(address),
- rssi, advData);
- } catch (Exception ex) {
- Log.w(TAG, "Unhandled exception: " + ex);
- }
+ // no op
}
/**
@@ -209,8 +148,7 @@
BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
if (service == null) return;
- BluetoothGattCharacteristic characteristic = service.getCharacteristic(
- charUuid);
+ BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
if (characteristic == null) return;
try {
@@ -340,31 +278,13 @@
/**
* Create a BluetoothGattServer proxy object.
*/
- /*package*/ BluetoothGattServer(Context context, ServiceListener l) {
+ /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) {
mContext = context;
- mServiceListener = l;
+ mService = iGatt;
mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mCallback = null;
+ mServerIf = 0;
mServices = new ArrayList<BluetoothGattService>();
-
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
- }
- } else {
- Log.e(TAG, "Unable to get BluetoothManager interface.");
- throw new RuntimeException("BluetoothManager inactive");
- }
-
- //Bind to the service only if the Bluetooth is ON
- if(mAdapter.isEnabled()){
- if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
- Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
- }
- }
}
/**
@@ -372,29 +292,75 @@
*/
/*package*/ void close() {
if (DBG) Log.d(TAG, "close()");
+ unregisterCallback();
+ }
- unregisterApp();
- mServiceListener = null;
+ /**
+ * Register an application callback to start using GattServer.
+ *
+ * <p>This is an asynchronous call. The callback is used to notify
+ * success or failure if the function returns true.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callback GATT callback handler that will receive asynchronous
+ * callbacks.
+ * @return true, the callback will be called to notify success or failure,
+ * false on immediate error
+ */
+ /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
+ if (DBG) Log.d(TAG, "registerCallback()");
+ if (mService == null) {
+ Log.e(TAG, "GATT service not available");
+ return false;
+ }
+ UUID uuid = UUID.randomUUID();
+ if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
- IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
- if (b != null) {
- IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
+ synchronized(mServerIfLock) {
+ if (mCallback != null) {
+ Log.e(TAG, "App can register callback only once");
+ return false;
+ }
+
+ mCallback = callback;
try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
+ mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ mCallback = null;
+ return false;
+ }
+
+ try {
+ mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "" + e);
+ mCallback = null;
+ }
+
+ if (mServerIf == 0) {
+ mCallback = null;
+ return false;
+ } else {
+ return true;
}
}
+ }
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG,"",re);
- }
- }
+ /**
+ * Unregister the current application and callbacks.
+ */
+ private void unregisterCallback() {
+ if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
+ if (mService == null || mServerIf == 0) return;
+
+ try {
+ mCallback = null;
+ mService.unregisterServer(mServerIf);
+ mServerIf = 0;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
}
}
@@ -414,123 +380,7 @@
}
/**
- * Register an application callback to start using Gatt.
- *
- * <p>This is an asynchronous call. The callback is used to notify
- * success or failure if the function returns true.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param callback Gatt callback handler that will receive asynchronous
- * callbacks.
- * @return true, if application was successfully registered.
- */
- public boolean registerApp(BluetoothGattServerCallback callback) {
- if (DBG) Log.d(TAG, "registerApp()");
- if (mService == null) return false;
-
- mCallback = callback;
- UUID uuid = UUID.randomUUID();
- if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
-
- try {
- mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
- }
-
- /**
- * Unregister the current application and callbacks.
- */
- public void unregisterApp() {
- if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf);
- if (mService == null || mServerIf == 0) return;
-
- try {
- mCallback = null;
- mService.unregisterServer(mServerIf);
- mServerIf = 0;
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
- }
-
- /**
- * Starts a scan for Bluetooth LE devices.
- *
- * <p>Results of the scan are reported using the
- * {@link BluetoothGattServerCallback#onScanResult} callback.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @return true, if the scan was started successfully
- */
- public boolean startScan() {
- if (DBG) Log.d(TAG, "startScan()");
- if (mService == null || mServerIf == 0) return false;
-
- try {
- mService.startScan(mServerIf, true);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
- }
-
- /**
- * Starts a scan for Bluetooth LE devices, looking for devices that
- * advertise given services.
- *
- * <p>Devices which advertise all specified services are reported using the
- * {@link BluetoothGattServerCallback#onScanResult} callback.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param serviceUuids Array of services to look for
- * @return true, if the scan was started successfully
- */
- public boolean startScan(UUID[] serviceUuids) {
- if (DBG) Log.d(TAG, "startScan() - with UUIDs");
- if (mService == null || mServerIf == 0) return false;
-
- try {
- ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
- for(int i = 0; i != uuids.length; ++i) {
- uuids[i] = new ParcelUuid(serviceUuids[i]);
- }
- mService.startScanWithUuids(mServerIf, true, uuids);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- return false;
- }
-
- return true;
- }
-
- /**
- * Stops an ongoing Bluetooth LE device scan.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- */
- public void stopScan() {
- if (DBG) Log.d(TAG, "stopScan()");
- if (mService == null || mServerIf == 0) return;
-
- try {
- mService.stopScan(mServerIf, true);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
- }
-
- /**
- * Initiate a connection to a Bluetooth Gatt capable device.
+ * Initiate a connection to a Bluetooth GATT capable device.
*
* <p>The connection may not be established right away, but will be
* completed when the remote device is available. A
@@ -542,11 +392,10 @@
* when the remote device is in range/available. Generally, the first ever
* connection to a device should be direct (autoConnect set to false) and
* subsequent connections to known devices should be invoked with the
- * autoConnect parameter set to false.
+ * autoConnect parameter set to true.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param device Remote device to connect to
* @param autoConnect Whether to directly connect to the remote device (false)
* or to automatically connect as soon as the remote
* device becomes available (true).
@@ -590,7 +439,7 @@
* Send a response to a read or write request to a remote device.
*
* <p>This function must be invoked in when a remote read/write request
- * is received by one of these callback methots:
+ * is received by one of these callback methods:
*
* <ul>
* <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
@@ -662,17 +511,17 @@
}
/**
- * Add a service to the list of services to be advertised.
+ * Add a service to the list of services to be hosted.
*
* <p>Once a service has been addded to the the list, the service and it's
- * included characteristics will be advertised by the local device.
+ * included characteristics will be provided by the local device.
*
- * <p>If the local device is already advertising services when this function
+ * <p>If the local device has already exposed services when this function
* is called, a service update notification will be sent to all clients.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param service Service to be added to the list of services advertised
+ * @param service Service to be added to the list of services provided
* by this device.
* @return true, if the service has been added successfully
*/
@@ -721,11 +570,11 @@
}
/**
- * Removes a service from the list of services to be advertised.
+ * Removes a service from the list of services to be provided.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * @param service Service to beremoved.
+ * @param service Service to be removed.
* @return true, if the service has been removed
*/
public boolean removeService(BluetoothGattService service) {
@@ -749,7 +598,7 @@
}
/**
- * Remove all services from the list of advertised services.
+ * Remove all services from the list of provided services.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
public void clearServices() {
@@ -765,7 +614,7 @@
}
/**
- * Returns a list of GATT services offered bu this device.
+ * Returns a list of GATT services offered by this device.
*
* <p>An application must call {@link #addService} to add a serice to the
* list of services offered by this device.
@@ -802,99 +651,40 @@
return null;
}
+
/**
- * Get the current connection state of the profile.
+ * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * with {@link BluetoothProfile#GATT} as argument
*
- * <p>This is not specific to any application configuration but represents
- * the connection state of the local Bluetooth adapter for this profile.
- * This can be used by applications like status bar which would just like
- * to know the state of the local adapter.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param device Remote bluetooth device.
- * @return State of the profile connection. One of
- * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
- * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+ * @throws UnsupportedOperationException
*/
@Override
public int getConnectionState(BluetoothDevice device) {
- if (DBG) Log.d(TAG,"getConnectionState()");
- if (mService == null) return STATE_DISCONNECTED;
-
- List<BluetoothDevice> connectedDevices = getConnectedDevices();
- for(BluetoothDevice connectedDevice : connectedDevices) {
- if (device.equals(connectedDevice)) {
- return STATE_CONNECTED;
- }
- }
-
- return STATE_DISCONNECTED;
+ throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
/**
- * Get connected devices for the Gatt profile.
+ * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * with {@link BluetoothProfile#GATT} as argument
*
- * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
- *
- * <p>This is not specific to any application configuration but represents
- * the connection state of the local Bluetooth adapter for this profile.
- * This can be used by applications like status bar which would just like
- * to know the state of the local adapter.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @return List of devices. The list will be empty on error.
+ * @throws UnsupportedOperationException
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
- if (DBG) Log.d(TAG,"getConnectedDevices");
-
- List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
- if (mService == null) return connectedDevices;
-
- try {
- connectedDevices = mService.getDevicesMatchingConnectionStates(
- new int[] { BluetoothProfile.STATE_CONNECTED });
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
-
- return connectedDevices;
+ throw new UnsupportedOperationException
+ ("Use BluetoothManager#getConnectedDevices instead.");
}
/**
- * Get a list of devices that match any of the given connection
- * states.
+ * Not supported - please use
+ * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
+ * with {@link BluetoothProfile#GATT} as first argument
*
- * <p> If none of the devices match any of the given states,
- * an empty list will be returned.
- *
- * <p>This is not specific to any application configuration but represents
- * the connection state of the local Bluetooth adapter for this profile.
- * This can be used by applications like status bar which would just like
- * to know the state of the local adapter.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param states Array of states. States can be one of
- * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
- * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
- * @return List of devices. The list will be empty on error.
+ * @throws UnsupportedOperationException
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
-
- List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
- if (mService == null) return devices;
-
- try {
- devices = mService.getDevicesMatchingConnectionStates(states);
- } catch (RemoteException e) {
- Log.e(TAG,"",e);
- }
-
- return devices;
+ throw new UnsupportedOperationException
+ ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
}
}
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
index 4f608ff..f9f1d97 100644
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -22,30 +22,8 @@
/**
* This abstract class is used to implement {@link BluetoothGattServer} callbacks.
- * @hide
*/
public abstract class BluetoothGattServerCallback {
- /**
- * Callback to inform change in registration state of the application.
- *
- * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application
- * was successfully registered.
- */
- public void onAppRegistered(int status) {
- }
-
- /**
- * Callback reporting an LE device found during a device scan initiated
- * by the {@link BluetoothGattServer#startScan} function.
- *
- * @param device Identifies the remote device
- * @param rssi The RSSI value for the remote device as reported by the
- * Bluetooth hardware. 0 if no RSSI value is available.
- * @param scanRecord The content of the advertisement record offered by
- * the remote device.
- */
- public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
- }
/**
* Callback indicating when a remote device has been connected or disconnected.
@@ -101,9 +79,9 @@
* @param value The value the client wants to assign to the characteristic
*/
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
- BluetoothGattCharacteristic characteristic,
- boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
+ BluetoothGattCharacteristic characteristic,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
}
/**
@@ -118,7 +96,7 @@
* @param descriptor Descriptor to be read
*/
public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
- int offset, BluetoothGattDescriptor descriptor) {
+ int offset, BluetoothGattDescriptor descriptor) {
}
/**
@@ -137,9 +115,9 @@
* @param value The value the client wants to assign to the descriptor
*/
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
- BluetoothGattDescriptor descriptor,
- boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
+ BluetoothGattDescriptor descriptor,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
}
/**
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index 6a3ce66e..c3b3cfe 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -22,8 +22,7 @@
import java.util.UUID;
/**
- * Represents a Bluetooth Gatt Service
- * @hide
+ * Represents a Bluetooth GATT Service
*/
public class BluetoothGattService {
@@ -81,9 +80,14 @@
/**
* Create a new BluetoothGattService.
- * @hide
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param uuid The UUID for this service
+ * @param serviceType The type of this service,
+ * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} or
+ * {@link BluetoothGattService#SERVICE_TYPE_SECONDARY}
*/
- /*package*/ BluetoothGattService(UUID uuid, int serviceType) {
+ public BluetoothGattService(UUID uuid, int serviceType) {
mDevice = null;
mUuid = uuid;
mInstanceId = 0;
@@ -115,11 +119,28 @@
}
/**
- * Add a characteristic to this service.
- * @hide
+ * Add an included service to this service.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param service The service to be added
+ * @return true, if the included service was added to the service
*/
- /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) {
+ public boolean addService(BluetoothGattService service) {
+ mIncludedServices.add(service);
+ return true;
+ }
+
+ /**
+ * Add a characteristic to this service.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param characteristic The characteristics to be added
+ * @return true, if the characteristic was added to the service
+ */
+ public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) {
mCharacteristics.add(characteristic);
+ characteristic.setService(this);
+ return true;
}
/**
@@ -136,6 +157,15 @@
}
/**
+ * Force the instance ID.
+ * This is needed for conformance testing only.
+ * @hide
+ */
+ public void setInstanceId(int instanceId) {
+ mInstanceId = instanceId;
+ }
+
+ /**
* Get the handle count override (conformance testing.
* @hide
*/
@@ -144,6 +174,15 @@
}
/**
+ * Force the number of handles to reserve for this service.
+ * This is needed for conformance testing only.
+ * @hide
+ */
+ public void setHandles(int handles) {
+ mHandles = handles;
+ }
+
+ /**
* Add an included service to the internal map.
* @hide
*/
@@ -153,7 +192,6 @@
/**
* Returns the UUID of this service
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return UUID of this service
*/
@@ -168,8 +206,6 @@
* (ex. multiple battery services for different batteries), the instance
* ID is used to distuinguish services.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Instance ID of this service
*/
public int getInstanceId() {
@@ -178,15 +214,13 @@
/**
* Get the type of this service (primary/secondary)
- * @hide
*/
public int getType() {
return mServiceType;
}
/**
- * Get the list of included Gatt services for this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ * Get the list of included GATT services for this service.
*
* @return List of included services or empty list if no included services
* were discovered.
@@ -197,7 +231,6 @@
/**
* Returns a list of characteristics included in this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @return Characteristics included in this service
*/
@@ -217,9 +250,7 @@
* UUID, the first instance of a characteristic with the given UUID
* is returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @return Gatt characteristic object or null if no characteristic with the
+ * @return GATT characteristic object or null if no characteristic with the
* given UUID was found.
*/
public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
new file mode 100644
index 0000000..19083b5
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 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.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * High level manager used to obtain an instance of an {@link BluetoothAdapter}
+ * and to conduct overall Bluetooth Management.
+ * <p>
+ * Use {@link android.content.Context#getSystemService(java.lang.String)}
+ * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
+ * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
+ * <p>
+ * Alternately, you can just call the static helper
+ * {@link BluetoothAdapter#getDefaultAdapter()}.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using BLUETOOTH, read the
+ * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
+ * </div>
+ *
+ * @see Context#getSystemService
+ * @see BluetoothAdapter#getDefaultAdapter()
+ */
+public final class BluetoothManager {
+ private static final String TAG = "BluetoothManager";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = true;
+
+ private final BluetoothAdapter mAdapter;
+
+ /**
+ * @hide
+ */
+ public BluetoothManager(Context context) {
+ context = context.getApplicationContext();
+ if (context == null) {
+ throw new IllegalArgumentException(
+ "context not associated with any application (using a mock context?)");
+ }
+ // Legacy api - getDefaultAdapter does not take in the context
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ }
+
+ /**
+ * Get the default BLUETOOTH Adapter for this device.
+ *
+ * @return the default BLUETOOTH Adapter
+ */
+ public BluetoothAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ /**
+ * Get the current connection state of the profile to the remote device.
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for certain profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of Bluetooth.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote bluetooth device.
+ * @param profile GATT or GATT_SERVER
+ * @return State of the profile connection. One of
+ * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING},
+ * {@link BluetoothProfile#STATE_DISCONNECTED},
+ * {@link BluetoothProfile#STATE_DISCONNECTING}
+ */
+ public int getConnectionState(BluetoothDevice device, int profile) {
+ if (DBG) Log.d(TAG,"getConnectionState()");
+
+ List<BluetoothDevice> connectedDevices = getConnectedDevices(profile);
+ for(BluetoothDevice connectedDevice : connectedDevices) {
+ if (device.equals(connectedDevice)) {
+ return BluetoothProfile.STATE_CONNECTED;
+ }
+ }
+
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Get connected devices for the specified profile.
+ *
+ * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED}
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of Bluetooth for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of Bluetooth.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param profile GATT or GATT_SERVER
+ * @return List of devices. The list will be empty on error.
+ */
+ public List<BluetoothDevice> getConnectedDevices(int profile) {
+ if (DBG) Log.d(TAG,"getConnectedDevices");
+ if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
+ throw new IllegalArgumentException("Profile not supported: " + profile);
+ }
+
+ List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
+
+ try {
+ IBluetoothManager managerService = mAdapter.getBluetoothManager();
+ IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ if (iGatt == null) return connectedDevices;
+
+ connectedDevices = iGatt.getDevicesMatchingConnectionStates(
+ new int[] { BluetoothProfile.STATE_CONNECTED });
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+
+ return connectedDevices;
+ }
+
+ /**
+ *
+ * Get a list of devices that match any of the given connection
+ * states.
+ *
+ * <p> If none of the devices match any of the given states,
+ * an empty list will be returned.
+ *
+ * <p>This is not specific to any application configuration but represents
+ * the connection state of the local Bluetooth adapter for this profile.
+ * This can be used by applications like status bar which would just like
+ * to know the state of the local adapter.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param profile GATT or GATT_SERVER
+ * @param states Array of states. States can be one of
+ * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING},
+ * {@link BluetoothProfile#STATE_DISCONNECTED},
+ * {@link BluetoothProfile#STATE_DISCONNECTING},
+ * @return List of devices. The list will be empty on error.
+ */
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
+ if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
+
+ if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
+ throw new IllegalArgumentException("Profile not supported: " + profile);
+ }
+
+ List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+
+ try {
+ IBluetoothManager managerService = mAdapter.getBluetoothManager();
+ IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ if (iGatt == null) return devices;
+ devices = iGatt.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+
+ return devices;
+ }
+
+ /**
+ * Open a GATT Server
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as the results of any other GATT server operations.
+ * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+ * to conduct GATT server operations.
+ * @param context App context
+ * @param callback GATT server callback handler that will receive asynchronous callbacks.
+ * @return BluetoothGattServer instance
+ */
+ public BluetoothGattServer openGattServer(Context context,
+ BluetoothGattServerCallback callback) {
+ if (context == null || callback == null) {
+ throw new IllegalArgumentException("null parameter: " + context + " " + callback);
+ }
+
+ // TODO(Bluetooth) check whether platform support BLE
+ // Do the check here or in GattServer?
+
+ try {
+ IBluetoothManager managerService = mAdapter.getBluetoothManager();
+ IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+ if (iGatt == null) {
+ Log.e(TAG, "Fail to get GATT Server connection");
+ return null;
+ }
+ BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt);
+ Boolean regStatus = mGattServer.registerCallback(callback);
+ return regStatus? mGattServer : null;
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 9ee202a..43079f4 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -89,13 +89,11 @@
/**
* GATT
- * @hide
*/
static public final int GATT = 7;
/**
* GATT_SERVER
- * @hide
*/
static public final int GATT_SERVER = 8;
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index ed8777c..493d2f8 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothStateChangeCallback;
@@ -35,6 +36,7 @@
boolean enable();
boolean enableNoAutoConnect();
boolean disable(boolean persist);
+ IBluetoothGatt getBluetoothGatt();
String getAddress();
String getName();
diff --git a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java
deleted file mode 100644
index c05abb2..0000000
--- a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 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 java.util.ArrayList;
-import java.util.IllegalFormatConversionException;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Mutable variant of a Bluetooth Gatt Characteristic
- * @hide
- */
-public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic {
-
- /**
- * Create a new MutableBluetoothGattCharacteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param uuid The UUID for this characteristic
- * @param properties Properties of this characteristic
- * @param permissions Permissions for this characteristic
- */
- public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
- super(null, uuid, 0, properties, permissions);
- }
-
- /**
- * Adds a descriptor to this characteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param descriptor Descriptor to be added to this characteristic.
- */
- public void addDescriptor(MutableBluetoothGattDescriptor descriptor) {
- mDescriptors.add(descriptor);
- descriptor.setCharacteristic(this);
- }
-
- /**
- * Set the desired key size.
- * @hide
- */
- public void setKeySize(int keySize) {
- mKeySize = keySize;
- }
-
- /**
- * Sets the service associated with this device.
- * @hide
- */
- /*package*/ void setService(BluetoothGattService service) {
- mService = service;
- }
-}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java
deleted file mode 100644
index e455392..0000000
--- a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 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 java.util.UUID;
-
-/**
- * Mutable variant of a Bluetooth Gatt Descriptor
- * @hide
- */
-public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor {
-
- /**
- * Create a new BluetoothGattDescriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param uuid The UUID for this descriptor
- * @param permissions Permissions for this descriptor
- */
- public MutableBluetoothGattDescriptor(UUID uuid, int permissions) {
- super(null, uuid, permissions);
- }
-
- /**
- * Set the back-reference to the associated characteristic
- * @hide
- */
- /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
- mCharacteristic = characteristic;
- }
-}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattService.java b/core/java/android/bluetooth/MutableBluetoothGattService.java
deleted file mode 100644
index 927f5ab..0000000
--- a/core/java/android/bluetooth/MutableBluetoothGattService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2013 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.bluetooth.BluetoothDevice;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth Gatt Service
- * @hide
- */
-public class MutableBluetoothGattService extends BluetoothGattService {
-
- /**
- * Create a new MutableBluetoothGattService.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param uuid The UUID for this service
- * @param serviceType The type of this service (primary/secondary)
- */
- public MutableBluetoothGattService(UUID uuid, int serviceType) {
- super(uuid, serviceType);
- }
-
- /**
- * Add an included service to this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param service The service to be added
- * @return true, if the included service was added to the service
- */
- public boolean addService(BluetoothGattService service) {
- mIncludedServices.add(service);
- return true;
- }
-
- /**
- * Add a characteristic to this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param characteristic The characteristics to be added
- * @return true, if the characteristic was added to the service
- */
- public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) {
- mCharacteristics.add(characteristic);
- characteristic.setService(this);
- return true;
- }
-
- /**
- * Force the instance ID.
- * This is needed for conformance testing only.
- * @hide
- */
- public void setInstanceId(int instanceId) {
- mInstanceId = instanceId;
- }
-
- /**
- * Force the number of handles to reserve for this service.
- * This is needed for conformance testing only.
- * @hide
- */
- public void setHandles(int handles) {
- mHandles = handles;
- }
-}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7dd76cd..ef9b0bf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2203,7 +2203,6 @@
* {@link android.bluetooth.BluetoothAdapter} for using Bluetooth.
*
* @see #getSystemService
- * @hide
*/
public static final String BLUETOOTH_SERVICE = "bluetooth";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9afbe6f..da15e3b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2862,16 +2862,16 @@
/**
* @deprecated This function no longer does anything; it was an old
- * approach to managing preferred activities, which has been superceeded
- * (and conflicts with) the modern activity-based preferences.
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
*/
@Deprecated
public abstract void addPackageToPreferred(String packageName);
/**
* @deprecated This function no longer does anything; it was an old
- * approach to managing preferred activities, which has been superceeded
- * (and conflicts with) the modern activity-based preferences.
+ * approach to managing preferred activities, which has been superseded
+ * by (and conflicts with) the modern activity-based preferences.
*/
@Deprecated
public abstract void removePackageFromPreferred(String packageName);
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index c0d2fae..7f94794 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -572,7 +572,10 @@
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}
- * or, the desired delay between events in microsecond.
+ * or, the desired delay between events in microseconds.
+ * Specifying the delay in microseconds only works from Android
+ * 2.3 (API level 9) onwards. For earlier releases, you must use
+ * one of the {@code SENSOR_DELAY_*} constants.
*
* @return <code>true</code> if the sensor is supported and successfully
* enabled.
@@ -604,7 +607,10 @@
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
- * or, the desired delay between events in microsecond.
+ * or, the desired delay between events in microseconds.
+ * Specifying the delay in microseconds only works from Android
+ * 2.3 (API level 9) onwards. For earlier releases, you must use
+ * one of the {@code SENSOR_DELAY_*} constants.
*
* @param handler
* The {@link android.os.Handler Handler} the
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 90bcf1c..66083c8 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1359,7 +1359,7 @@
* status definitions. Automatically computed as the highest presence of all
* constituent raw contacts. The provider may choose not to store this value
* in persistent storage. The expectation is that presence status will be
- * updated on a regular basic.</td>
+ * updated on a regular basis.</td>
* </tr>
* <tr>
* <td>String</td>
@@ -4181,7 +4181,7 @@
* all IM rows. See {@link StatusUpdates} for individual status definitions.
* The provider may choose not to store this value
* in persistent storage. The expectation is that presence status will be
- * updated on a regular basic.
+ * updated on a regular basis.
* </td>
* </tr>
* <tr>
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index a890d9b..c365643 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -427,6 +427,23 @@
}
return _result;
}
+
+ @Override
+ public int is_hardware_backed() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_is_hardware_backed, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
}
private static final String DESCRIPTOR = "android.security.keystore";
@@ -452,6 +469,7 @@
static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18;
static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19;
static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
+ static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21;
/**
* Cast an IBinder object into an IKeystoreService interface, generating
@@ -539,4 +557,6 @@
public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
throws RemoteException;
+
+ public int is_hardware_backed() throws RemoteException;
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 9051285..1291279 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -340,7 +340,7 @@
w += widths[j - paraStart];
}
- boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB;
+ boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB || c == CHAR_ZWSP;
if (w <= width || isSpaceOrTab) {
fitWidth = w;
@@ -956,6 +956,7 @@
private static final char CHAR_SPACE = ' ';
private static final char CHAR_SLASH = '/';
private static final char CHAR_HYPHEN = '-';
+ private static final char CHAR_ZWSP = '\u200B';
private static final double EXTRA_ROUNDING = 0.5;
diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java
index 470526c..74942ba 100644
--- a/core/java/android/util/AttributeSet.java
+++ b/core/java/android/util/AttributeSet.java
@@ -151,7 +151,7 @@
* Return the value of 'attribute' as a resource identifier.
*
* <p>Note that this is different than {@link #getAttributeNameResource}
- * in that it returns a the value contained in this attribute as a
+ * in that it returns the value contained in this attribute as a
* resource identifier (i.e., a value originally of the form
* "@package:type/resource"); the other method returns a resource
* identifier that identifies the name of the attribute.
@@ -230,7 +230,7 @@
* Return the value of attribute at 'index' as a resource identifier.
*
* <p>Note that this is different than {@link #getAttributeNameResource}
- * in that it returns a the value contained in this attribute as a
+ * in that it returns the value contained in this attribute as a
* resource identifier (i.e., a value originally of the form
* "@package:type/resource"); the other method returns a resource
* identifier that identifies the name of the attribute.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 987ff785..40b6a08 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1054,8 +1054,7 @@
String categoryPrefix =
property.category().length() != 0 ? property.category() + ":" : "";
- if (type == int.class) {
-
+ if (type == int.class || type == byte.class) {
if (property.resolveId() && context != null) {
final int id = field.getInt(view);
fieldValue = resolveId(context, id);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index dd36022..725502d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5882,18 +5882,40 @@
*/
public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
- // Layout direction is LTR by default
- private int mLayoutDirection = LAYOUT_DIRECTION_LTR;
+ /**
+ * Bit 0: layout direction
+ * Bit 1: layout direction
+ * Bit 2: left margin undefined
+ * Bit 3: right margin undefined
+ * Bit 4: is RTL compatibility mode
+ * Bit 5: need resolution
+ *
+ * Bit 6 to 7 not used
+ *
+ * @hide
+ */
+ @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
+ @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
+ equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
+ @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
+ equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
+ @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
+ equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
+ @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
+ equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
+ @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
+ equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
+ })
+ byte mMarginFlags;
- private static int DEFAULT_MARGIN_RESOLVED = 0;
+ private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
+ private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
+ private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
+ private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
+ private static final int NEED_RESOLUTION_MASK = 0x00000020;
- private boolean mNeedResolution = false;
- private boolean mIsRtlCompatibilityMode = true;
-
- private static int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
-
- private boolean mLeftMarginUndefined = false;
- private boolean mRightMarginUndefined = false;
+ private static final int DEFAULT_MARGIN_RESOLVED = 0;
+ private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
/**
* Creates a new set of layout parameters. The values are extracted from
@@ -5923,14 +5945,14 @@
R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
UNDEFINED_MARGIN);
if (leftMargin == UNDEFINED_MARGIN) {
- mLeftMarginUndefined = true;
+ mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
leftMargin = DEFAULT_MARGIN_RESOLVED;
}
rightMargin = a.getDimensionPixelSize(
R.styleable.ViewGroup_MarginLayout_layout_marginRight,
UNDEFINED_MARGIN);
if (rightMargin == UNDEFINED_MARGIN) {
- mRightMarginUndefined = true;
+ mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
rightMargin = DEFAULT_MARGIN_RESOLVED;
}
@@ -5948,12 +5970,19 @@
R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
DEFAULT_MARGIN_RELATIVE);
- mNeedResolution = isMarginRelative();
+ if (isMarginRelative()) {
+ mMarginFlags |= NEED_RESOLUTION_MASK;
+ }
}
final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
- mIsRtlCompatibilityMode = targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport;
+ if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
+ mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
+ }
+
+ // Layout direction is LTR by default
+ mMarginFlags |= LAYOUT_DIRECTION_LTR;
a.recycle();
}
@@ -5964,11 +5993,11 @@
public MarginLayoutParams(int width, int height) {
super(width, height);
- mLeftMarginUndefined = true;
- mRightMarginUndefined = true;
+ mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
+ mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
- mNeedResolution = false;
- mIsRtlCompatibilityMode = false;
+ mMarginFlags &= ~NEED_RESOLUTION_MASK;
+ mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
}
/**
@@ -5987,13 +6016,7 @@
this.startMargin = source.startMargin;
this.endMargin = source.endMargin;
- this.mLeftMarginUndefined = source.mLeftMarginUndefined;
- this.mRightMarginUndefined = source.mRightMarginUndefined;
-
- this.mNeedResolution = source.mNeedResolution;
- this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
-
- setLayoutDirection(source.mLayoutDirection);
+ this.mMarginFlags = source.mMarginFlags;
}
/**
@@ -6002,11 +6025,11 @@
public MarginLayoutParams(LayoutParams source) {
super(source);
- mLeftMarginUndefined = true;
- mRightMarginUndefined = true;
+ mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
+ mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
- mNeedResolution = false;
- mIsRtlCompatibilityMode = false;
+ mMarginFlags &= ~NEED_RESOLUTION_MASK;
+ mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
}
/**
@@ -6029,9 +6052,13 @@
topMargin = top;
rightMargin = right;
bottomMargin = bottom;
- mLeftMarginUndefined = false;
- mRightMarginUndefined = false;
- mNeedResolution = isMarginRelative();
+ mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
+ mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
+ if (isMarginRelative()) {
+ mMarginFlags |= NEED_RESOLUTION_MASK;
+ } else {
+ mMarginFlags &= ~NEED_RESOLUTION_MASK;
+ }
}
/**
@@ -6057,7 +6084,7 @@
topMargin = top;
endMargin = end;
bottomMargin = bottom;
- mNeedResolution = true;
+ mMarginFlags |= NEED_RESOLUTION_MASK;
}
/**
@@ -6069,7 +6096,7 @@
*/
public void setMarginStart(int start) {
startMargin = start;
- mNeedResolution = true;
+ mMarginFlags |= NEED_RESOLUTION_MASK;
}
/**
@@ -6081,10 +6108,10 @@
*/
public int getMarginStart() {
if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
- if (mNeedResolution) {
+ if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
doResolveMargins();
}
- switch(mLayoutDirection) {
+ switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
case View.LAYOUT_DIRECTION_RTL:
return rightMargin;
case View.LAYOUT_DIRECTION_LTR:
@@ -6102,7 +6129,7 @@
*/
public void setMarginEnd(int end) {
endMargin = end;
- mNeedResolution = true;
+ mMarginFlags |= NEED_RESOLUTION_MASK;
}
/**
@@ -6114,10 +6141,10 @@
*/
public int getMarginEnd() {
if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
- if (mNeedResolution) {
+ if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
doResolveMargins();
}
- switch(mLayoutDirection) {
+ switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
case View.LAYOUT_DIRECTION_RTL:
return leftMargin;
case View.LAYOUT_DIRECTION_LTR:
@@ -6147,9 +6174,14 @@
public void setLayoutDirection(int layoutDirection) {
if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
- if (layoutDirection != this.mLayoutDirection) {
- this.mLayoutDirection = layoutDirection;
- this.mNeedResolution = isMarginRelative();
+ if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
+ mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
+ mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
+ if (isMarginRelative()) {
+ mMarginFlags |= NEED_RESOLUTION_MASK;
+ } else {
+ mMarginFlags &= ~NEED_RESOLUTION_MASK;
+ }
}
}
@@ -6160,7 +6192,7 @@
* @return the layout direction.
*/
public int getLayoutDirection() {
- return mLayoutDirection;
+ return (mMarginFlags & LAYOUT_DIRECTION_MASK);
}
/**
@@ -6173,28 +6205,30 @@
// No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
// Will use the left and right margins if no relative margin is defined.
- if (!isMarginRelative() || !mNeedResolution) return;
+ if (!isMarginRelative() ||
+ (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
// Proceed with resolution
doResolveMargins();
}
private void doResolveMargins() {
-
- if (mIsRtlCompatibilityMode) {
+ if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
// if left or right margins are not defined and if we have some start or end margin
// defined then use those start and end margins.
- if (mLeftMarginUndefined && startMargin > DEFAULT_MARGIN_RELATIVE) {
+ if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
+ && startMargin > DEFAULT_MARGIN_RELATIVE) {
leftMargin = startMargin;
}
- if (mRightMarginUndefined && endMargin > DEFAULT_MARGIN_RELATIVE) {
+ if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
+ && endMargin > DEFAULT_MARGIN_RELATIVE) {
rightMargin = endMargin;
}
} else {
// We have some relative margins (either the start one or the end one or both). So use
// them and override what has been defined for left and right margins. If either start
// or end margin is not defined, just set it to default "0".
- switch(mLayoutDirection) {
+ switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
case View.LAYOUT_DIRECTION_RTL:
leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
endMargin : DEFAULT_MARGIN_RESOLVED;
@@ -6210,14 +6244,14 @@
break;
}
}
- mNeedResolution = false;
+ mMarginFlags &= ~NEED_RESOLUTION_MASK;
}
/**
* @hide
*/
public boolean isLayoutRtl() {
- return (mLayoutDirection == View.LAYOUT_DIRECTION_RTL);
+ return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7b34ce1..7790f92 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -258,12 +258,12 @@
QueuedInputEvent mPendingInputEventHead;
QueuedInputEvent mPendingInputEventTail;
int mPendingInputEventCount;
- QueuedInputEvent mCurrentInputEventHead;
- QueuedInputEvent mCurrentInputEventTail;
- int mCurrentInputEventCount;
+ QueuedInputEvent mActiveInputEventHead;
+ QueuedInputEvent mActiveInputEventTail;
+ int mActiveInputEventCount;
boolean mProcessInputEventsScheduled;
String mPendingInputEventQueueLengthCounterName = "pq";
- String mCurrentInputEventQueueLengthCounterName = "cq";
+ String mActiveInputEventQueueLengthCounterName = "aq";
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -661,7 +661,7 @@
}
mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
- mCurrentInputEventQueueLengthCounterName = "cq:" + attrs.getTitle();
+ mActiveInputEventQueueLengthCounterName = "aq:" + attrs.getTitle();
}
}
}
@@ -4443,7 +4443,7 @@
void doProcessInputEvents() {
// Handle all of the available pending input events. Currently this will immediately
// process all of the events it can until it encounters one that must go through the IME.
- // After that it will continue adding events to the current input queue but will wait for a
+ // After that it will continue adding events to the active input queue but will wait for a
// response from the IME, regardless of whether that particular event needs it or not, in
// order to guarantee ordering consistency. This could be slightly improved by only
// queueing events whose source has previously encountered something that needs to be
@@ -4466,18 +4466,18 @@
if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) {
finishInputEvent(q, result == EVENT_HANDLED);
} else if (result == EVENT_PENDING_IME) {
- enqueueCurrentInputEvent(q);
+ enqueueActiveInputEvent(q);
} else {
q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
// If the IME decided not to handle this event, and we have no events already being
// handled by the IME, go ahead and handle this one and then continue to the next
// input event. Otherwise, queue it up and handle it after whatever in front of it
// in the queue has been handled.
- if (mCurrentInputEventHead == null) {
+ if (mActiveInputEventHead == null) {
result = deliverInputEventPostIme(q);
finishInputEvent(q, result == EVENT_HANDLED);
} else {
- enqueueCurrentInputEvent(q);
+ enqueueActiveInputEvent(q);
}
}
}
@@ -4490,29 +4490,54 @@
}
}
- private void enqueueCurrentInputEvent(QueuedInputEvent q) {
- if (mCurrentInputEventHead == null) {
- mCurrentInputEventHead = q;
- mCurrentInputEventTail = q;
+ private void enqueueActiveInputEvent(QueuedInputEvent q) {
+ if (mActiveInputEventHead == null) {
+ mActiveInputEventHead = q;
+ mActiveInputEventTail = q;
} else {
- mCurrentInputEventTail.mNext = q;
- mCurrentInputEventTail = q;
+ mActiveInputEventTail.mNext = q;
+ mActiveInputEventTail = q;
}
- mCurrentInputEventCount += 1;
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, mCurrentInputEventQueueLengthCounterName,
- mCurrentInputEventCount);
+ mActiveInputEventCount += 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
+ mActiveInputEventCount);
}
- private QueuedInputEvent dequeueCurrentInputEvent() {
- QueuedInputEvent q = mCurrentInputEventHead;
- mCurrentInputEventHead = q.mNext;
- if (mCurrentInputEventHead == null) {
- mCurrentInputEventTail = null;
+ private QueuedInputEvent dequeueActiveInputEvent() {
+ return dequeueActiveInputEvent(mActiveInputEventHead);
+ }
+
+
+ private QueuedInputEvent dequeueActiveInputEvent(QueuedInputEvent q) {
+ QueuedInputEvent curr = mActiveInputEventHead;
+ QueuedInputEvent prev = null;
+ while (curr != null && curr != q) {
+ prev = curr;
+ curr = curr.mNext;
}
- q.mNext = null;
- mCurrentInputEventCount -= 1;
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, mCurrentInputEventQueueLengthCounterName,
- mCurrentInputEventCount);
+ if (curr != null) {
+ if (mActiveInputEventHead == curr) {
+ mActiveInputEventHead = curr.mNext;
+ } else {
+ prev.mNext = curr.mNext;
+ }
+ if (mActiveInputEventTail == curr) {
+ mActiveInputEventTail = prev;
+ }
+ curr.mNext = null;
+
+ mActiveInputEventCount -= 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
+ mActiveInputEventCount);
+ }
+ return curr;
+ }
+
+ private QueuedInputEvent findActiveInputEvent(int seq) {
+ QueuedInputEvent q = mActiveInputEventHead;
+ while (q != null && q.mEvent.getSequenceNumber() != seq) {
+ q = q.mNext;
+ }
return q;
}
@@ -4532,36 +4557,35 @@
}
void handleImeFinishedEvent(int seq, boolean handled) {
- QueuedInputEvent q = mCurrentInputEventHead;
- if (q != null && q.mEvent.getSequenceNumber() == seq) {
- dequeueCurrentInputEvent();
+ QueuedInputEvent q = findActiveInputEvent(seq);
+ if (q != null) {
if (DEBUG_IMF) {
Log.v(TAG, "IME finished event: seq=" + seq
+ " handled=" + handled + " event=" + q);
}
- if (!handled) {
+ if (handled) {
+ dequeueActiveInputEvent(q);
+ finishInputEvent(q, true);
+ } else {
+ q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
+ }
+
+
+ // Flush all of the input events that are no longer waiting on the IME
+ while (mActiveInputEventHead != null && (mActiveInputEventHead.mFlags &
+ QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
+ q = dequeueActiveInputEvent();
// If the window doesn't currently have input focus, then drop
// this event. This could be an event that came back from the
// IME dispatch but the window has lost focus in the meantime.
+ handled = false;
if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
} else {
- final int result = deliverInputEventPostIme(q);
- if (result == EVENT_HANDLED) {
- handled = true;
- }
+ handled = (deliverInputEventPostIme(q) == EVENT_HANDLED);
}
- }
-
- finishInputEvent(q, handled);
-
- // Flush all of the input events that are no longer waiting on the IME
- while (mCurrentInputEventHead != null && (mCurrentInputEventHead.mFlags &
- QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
- q = dequeueCurrentInputEvent();
- final int result = deliverInputEventPostIme(q);
- finishInputEvent(q, result == EVENT_HANDLED);
+ finishInputEvent(q, handled);
}
} else {
if (DEBUG_IMF) {
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index e711b94..9c00b7f 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -468,7 +468,8 @@
// Force reloading the image resource
sc.icon.setImageDrawable(null);
sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
- if (sc.streamType == AudioManager.STREAM_RING &&
+ if (((sc.streamType == AudioManager.STREAM_RING) ||
+ (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
}
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index c33f038..8ebe94c 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -143,7 +143,8 @@
mLocalHandler = new WeakReference<Handler>(handler);
}
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
Handler handler = mLocalHandler.get();
if (handler != null) {
handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 90161fd..5454c08 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -383,7 +383,8 @@
return descent - ascent + leading;
}
- static jfloat measureText_CII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count) {
+ static jfloat measureText_CIII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count,
+ jint bidiFlags) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -401,13 +402,14 @@
jfloat result = 0;
TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength,
- paint->getFlags(), NULL /* dont need all advances */, &result);
+ bidiFlags, NULL /* dont need all advances */, &result);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
return result;
}
- static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) {
+ static jfloat measureText_StringIII(JNIEnv* env, jobject jpaint, jstring text, int start, int end,
+ jint bidiFlags) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -426,13 +428,13 @@
jfloat width = 0;
TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength,
- paint->getFlags(), NULL /* dont need all advances */, &width);
+ bidiFlags, NULL /* dont need all advances */, &width);
env->ReleaseStringChars(text, textArray);
return width;
}
- static jfloat measureText_String(JNIEnv* env, jobject jpaint, jstring text) {
+ static jfloat measureText_StringI(JNIEnv* env, jobject jpaint, jstring text, jint bidiFlags) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -446,13 +448,14 @@
jfloat width = 0;
TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength,
- paint->getFlags(), NULL /* dont need all advances */, &width);
+ bidiFlags, NULL /* dont need all advances */, &width);
env->ReleaseStringChars(text, textArray);
return width;
}
- static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths) {
+ static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths,
+ jint bidiFlags) {
NPE_CHECK_RETURN_ZERO(env, paint);
NPE_CHECK_RETURN_ZERO(env, text);
@@ -473,23 +476,24 @@
jfloat* widthsArray = autoWidths.ptr();
TextLayout::getTextRunAdvances(paint, text, 0, count, count,
- paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */);
+ bidiFlags, widthsArray, NULL /* dont need totalAdvance */);
return count;
}
- static int getTextWidths___CII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloatArray widths) {
+ static int getTextWidths___CIII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text,
+ int index, int count, jint bidiFlags, jfloatArray widths) {
const jchar* textArray = env->GetCharArrayElements(text, NULL);
- count = dotextwidths(env, paint, textArray + index, count, widths);
+ count = dotextwidths(env, paint, textArray + index, count, widths, bidiFlags);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
JNI_ABORT);
return count;
}
- static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
- int start, int end, jfloatArray widths) {
+ static int getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
+ int start, int end, jint bidiFlags, jfloatArray widths) {
const jchar* textArray = env->GetStringChars(text, NULL);
- int count = dotextwidths(env, paint, textArray + start, end - start, widths);
+ int count = dotextwidths(env, paint, textArray + start, end - start, widths, bidiFlags);
env->ReleaseStringChars(text, textArray);
return count;
}
@@ -685,10 +689,10 @@
}
static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[],
- int count, float maxWidth, jfloatArray jmeasured,
+ int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
SkPaint::TextBufferDirection tbd) {
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
- text, 0, count, count, paint.getFlags());
+ text, 0, count, count, bidiFlags);
if (value == NULL) {
return 0;
}
@@ -706,7 +710,7 @@
}
static int breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext,
- int index, int count, float maxWidth, jfloatArray jmeasuredWidth) {
+ int index, int count, float maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, jtext);
@@ -727,14 +731,14 @@
SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
const jchar* text = env->GetCharArrayElements(jtext, NULL);
count = breakText(env, *paint, text + index, count, maxWidth,
- jmeasuredWidth, tbd);
+ bidiFlags, jmeasuredWidth, tbd);
env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
JNI_ABORT);
return count;
}
static int breakTextS(JNIEnv* env, jobject jpaint, jstring jtext,
- bool forwards, float maxWidth, jfloatArray jmeasuredWidth) {
+ bool forwards, float maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
NPE_CHECK_RETURN_ZERO(env, jpaint);
NPE_CHECK_RETURN_ZERO(env, jtext);
@@ -745,22 +749,20 @@
SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
int count = env->GetStringLength(jtext);
const jchar* text = env->GetStringChars(jtext, NULL);
- count = breakText(env, *paint, text, count, maxWidth,
- jmeasuredWidth, tbd);
+ count = breakText(env, *paint, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd);
env->ReleaseStringChars(jtext, text);
return count;
}
static void doTextBounds(JNIEnv* env, const jchar* text, int count,
- jobject bounds, const SkPaint& paint)
- {
+ jobject bounds, const SkPaint& paint, jint bidiFlags) {
SkRect r;
r.set(0,0,0,0);
SkIRect ir;
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
- text, 0, count, count, paint.getFlags());
+ text, 0, count, count, bidiFlags);
if (value == NULL) {
return;
}
@@ -770,18 +772,16 @@
}
static void getStringBounds(JNIEnv* env, jobject, const SkPaint* paint,
- jstring text, int start, int end, jobject bounds)
- {
+ jstring text, int start, int end, jint bidiFlags, jobject bounds) {
const jchar* textArray = env->GetStringChars(text, NULL);
- doTextBounds(env, textArray + start, end - start, bounds, *paint);
+ doTextBounds(env, textArray + start, end - start, bounds, *paint, bidiFlags);
env->ReleaseStringChars(text, textArray);
}
static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint,
- jcharArray text, int index, int count, jobject bounds)
- {
+ jcharArray text, int index, int count, jint bidiFlags, jobject bounds) {
const jchar* textArray = env->GetCharArrayElements(text, NULL);
- doTextBounds(env, textArray + index, count, bounds, *paint);
+ doTextBounds(env, textArray + index, count, bounds, *paint, bidiFlags);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
JNI_ABORT);
}
@@ -841,13 +841,13 @@
{"descent","()F", (void*) SkPaintGlue::descent},
{"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics},
{"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt},
- {"native_measureText","([CII)F", (void*) SkPaintGlue::measureText_CII},
- {"native_measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String},
- {"native_measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII},
- {"native_breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC},
- {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
- {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
- {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
+ {"native_measureText","([CIII)F", (void*) SkPaintGlue::measureText_CIII},
+ {"native_measureText","(Ljava/lang/String;I)F", (void*) SkPaintGlue::measureText_StringI},
+ {"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII},
+ {"native_breakText","([CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
+ {"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
+ {"native_getTextWidths","(I[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F},
+ {"native_getTextWidths","(ILjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F},
{"native_getTextRunAdvances","(I[CIIIII[FI)F",
(void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI},
{"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
@@ -861,9 +861,9 @@
(void*) SkPaintGlue::getTextRunCursor__String},
{"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
{"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
- {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V",
+ {"nativeGetStringBounds", "(ILjava/lang/String;IIILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getStringBounds },
- {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
+ {"nativeGetCharArrayBounds", "(I[CIIILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getCharArrayBounds },
{"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
};
diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml
index edf6ee6..bf46af8 100644
--- a/core/res/res/layout-xlarge/activity_list.xml
+++ b/core/res/res/layout-xlarge/activity_list.xml
@@ -52,7 +52,8 @@
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
</LinearLayout>
<View android:id="@+id/titleDivider"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 35552d3..59e56af 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -55,7 +55,8 @@
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
</LinearLayout>
<ImageView android:id="@+id/titleDivider"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml
index 3f1fa3c..34cb21d 100644
--- a/core/res/res/layout/alert_dialog_holo.xml
+++ b/core/res/res/layout/alert_dialog_holo.xml
@@ -53,7 +53,8 @@
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
</LinearLayout>
<View android:id="@+id/titleDivider"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/wifi_p2p_dialog_row.xml b/core/res/res/layout/wifi_p2p_dialog_row.xml
index ef810a0..2c88b10 100644
--- a/core/res/res/layout/wifi_p2p_dialog_row.xml
+++ b/core/res/res/layout/wifi_p2p_dialog_row.xml
@@ -23,5 +23,6 @@
<TextView
android:id="@+id/value"
style="@style/wifi_item_content"
- android:textStyle="bold" />
+ android:textStyle="bold"
+ android:textAlignment="viewStart" />
</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5282df3..8c7a374 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2092,11 +2092,11 @@
<!-- Don't use a layer. -->
<enum name="none" value="0" />
<!-- Use a software layer. Refer to
- {@link android.view.View#setLayerType(int, android.graphics.Paint) for
+ {@link android.view.View#setLayerType(int, android.graphics.Paint)} for
more information. -->
<enum name="software" value="1" />
<!-- Use a hardware layer. Refer to
- {@link android.view.View#setLayerType(int, android.graphics.Paint) for
+ {@link android.view.View#setLayerType(int, android.graphics.Paint)} for
more information. -->
<enum name="hardware" value="2" />
</attr>
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 999ddc4..744b15d 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -78,17 +78,50 @@
</family>
<family>
<fileset>
- <file>AnjaliNewLipi-light.ttf</file>
+ <file variant="elegant">NotoSansMalayalam-Regular.ttf</file>
+ <file variant="elegant">NotoSansMalayalam-Bold.ttf</file>
</fileset>
</family>
<family>
<fileset>
- <file>Lohit-Bengali.ttf</file>
+ <file variant="compact">NotoSansMalayalamUI-Regular.ttf</file>
+ <file variant="compact">NotoSansMalayalamUI-Bold.ttf</file>
</fileset>
</family>
<family>
<fileset>
- <file>Lohit-Kannada.ttf</file>
+ <file variant="elegant">NotoSansBengali-Regular.ttf</file>
+ <file variant="elegant">NotoSansBengali-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file variant="compact">NotoSansBengaliUI-Regular.ttf</file>
+ <file variant="compact">NotoSansBengaliUI-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file variant="elegant">NotoSansTelugu-Regular.ttf</file>
+ <file variant="elegant">NotoSansTelugu-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file variant="compact">NotoSansTeluguUI-Regular.ttf</file>
+ <file variant="compact">NotoSansTeluguUI-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file variant="elegant">NotoSansKannada-Regular.ttf</file>
+ <file variant="elegant">NotoSansKannada-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file variant="compact">NotoSansKannadaUI-Regular.ttf</file>
+ <file variant="compact">NotoSansKannadaUI-Bold.ttf</file>
</fileset>
</family>
<family>
@@ -103,11 +136,6 @@
</family>
<family>
<fileset>
- <file>Lohit-Telugu.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
<file>DroidSansFallback.ttf</file>
</fileset>
</family>
diff --git a/docs/html/google/gcm/gcm.jd b/docs/html/google/gcm/gcm.jd
index f8ba7b4..089baf5 100644
--- a/docs/html/google/gcm/gcm.jd
+++ b/docs/html/google/gcm/gcm.jd
@@ -777,10 +777,11 @@
<p class="note"><strong>Note:</strong> If your organization has a firewall
that restricts the traffic to or
-from the Internet, you need to configure it to allow connectivity with GCM.
+from the Internet, you need to configure it to allow connectivity with GCM in order for
+your Android devices to receive messages.
The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but
it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs, so you should
-allow your server to accept incoming connections from all IP addresses
+allow your firewall to accept incoming connections from all IP addresses
contained in the IP blocks listed in Google's ASN of 15169.</p>
diff --git a/docs/html/tools/devices/emulator.jd b/docs/html/tools/devices/emulator.jd
index cee6473..bae3985 100644
--- a/docs/html/tools/devices/emulator.jd
+++ b/docs/html/tools/devices/emulator.jd
@@ -1254,7 +1254,7 @@
<td> </td>
</tr>
<tr>
- <td><code>power health <percent></code></td>
+ <td><code>capacity <percent></code></td>
<td>Set remaining battery capacity state (0-100).</td>
<td> </td>
</tr>
diff --git a/docs/html/tools/testing/testing_otheride.jd b/docs/html/tools/testing/testing_otheride.jd
index 0678f52..9484158 100644
--- a/docs/html/tools/testing/testing_otheride.jd
+++ b/docs/html/tools/testing/testing_otheride.jd
@@ -75,9 +75,9 @@
<p>
You use the <code>android</code> tool to create test projects.
You also use <code>android</code> to convert existing test code into an Android test project,
- or to add the <code>run-tests</code> Ant target to an existing Android test project.
+ or to add the <code>test</code> Ant target to an existing Android test project.
These operations are described in more detail in the section <a href="#UpdateTestProject">
- Updating a test project</a>. The <code>run-tests</code> target is described in
+ Updating a test project</a>. The <code>test</code> target is described in
<a href="#RunTestsAnt">Quick build and run with Ant</a>.
</p>
<h3 id="CreateTestProject">Creating a test project</h3>
@@ -300,7 +300,7 @@
<h3 id="RunTestsAnt">Quick build and run with Ant</h3>
<p>
You can use Ant to run all the tests in your test project, using the target
- <code>run-tests</code>, which is created automatically when you create a test project with
+ <code>test</code>, which is created automatically when you create a test project with
the <code>android</code> tool.
</p>
<p>
diff --git a/docs/html/training/basics/data-storage/databases.jd b/docs/html/training/basics/data-storage/databases.jd
index 9976bb1..61fb758 100644
--- a/docs/html/training/basics/data-storage/databases.jd
+++ b/docs/html/training/basics/data-storage/databases.jd
@@ -284,7 +284,7 @@
// Define 'where' part of query.
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
-String[] selelectionArgs = { String.valueOf(rowId) };
+String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);
</pre>
@@ -309,7 +309,7 @@
// Which row to update, based on the ID
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
-String[] selelectionArgs = { String.valueOf(rowId) };
+String[] selectionArgs = { String.valueOf(rowId) };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java
index 4f16dc4..2bdecce 100644
--- a/graphics/java/android/graphics/DashPathEffect.java
+++ b/graphics/java/android/graphics/DashPathEffect.java
@@ -26,7 +26,7 @@
* controls the length of the dashes. The paint's strokeWidth controls the
* thickness of the dashes.
* Note: this patheffect only affects drawing with the paint's style is set
- * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
+ * to STROKE or FILL_AND_STROKE. It is ignored if the drawing is done with
* style == FILL.
* @param intervals array of ON and OFF distances
* @param phase offset into the intervals array
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index d5cee3a..1485d8d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1295,17 +1295,17 @@
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_measureText(text, index, count));
+ return (float) Math.ceil(native_measureText(text, index, count, mBidiFlags));
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- float w = native_measureText(text, index, count);
+ float w = native_measureText(text, index, count, mBidiFlags);
setTextSize(oldSize);
return (float) Math.ceil(w*mInvCompatScaling);
}
- private native float native_measureText(char[] text, int index, int count);
+ private native float native_measureText(char[] text, int index, int count, int bidiFlags);
/**
* Return the width of the text.
@@ -1327,17 +1327,17 @@
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_measureText(text, start, end));
+ return (float) Math.ceil(native_measureText(text, start, end, mBidiFlags));
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- float w = native_measureText(text, start, end);
+ float w = native_measureText(text, start, end, mBidiFlags);
setTextSize(oldSize);
return (float) Math.ceil(w*mInvCompatScaling);
}
- private native float native_measureText(String text, int start, int end);
+ private native float native_measureText(String text, int start, int end, int bidiFlags);
/**
* Return the width of the text.
@@ -1355,16 +1355,16 @@
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_measureText(text));
+ return (float) Math.ceil(native_measureText(text, mBidiFlags));
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- float w = native_measureText(text);
+ float w = native_measureText(text, mBidiFlags);
setTextSize(oldSize);
return (float) Math.ceil(w*mInvCompatScaling);
}
- private native float native_measureText(String text);
+ private native float native_measureText(String text, int bidiFlags);
/**
* Return the width of the text.
@@ -1431,12 +1431,12 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(text, index, count, maxWidth, measuredWidth);
+ return native_breakText(text, index, count, maxWidth, mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(text, index, count, maxWidth*mCompatScaling,
+ int res = native_breakText(text, index, count, maxWidth*mCompatScaling, mBidiFlags,
measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
@@ -1444,7 +1444,7 @@
}
private native int native_breakText(char[] text, int index, int count,
- float maxWidth, float[] measuredWidth);
+ float maxWidth, int bidiFlags, float[] measuredWidth);
/**
* Measure the text, stopping early if the measured width exceeds maxWidth.
@@ -1521,12 +1521,12 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(text, measureForwards, maxWidth, measuredWidth);
+ return native_breakText(text, measureForwards, maxWidth, mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling,
+ int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling, mBidiFlags,
measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
@@ -1534,7 +1534,7 @@
}
private native int native_breakText(String text, boolean measureForwards,
- float maxWidth, float[] measuredWidth);
+ float maxWidth, int bidiFlags, float[] measuredWidth);
/**
* Return the advance widths for the characters in the string.
@@ -1560,12 +1560,12 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_getTextWidths(mNativePaint, text, index, count, widths);
+ return native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_getTextWidths(mNativePaint, text, index, count, widths);
+ int res = native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths);
setTextSize(oldSize);
for (int i=0; i<res; i++) {
widths[i] *= mInvCompatScaling;
@@ -1642,12 +1642,12 @@
return 0;
}
if (!mHasCompatScaling) {
- return native_getTextWidths(mNativePaint, text, start, end, widths);
+ return native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_getTextWidths(mNativePaint, text, start, end, widths);
+ int res = native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths);
setTextSize(oldSize);
for (int i=0; i<res; i++) {
widths[i] *= mInvCompatScaling;
@@ -2073,7 +2073,7 @@
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nativeGetStringBounds(mNativePaint, text, start, end, bounds);
+ nativeGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds);
}
/**
@@ -2093,7 +2093,7 @@
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds);
+ nativeGetCharArrayBounds(mNativePaint, text, index, count, mBidiFlags, bounds);
}
@Override
@@ -2140,9 +2140,9 @@
String locale);
private static native int native_getTextWidths(int native_object,
- char[] text, int index, int count, float[] widths);
+ char[] text, int index, int count, int bidiFlags, float[] widths);
private static native int native_getTextWidths(int native_object,
- String text, int start, int end, float[] widths);
+ String text, int start, int end, int bidiFlags, float[] widths);
private static native int native_getTextGlyphs(int native_object,
String text, int start, int end, int contextStart, int contextEnd,
@@ -2165,8 +2165,8 @@
private static native void native_getTextPath(int native_object, int bidiFlags,
String text, int start, int end, float x, float y, int path);
private static native void nativeGetStringBounds(int nativePaint,
- String text, int start, int end, Rect bounds);
+ String text, int start, int end, int bidiFlags, Rect bounds);
private static native void nativeGetCharArrayBounds(int nativePaint,
- char[] text, int index, int count, Rect bounds);
+ char[] text, int index, int count, int bidiFlags, Rect bounds);
private static native void finalizer(int nativePaint);
}
diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
index 79a7630..83faf35 100644
--- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
@@ -28,10 +28,28 @@
import javax.security.auth.x500.X500Principal;
/**
- * This provides the required parameters needed for initializing the KeyPair
- * generator that works with
- * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
- * facility</a>.
+ * This provides the required parameters needed for initializing the
+ * {@code KeyPairGenerator} that works with <a href="{@docRoot}
+ * guide/topics/security/keystore.html">Android KeyStore facility</a>. The
+ * Android KeyStore facility is accessed through a
+ * {@link java.security.KeyPairGenerator} API using the
+ * {@code AndroidKeyPairGenerator} provider. The {@code context} passed in may
+ * be used to pop up some UI to ask the user to unlock or initialize the Android
+ * keystore facility.
+ * <p>
+ * After generation, the {@code keyStoreAlias} is used with the
+ * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+ * interface to retrieve the {@link PrivateKey} and its associated
+ * {@link Certificate} chain.
+ * <p>
+ * The KeyPair generator will create a self-signed certificate with the subject
+ * as its X.509v3 Subject Distinguished Name and as its X.509v3 Issuer
+ * Distinguished Name along with the other parameters specified with the
+ * {@link Builder}.
+ * <p>
+ * The self-signed certificate may be replaced at a later time by a certificate
+ * signed by a real Certificate Authority.
+ *
* @hide
*/
public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
@@ -74,6 +92,7 @@
* period
* @throws IllegalArgumentException when any argument is {@code null} or
* {@code endDate} is before {@code startDate}.
+ * @hide should be built with AndroidKeyPairGeneratorSpecBuilder
*/
public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) {
@@ -142,4 +161,121 @@
Date getEndDate() {
return mEndDate;
}
+
+ /**
+ * Builder class for {@link AndroidKeyPairGeneratorSpec} objects.
+ * <p>
+ * This will build a parameter spec for use with the <a href="{@docRoot}
+ * guide/topics/security/keystore.html">Android KeyStore facility</a>.
+ * <p>
+ * The required fields must be filled in with the builder.
+ * <p>
+ * Example:
+ *
+ * <pre class="prettyprint">
+ * Calendar start = new Calendar();
+ * Calendar end = new Calendar();
+ * end.add(1, Calendar.YEAR);
+ *
+ * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext)
+ * .setAlias("myKey")
+ * .setSubject(new X500Principal("CN=myKey"))
+ * .setSerial(BigInteger.valueOf(1337))
+ * .setStartDate(start.getTime())
+ * .setEndDate(end.getTime())
+ * .build();
+ * </pre>
+ */
+ public static class Builder {
+ private final Context mContext;
+
+ private String mKeystoreAlias;
+
+ private X500Principal mSubjectDN;
+
+ private BigInteger mSerialNumber;
+
+ private Date mStartDate;
+
+ private Date mEndDate;
+
+ public Builder(Context context) {
+ if (context == null) {
+ throw new NullPointerException("context == null");
+ }
+ mContext = context;
+ }
+
+ /**
+ * Sets the alias to be used to retrieve the key later from a
+ * {@link java.security.KeyStore} instance using the
+ * {@code AndroidKeyStore} provider.
+ */
+ public Builder setAlias(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+ mKeystoreAlias = alias;
+ return this;
+ }
+
+ /**
+ * Sets the subject used for the self-signed certificate of the
+ * generated key pair.
+ */
+ public Builder setSubject(X500Principal subject) {
+ if (subject == null) {
+ throw new NullPointerException("subject == null");
+ }
+ mSubjectDN = subject;
+ return this;
+ }
+
+ /**
+ * Sets the serial number used for the self-signed certificate of the
+ * generated key pair.
+ */
+ public Builder setSerialNumber(BigInteger serialNumber) {
+ if (serialNumber == null) {
+ throw new NullPointerException("serialNumber == null");
+ }
+ mSerialNumber = serialNumber;
+ return this;
+ }
+
+ /**
+ * Sets the start of the validity period for the self-signed certificate
+ * of the generated key pair.
+ */
+ public Builder setStartDate(Date startDate) {
+ if (startDate == null) {
+ throw new NullPointerException("startDate == null");
+ }
+ mStartDate = startDate;
+ return this;
+ }
+
+ /**
+ * Sets the end of the validity period for the self-signed certificate
+ * of the generated key pair.
+ */
+ public Builder setEndDate(Date endDate) {
+ if (endDate == null) {
+ throw new NullPointerException("endDate == null");
+ }
+ mEndDate = endDate;
+ return this;
+ }
+
+ /**
+ * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
+ *
+ * @throws IllegalArgumentException if a required field is missing
+ * @return built instance of {@code AndroidKeyPairGeneratorSpec}
+ */
+ public AndroidKeyPairGeneratorSpec build() {
+ return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
+ mSerialNumber, mStartDate, mEndDate);
+ }
+ }
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 12c0ed8..2037472 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -296,6 +296,15 @@
}
}
+ public boolean isHardwareBacked() {
+ try {
+ return mBinder.is_hardware_backed() == NO_ERROR;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to keystore", e);
+ return false;
+ }
+ }
+
public int getLastError() {
return mError;
}
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
index e6a3750..3d275cd 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
@@ -53,6 +53,26 @@
assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
}
+ public void testBuilder_Success() throws Exception {
+ AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+
+ assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+ assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+ assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+ assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+ assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+ }
+
public void testConstructor_NullContext_Failure() throws Exception {
try {
new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 7f4977a..63bb73f 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -165,10 +165,13 @@
dirtyRect.right, dirtyRect.bottom, !isBlend());
displayList->defer(deferredState, 0);
+
+ deferredUpdateScheduled = false;
+ displayList = NULL;
}
void Layer::flush() {
- if (deferredList && !deferredList->isEmpty()) {
+ if (deferredList) {
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
@@ -183,5 +186,21 @@
}
}
+void Layer::render() {
+ renderer->setViewport(layer.getWidth(), layer.getHeight());
+ renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
+ !isBlend());
+
+ renderer->drawDisplayList(displayList, dirtyRect, DisplayList::kReplayFlag_ClipChildren);
+
+ renderer->finish();
+ renderer = NULL;
+
+ dirtyRect.setEmpty();
+
+ deferredUpdateScheduled = false;
+ displayList = NULL;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 0e00191..27e0cf1 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -275,6 +275,7 @@
void defer();
void flush();
+ void render();
/**
* Bounds of the layer.
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index aeabe366..4a5785c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -540,13 +540,7 @@
}
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
- OpenGLRenderer* renderer = layer->renderer;
- renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
- renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom,
- !layer->isBlend());
- renderer->drawDisplayList(layer->displayList, dirty,
- DisplayList::kReplayFlag_ClipChildren);
- renderer->finish();
+ layer->render();
} else {
layer->defer();
}
@@ -556,13 +550,6 @@
startTiling(mSnapshot);
}
- if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
- dirty.setEmpty();
- layer->renderer = NULL;
- }
-
- layer->deferredUpdateScheduled = false;
- layer->displayList = NULL;
layer->debugDrawUpdate = mCaches.debugLayersUpdates;
return true;
@@ -609,11 +596,12 @@
// Note: it is very important to update the layers in reverse order
for (int i = count - 1; i >= 0; i--) {
sprintf(layerName, "Layer #%d", i);
- startMark(layerName); {
- Layer* layer = mLayerUpdates.itemAt(i);
- layer->flush();
- mCaches.resourceCache.decrementRefcount(layer);
- }
+ startMark(layerName);
+
+ Layer* layer = mLayerUpdates.itemAt(i);
+ layer->flush();
+ mCaches.resourceCache.decrementRefcount(layer);
+
endMark();
}
@@ -626,6 +614,15 @@
void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
if (layer) {
+ // Make sure we don't introduce duplicates.
+ // SortedVector would do this automatically but we need to respect
+ // the insertion order. The linear search is not an issue since
+ // this list is usually very short (typically one item, at most a few)
+ for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
+ if (mLayerUpdates.itemAt(i) == layer) {
+ return;
+ }
+ }
mLayerUpdates.push_back(layer);
mCaches.resourceCache.incrementRefcount(layer);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 38cdb8a..32c1406e 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -166,6 +166,7 @@
private static final int MSG_PROMOTE_RCC = 29;
private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
+ private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -1533,7 +1534,9 @@
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode
if (newModeOwnerPid != 0) {
- disconnectBluetoothSco(newModeOwnerPid);
+ final long ident = Binder.clearCallingIdentity();
+ disconnectBluetoothSco(newModeOwnerPid);
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -3739,6 +3742,10 @@
onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
(IRemoteVolumeObserver)msg.obj /* rvo */);
break;
+ case MSG_RCC_NEW_PLAYBACK_STATE:
+ onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
+ (RccPlaybackState)msg.obj /* newState */);
+ break;
case MSG_SET_RSX_CONNECTION_STATE:
onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
@@ -4999,6 +5006,59 @@
*/
private boolean mHasRemotePlayback;
+ private static class RccPlaybackState {
+ public int mState;
+ public long mPositionMs;
+ public float mSpeed;
+
+ public RccPlaybackState(int state, long positionMs, float speed) {
+ mState = state;
+ mPositionMs = positionMs;
+ mSpeed = speed;
+ }
+
+ public void reset() {
+ mState = RemoteControlClient.PLAYSTATE_STOPPED;
+ mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
+ mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
+ }
+
+ @Override
+ public String toString() {
+ return stateToString() + ", "
+ + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
+ "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
+ + mSpeed + "X";
+ }
+
+ private String stateToString() {
+ switch (mState) {
+ case RemoteControlClient.PLAYSTATE_NONE:
+ return "PLAYSTATE_NONE";
+ case RemoteControlClient.PLAYSTATE_STOPPED:
+ return "PLAYSTATE_STOPPED";
+ case RemoteControlClient.PLAYSTATE_PAUSED:
+ return "PLAYSTATE_PAUSED";
+ case RemoteControlClient.PLAYSTATE_PLAYING:
+ return "PLAYSTATE_PLAYING";
+ case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+ return "PLAYSTATE_FAST_FORWARDING";
+ case RemoteControlClient.PLAYSTATE_REWINDING:
+ return "PLAYSTATE_REWINDING";
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ return "PLAYSTATE_SKIPPING_FORWARDS";
+ case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+ return "PLAYSTATE_SKIPPING_BACKWARDS";
+ case RemoteControlClient.PLAYSTATE_BUFFERING:
+ return "PLAYSTATE_BUFFERING";
+ case RemoteControlClient.PLAYSTATE_ERROR:
+ return "PLAYSTATE_ERROR";
+ default:
+ return "[invalid playstate]";
+ }
+ }
+ }
+
private static class RemoteControlStackEntry {
public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
/**
@@ -5027,7 +5087,7 @@
public int mPlaybackVolumeMax;
public int mPlaybackVolumeHandling;
public int mPlaybackStream;
- public int mPlaybackState;
+ public RccPlaybackState mPlaybackState;
public IRemoteVolumeObserver mRemoteVolumeObs;
public void resetPlaybackInfo() {
@@ -5036,7 +5096,7 @@
mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
mPlaybackStream = AudioManager.STREAM_MUSIC;
- mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
+ mPlaybackState.reset();
mRemoteVolumeObs = null;
}
@@ -6005,21 +6065,6 @@
case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
rcse.mPlaybackStream = value;
break;
- case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
- rcse.mPlaybackState = value;
- synchronized (mMainRemote) {
- if (rccId == mMainRemote.mRccId) {
- mMainRemoteIsActive = isPlaystateActive(value);
- postReevaluateRemote();
- }
- }
- // an RCC moving to a "playing" state should become the media button
- // event receiver so it can be controlled, without requiring the
- // app to re-register its receiver
- if (isPlaystateActive(value)) {
- postPromoteRcc(rccId);
- }
- break;
default:
Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
break;
@@ -6029,7 +6074,45 @@
}//for
} catch (ArrayIndexOutOfBoundsException e) {
// not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+ Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
+ }
+ }
+ }
+
+ public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
+ sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
+ rccId /* arg1 */, state /* arg2 */,
+ new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
+ }
+
+ public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
+ if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
+ + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
+ synchronized(mRCStack) {
+ // iterating from top of stack as playback information changes are more likely
+ // on entries at the top of the remote control stack
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mRccId == rccId) {
+ rcse.mPlaybackState = newState;
+ synchronized (mMainRemote) {
+ if (rccId == mMainRemote.mRccId) {
+ mMainRemoteIsActive = isPlaystateActive(state);
+ postReevaluateRemote();
+ }
+ }
+ // an RCC moving to a "playing" state should become the media button
+ // event receiver so it can be controlled, without requiring the
+ // app to re-register its receiver
+ if (isPlaystateActive(state)) {
+ postPromoteRcc(rccId);
+ }
+ }
+ }//for
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
}
}
}
@@ -6073,7 +6156,7 @@
for (int index = mRCStack.size()-1; index >= 0; index--) {
final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
- && isPlaystateActive(rcse.mPlaybackState)
+ && isPlaystateActive(rcse.mPlaybackState.mState)
&& (rcse.mPlaybackStream == streamType)) {
if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
+ ", vol =" + rcse.mPlaybackVolume);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index efa8089..e21b26b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -159,6 +159,7 @@
oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
+ void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed);
int getRemoteStreamMaxVolume();
int getRemoteStreamVolume();
oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index 204de3c..095cf80 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -40,7 +40,8 @@
void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
boolean clearing);
- void setPlaybackState(int generationId, int state, long stateChangeTimeMs);
+ void setPlaybackState(int generationId, int state, long stateChangeTimeMs, long currentPosMs,
+ float speed);
void setTransportControlFlags(int generationId, int transportControlFlags);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 11f4180..85a32ca 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -328,8 +328,8 @@
* the state. Calling this method in an invalid state transfers the
* object to the <em>Error</em> state. </p></td></tr>
* <tr><td>pause </p></td>
- * <td>{Started, Paused}</p></td>
- * <td>{Idle, Initialized, Prepared, Stopped, PlaybackCompleted, Error}</p></td>
+ * <td>{Started, Paused, PlaybackCompleted}</p></td>
+ * <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
* <td>Successful invoke of this method in a valid state transfers the
* object to the <em>Paused</em> state. Calling this method in an
* invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 9a0ecdf..f186000 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -172,6 +172,17 @@
*/
public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE;
+ /**
+ * @hide
+ * An unknown or invalid playback position value.
+ */
+ public final static long PLAYBACK_POSITION_INVALID = -1;
+ /**
+ * @hide
+ * The default playback speed, 1x.
+ */
+ public final static float PLAYBACK_SPEED_1X = 1.0f;
+
//==========================================
// Public keys for playback information
/**
@@ -208,15 +219,7 @@
public final static int PLAYBACKINFO_USES_STREAM = 5;
//==========================================
- // Private keys for playback information
- /**
- * @hide
- * Used internally to relay playback state (set by the application with
- * {@link #setPlaybackState(int)}) to AudioService
- */
- public final static int PLAYBACKINFO_PLAYSTATE = 255;
-
-
+ // Public flags for the supported transport control capabililities
/**
* Flag indicating a RemoteControlClient makes use of the "previous" media key.
*
@@ -273,6 +276,15 @@
* @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
*/
public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
+ /**
+ * @hide
+ * (to be un-hidden and added in javadoc of setTransportControlFlags(int))
+ * Flag indicating a RemoteControlClient can receive changes in the media playback position
+ * through the {@link #OnPlaybackPositionUpdateListener} interface.
+ *
+ * @see #setTransportControlFlags(int)
+ */
+ public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8;
/**
* @hide
@@ -588,17 +600,54 @@
* {@link #PLAYSTATE_ERROR}.
*/
public void setPlaybackState(int state) {
+ setPlaybackState(state, PLAYBACK_POSITION_INVALID, PLAYBACK_SPEED_1X);
+ }
+
+ /**
+ * @hide
+ * (to be un-hidden)
+ * Sets the current playback state and the matching media position for the current playback
+ * speed.
+ * @param state The current playback state, one of the following values:
+ * {@link #PLAYSTATE_STOPPED},
+ * {@link #PLAYSTATE_PAUSED},
+ * {@link #PLAYSTATE_PLAYING},
+ * {@link #PLAYSTATE_FAST_FORWARDING},
+ * {@link #PLAYSTATE_REWINDING},
+ * {@link #PLAYSTATE_SKIPPING_FORWARDS},
+ * {@link #PLAYSTATE_SKIPPING_BACKWARDS},
+ * {@link #PLAYSTATE_BUFFERING},
+ * {@link #PLAYSTATE_ERROR}.
+ * @param timeInMs a 0 or positive value for the current media position expressed in ms
+ * (same unit as for when sending the media duration, if applicable, with
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} in the
+ * {@link RemoteControlClient.MetadataEditor}). Negative values imply that position is not
+ * known (e.g. listening to a live stream of a radio) or not applicable (e.g. when state
+ * is {@link #PLAYSTATE_BUFFERING} and nothing had played yet).
+ * @param playbackSpeed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
+ * 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
+ * playing (e.g. when state is {@link #PLAYSTATE_ERROR}).
+ */
+ public void setPlaybackState(int state, long timeInMs, float playbackSpeed) {
synchronized(mCacheLock) {
- if (mPlaybackState != state) {
+ if (timeInMs != PLAYBACK_POSITION_INVALID) {
+ mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE;
+ } else {
+ mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE;
+ }
+ if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs)
+ || (mPlaybackSpeed != playbackSpeed)) {
// store locally
mPlaybackState = state;
+ mPlaybackPositionMs = timeInMs;
+ mPlaybackSpeed = playbackSpeed;
// keep track of when the state change occurred
mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();
// send to remote control display if conditions are met
sendPlaybackState_syncCacheLock();
// update AudioService
- sendAudioServiceNewPlaybackInfo_syncCacheLock(PLAYBACKINFO_PLAYSTATE, state);
+ sendAudioServiceNewPlaybackState_syncCacheLock();
}
}
}
@@ -625,6 +674,65 @@
}
}
+ /**
+ * @hide
+ * (to be un-hidden)
+ * Interface definition for a callback to be invoked when the media playback position is
+ * requested to be updated.
+ */
+ public interface OnPlaybackPositionUpdateListener {
+ /**
+ * Called on the listener to notify it that the playback head should be set at the given
+ * position. If the position can be changed from its current value, the implementor of
+ * the interface should also update the playback position using
+ * {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new
+ * position being used, regardless of whether it differs from the requested position.
+ * @param newPositionMs the new requested position in the current media, expressed in ms.
+ */
+ void onPlaybackPositionUpdate(long newPositionMs);
+ }
+
+ /**
+ * @hide
+ * (to be un-hidden)
+ * Sets the listener RemoteControlClient calls whenever the media playback position is requested
+ * to be updated.
+ * Notifications will be received in the same thread as the one in which RemoteControlClient
+ * was created.
+ * @param l
+ */
+ public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
+ synchronized(mCacheLock) {
+ if ((mPositionUpdateListener == null) && (l != null)) {
+ mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE;
+ // tell RCDs and AudioService this RCC accepts position updates
+ // TODO implement
+ } else if ((mPositionUpdateListener != null) && (l == null)) {
+ mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE;
+ // tell RCDs and AudioService this RCC doesn't handle position updates
+ // TODO implement
+ }
+ mPositionUpdateListener = l;
+ }
+ }
+
+ /**
+ * @hide
+ * Flag to reflect that the application controlling this RemoteControlClient sends playback
+ * position updates. The playback position being "readable" is considered from the application's
+ * point of view.
+ */
+ public static int MEDIA_POSITION_READABLE = 1 << 0;
+ /**
+ * @hide
+ * Flag to reflect that the application controlling this RemoteControlClient can receive
+ * playback position updates. The playback position being "writable"
+ * is considered from the application's point of view.
+ */
+ public static int MEDIA_POSITION_WRITABLE = 1 << 1;
+
+ private int mPlaybackPositionCapabilities = 0;
+
/** @hide */
public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE;
/** @hide */
@@ -756,6 +864,14 @@
*/
private long mPlaybackStateChangeTimeMs = 0;
/**
+ * Last playback position in ms reported by the user
+ */
+ private long mPlaybackPositionMs = PLAYBACK_POSITION_INVALID;
+ /**
+ * Last playback speed reported by the user
+ */
+ private float mPlaybackSpeed = PLAYBACK_SPEED_1X;
+ /**
* Cache for the artwork bitmap.
* Access synchronized on mCacheLock
* Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be
@@ -774,9 +890,13 @@
* This is re-initialized in apply() and so cannot be final.
*/
private Bundle mMetadata = new Bundle();
-
/**
- * The current remote control client generation ID across the system
+ * Listener registered by user of RemoteControlClient to receive requests for playback position
+ * update requests.
+ */
+ private OnPlaybackPositionUpdateListener mPositionUpdateListener;
+ /**
+ * The current remote control client generation ID across the system, as known by this object
*/
private int mCurrentClientGenId = -1;
/**
@@ -789,7 +909,8 @@
/**
* The media button intent description associated with this remote control client
- * (can / should include target component for intent handling)
+ * (can / should include target component for intent handling, used when persisting media
+ * button event receiver across reboots).
*/
private final PendingIntent mRcMediaIntent;
@@ -990,7 +1111,8 @@
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
try {
di.mRcDisplay.setPlaybackState(mInternalClientGenId,
- mPlaybackState, mPlaybackStateChangeTimeMs);
+ mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
+ mPlaybackSpeed);
} catch (RemoteException e) {
Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e);
displayIterator.remove();
@@ -1109,7 +1231,20 @@
try {
service.setPlaybackInfoForRcc(mRcseId, what, value);
} catch (RemoteException e) {
- Log.e(TAG, "Dead object in sendAudioServiceNewPlaybackInfo_syncCacheLock", e);
+ Log.e(TAG, "Dead object in setPlaybackInfoForRcc", e);
+ }
+ }
+
+ private void sendAudioServiceNewPlaybackState_syncCacheLock() {
+ if (mRcseId == RCSE_ID_UNREGISTERED) {
+ return;
+ }
+ IAudioService service = getService();
+ try {
+ service.setPlaybackStateForRcc(mRcseId,
+ mPlaybackState, mPlaybackPositionMs, mPlaybackSpeed);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setPlaybackStateForRcc", e);
}
}
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 262000e..015c0cc 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -4,13 +4,10 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs \
src/com/android/systemui/EventLogTags.logtags
LOCAL_JAVA_LIBRARIES := services telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := android-common-carousel
-
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index 9712ea8..d5798d7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -132,7 +132,8 @@
mLocalHandler = new WeakReference<Handler>(handler);
}
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
Handler handler = mLocalHandler.get();
if (handler != null) {
handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 27d816e..e958e9a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -200,7 +200,8 @@
private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
new IRemoteControlDisplay.Stub() {
- public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+ public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+ long currentPosMs, float speed) {
Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
generationId, state, stateChangeTimeMs);
mHandler.sendMessage(msg);
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index 924b9df..ea7b696 100644
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothManagerCallback;
@@ -87,6 +88,9 @@
// and Airplane mode will have higher priority.
private static final int BLUETOOTH_ON_AIRPLANE=2;
+ private static final int SERVICE_IBLUETOOTH = 1;
+ private static final int SERVICE_IBLUETOOTHGATT = 2;
+
private final Context mContext;
// Locks are not provided for mName and mAddress.
@@ -97,6 +101,7 @@
private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
private IBluetooth mBluetooth;
+ private IBluetoothGatt mBluetoothGatt;
private boolean mBinding;
private boolean mUnbinding;
// used inside handler thread
@@ -463,6 +468,11 @@
}
}
+ public IBluetoothGatt getBluetoothGatt() {
+ // sync protection
+ return mBluetoothGatt;
+ }
+
private void sendBluetoothStateCallback(boolean isUp) {
int n = mStateChangeCallbacks.beginBroadcast();
if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
@@ -575,16 +585,35 @@
}
public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService");
+ if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
+ // TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
+ if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+ msg.arg1 = SERVICE_IBLUETOOTH;
+ // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
+ } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+ msg.arg1 = SERVICE_IBLUETOOTHGATT;
+ } else {
+ Log.e(TAG, "Unknown service connected: " + className.getClassName());
+ return;
+ }
msg.obj = service;
mHandler.sendMessage(msg);
}
public void onServiceDisconnected(ComponentName className) {
// Called if we unexpected disconnected.
- if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService");
+ if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " +
+ className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
+ if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+ msg.arg1 = SERVICE_IBLUETOOTH;
+ } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+ msg.arg1 = SERVICE_IBLUETOOTHGATT;
+ } else {
+ Log.e(TAG, "Unknown service disconnected: " + className.getClassName());
+ return;
+ }
mHandler.sendMessage(msg);
}
}
@@ -746,13 +775,18 @@
}
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
- if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED");
-
- //Remove timeout
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+ if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
IBinder service = (IBinder) msg.obj;
synchronized(mConnection) {
+ if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
+ mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
+ break;
+ } // else must be SERVICE_IBLUETOOTH
+
+ //Remove timeout
+ mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+
mBinding = false;
mBluetooth = IBluetooth.Stub.asInterface(service);
@@ -816,11 +850,19 @@
}
case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
{
- Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
+ Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
synchronized(mConnection) {
- // if service is unbinded already, do nothing and return
- if (mBluetooth == null) return;
- mBluetooth = null;
+ if (msg.arg1 == SERVICE_IBLUETOOTH) {
+ // if service is unbinded already, do nothing and return
+ if (mBluetooth == null) break;
+ mBluetooth = null;
+ } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
+ mBluetoothGatt = null;
+ break;
+ } else {
+ Log.e(TAG, "Bad msg.arg1: " + msg.arg1);
+ break;
+ }
}
if (mEnable) {
@@ -1048,10 +1090,19 @@
boolean isUp = (newState==BluetoothAdapter.STATE_ON);
sendBluetoothStateCallback(isUp);
- //If Bluetooth is off, send service down event to proxy objects, and unbind
- if (!isUp && canUnbindBluetoothService()) {
- sendBluetoothServiceDownCallback();
- unbindAndFinish();
+ if (isUp) {
+ // connect to GattService
+ Intent i = new Intent(IBluetoothGatt.class.getName());
+ if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.CURRENT)) {
+ Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName());
+ }
+ } else {
+ //If Bluetooth is off, send service down event to proxy objects, and unbind
+ if (!isUp && canUnbindBluetoothService()) {
+ sendBluetoothServiceDownCallback();
+ unbindAndFinish();
+ }
}
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 0bb0366..9e06db8 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2285,9 +2285,17 @@
}
// Update 464xlat state.
- // TODO: Move to handleConnect()
NetworkStateTracker tracker = mNetTrackers[netType];
if (mClat.requiresClat(netType, tracker)) {
+ // If the connection was previously using clat, but is not using it now, stop the clat
+ // daemon. Normally, this happens automatically when the connection disconnects, but if
+ // the disconnect is not reported, or if the connection's LinkProperties changed for
+ // some other reason (e.g., handoff changes the IP addresses on the link), it would
+ // still be running. If it's not running, then stopping it is a no-op.
+ if (Nat464Xlat.isRunningClat(curLp) && !Nat464Xlat.isRunningClat(newLp)) {
+ mClat.stopClat();
+ }
+ // If the link requires clat to be running, then start the daemon now.
if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
mClat.startClat(tracker);
} else {
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 03679a5..944bf33 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -541,6 +541,7 @@
}
}
+ @Override
public boolean addAccount(Account account, String password, Bundle extras) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "addAccount: " + account
@@ -549,9 +550,13 @@
}
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
- if (!canUserModifyAccounts(Binder.getCallingUid())) {
- return false;
- }
+ /*
+ * Child users are not allowed to add accounts. Only the accounts that are
+ * shared by the parent profile can be added to child profile.
+ *
+ * TODO: Only allow accounts that were shared to be added by
+ * a limited user.
+ */
UserAccounts accounts = getUserAccountsForCaller();
// fails if the account already exists
@@ -588,12 +593,9 @@
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
// Create a Session for the target user and pass in the bundle
completeCloningAccount(result, account, toAccounts);
- } else {
- clonePassword(fromAccounts, toAccounts, account);
}
return;
} else {
- clonePassword(fromAccounts, toAccounts, account);
super.onResult(result);
}
}
@@ -604,23 +606,6 @@
return true;
}
- // TODO: Remove fallback - move to authenticator
- private void clonePassword(UserAccounts fromAccounts, UserAccounts toAccounts,
- Account account) {
- long id = clearCallingIdentity();
- try {
- String password = readPasswordInternal(fromAccounts, account);
- String extraFlags = readUserDataInternal(fromAccounts, account, "flags");
- String extraServices = readUserDataInternal(fromAccounts, account, "services");
- Bundle extras = new Bundle();
- extras.putString("flags", extraFlags);
- extras.putString("services", extraServices);
- addAccountInternal(toAccounts, account, password, extras, true);
- } finally {
- restoreCallingIdentity(id);
- }
- }
-
void completeCloningAccount(final Bundle result, final Account account,
final UserAccounts targetUser) {
long id = clearCallingIdentity();
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java
index 2884eaf..59403c5 100644
--- a/services/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/java/com/android/server/connectivity/Nat464Xlat.java
@@ -87,6 +87,10 @@
return netType == TYPE_MOBILE && !lp.hasIPv4Address();
}
+ public static boolean isRunningClat(LinkProperties lp) {
+ return lp != null && lp.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME);
+ }
+
/**
* Starts the clat daemon.
* @param lp The link properties of the interface to start clatd on.
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index e13a17b..70183df 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1372,6 +1372,7 @@
// userId - application-specific user id
// debugFlag - 0 or 1 if the package is debuggable.
// dataPath - path to package's data path
+ // seinfo - seinfo label for the app (assigned at install time)
//
// NOTE: We prefer not to expose all ApplicationInfo flags for now.
//
@@ -1385,6 +1386,8 @@
sb.append((int)ai.uid);
sb.append(isDebug ? " 1 " : " 0 ");
sb.append(dataPath);
+ sb.append(" ");
+ sb.append(ai.seinfo);
sb.append("\n");
str.write(sb.toString().getBytes());
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index c501553..a70ebf4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -111,9 +111,9 @@
private void writePackagesList() {
writeFile(new File(getContext().getFilesDir(), "system/packages.list"),
- ( "com.google.app1 11000 0 /data/data/com.google.app1"
- + "com.google.app2 11001 0 /data/data/com.google.app2"
- + "com.android.app3 11030 0 /data/data/com.android.app3")
+ ( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1"
+ + "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2"
+ + "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3")
.getBytes());
}
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 2385c24..47f1fbf 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1468,6 +1468,8 @@
if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) {
saveConfig();
}
+
+ config.enterpriseConfig.migrateCerts(mKeyStore);
}
private String removeDoubleQuotes(String string) {
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 95ffb1c..f73a13c 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -17,9 +17,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
import android.security.Credentials;
import android.text.TextUtils;
-import android.util.Log;
import com.android.org.bouncycastle.asn1.ASN1InputStream;
import com.android.org.bouncycastle.asn1.ASN1Sequence;
@@ -481,7 +481,7 @@
String caCertName = Credentials.CA_CERTIFICATE + name;
if (mClientCertificate != null) {
byte[] privKeyData = mClientPrivateKey.getEncoded();
- ret = keyStore.importKey(privKeyName, privKeyData);
+ ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID);
if (ret == false) {
return ret;
}
@@ -489,7 +489,7 @@
ret = putCertInKeyStore(keyStore, userCertName, mClientCertificate);
if (ret == false) {
// Remove private key installed
- keyStore.delKey(privKeyName);
+ keyStore.delKey(privKeyName, Process.WIFI_UID);
return ret;
}
}
@@ -499,8 +499,8 @@
if (ret == false) {
if (mClientCertificate != null) {
// Remove client key+cert
- keyStore.delKey(privKeyName);
- keyStore.delete(userCertName);
+ keyStore.delKey(privKeyName, Process.WIFI_UID);
+ keyStore.delete(userCertName, Process.WIFI_UID);
}
return ret;
}
@@ -525,7 +525,7 @@
Certificate cert) {
try {
byte[] certData = Credentials.convertToPem(cert);
- return keyStore.put(name, certData);
+ return keyStore.put(name, certData, Process.WIFI_UID);
} catch (IOException e1) {
return false;
} catch (CertificateException e2) {
@@ -537,14 +537,14 @@
String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
// a valid client certificate is configured
if (!TextUtils.isEmpty(client)) {
- keyStore.delKey(Credentials.USER_PRIVATE_KEY + client);
- keyStore.delete(Credentials.USER_CERTIFICATE + client);
+ keyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+ keyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
}
String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
// a valid ca certificate is configured
if (!TextUtils.isEmpty(ca)) {
- keyStore.delete(Credentials.CA_CERTIFICATE + ca);
+ keyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
}
}
@@ -625,6 +625,29 @@
return true;
}
+ /** Migrate certs from global pool to wifi UID if not already done */
+ void migrateCerts(android.security.KeyStore keyStore) {
+ String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
+ // a valid client certificate is configured
+ if (!TextUtils.isEmpty(client)) {
+ if (!keyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
+ keyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
+ Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+ keyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
+ Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
+ }
+ }
+
+ String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
+ // a valid ca certificate is configured
+ if (!TextUtils.isEmpty(ca)) {
+ if (!keyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
+ keyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
+ Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
+ }
+ }
+ }
+
private String removeDoubleQuotes(String string) {
int length = string.length();
if ((length > 1) && (string.charAt(0) == '"')