summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSudhir Sharma <sudhshar@codeaurora.org>2015-03-10 01:21:10 -0700
committerSudhir Sharma <sudhshar@codeaurora.org>2015-03-10 15:27:58 -0700
commit9d81dc62517d86b1fccc65bcf87193819ce7c66d (patch)
tree6f7aa485047446c6cd41ea9d7f2617a4270cab1e /src
parentbb5835ad23b6357bb98848a481c971cc169739a9 (diff)
parent2ab3133a5c3a70fe1184ba8ff63ac3365f648a4e (diff)
downloadandroid_packages_apps_Bluetooth-9d81dc62517d86b1fccc65bcf87193819ce7c66d.tar.gz
android_packages_apps_Bluetooth-9d81dc62517d86b1fccc65bcf87193819ce7c66d.tar.bz2
android_packages_apps_Bluetooth-9d81dc62517d86b1fccc65bcf87193819ce7c66d.zip
Merge tag 'android-5.1.0_r1' into HEAD
Android 5.1.0 release 1 Conflicts: src/com/android/bluetooth/btservice/AdapterService.java src/com/android/bluetooth/gatt/AdvertiseManager.java src/com/android/bluetooth/gatt/ScanManager.java src/com/android/bluetooth/hfp/HeadsetPhoneState.java src/com/android/bluetooth/hfp/HeadsetStateMachine.java src/com/android/bluetooth/map/BluetoothMapContent.java src/com/android/bluetooth/map/BluetoothMapContentObserver.java src/com/android/bluetooth/map/BluetoothMapEmailSettingsLoader.java src/com/android/bluetooth/map/BluetoothMapObexServer.java src/com/android/bluetooth/map/BluetoothMapService.java src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java src/com/android/bluetooth/opp/BluetoothOppService.java src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java Change-Id: Ic7a754ea29d6124d7e53b6f13eed181d3f7e64e5
Diffstat (limited to 'src')
-rw-r--r--src/com/android/bluetooth/Utils.java46
-rw-r--r--src/com/android/bluetooth/a2dp/A2dpService.java11
-rw-r--r--src/com/android/bluetooth/a2dp/A2dpSinkService.java8
-rw-r--r--src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java9
-rw-r--r--src/com/android/bluetooth/a2dp/A2dpStateMachine.java8
-rw-r--r--src/com/android/bluetooth/avrcp/Avrcp.java27
-rw-r--r--src/com/android/bluetooth/avrcp/AvrcpControllerService.java5
-rw-r--r--src/com/android/bluetooth/btservice/AdapterService.java154
-rw-r--r--src/com/android/bluetooth/btservice/ProfileService.java34
-rw-r--r--src/com/android/bluetooth/gatt/AdvertiseManager.java90
-rw-r--r--src/com/android/bluetooth/gatt/ContextMap.java34
-rw-r--r--src/com/android/bluetooth/gatt/GattDebugUtils.java22
-rw-r--r--src/com/android/bluetooth/gatt/GattService.java113
-rw-r--r--src/com/android/bluetooth/gatt/HandleMap.java23
-rw-r--r--src/com/android/bluetooth/gatt/ScanManager.java3
-rw-r--r--src/com/android/bluetooth/hdp/HealthService.java17
-rw-r--r--src/com/android/bluetooth/hfp/HeadsetPhoneState.java116
-rw-r--r--src/com/android/bluetooth/hfp/HeadsetService.java9
-rw-r--r--src/com/android/bluetooth/hfp/HeadsetStateMachine.java36
-rw-r--r--src/com/android/bluetooth/hfpclient/HeadsetClientService.java8
-rw-r--r--src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java29
-rw-r--r--src/com/android/bluetooth/hid/HidService.java10
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapContent.java283
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapContentObserver.java224
-rwxr-xr-xsrc/com/android/bluetooth/map/BluetoothMapObexServer.java20
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapService.java22
-rw-r--r--src/com/android/bluetooth/map/BluetoothMapSmsPdu.java2
-rw-r--r--src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java6
-rw-r--r--src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java3
-rw-r--r--src/com/android/bluetooth/opp/BluetoothOppService.java14
-rw-r--r--src/com/android/bluetooth/opp/BluetoothOppTransfer.java8
-rwxr-xr-xsrc/com/android/bluetooth/pan/PanService.java35
-rw-r--r--src/com/android/bluetooth/util/NumberUtils.java46
33 files changed, 1029 insertions, 446 deletions
diff --git a/src/com/android/bluetooth/Utils.java b/src/com/android/bluetooth/Utils.java
index 56d5a641b..0b2f836d6 100644
--- a/src/com/android/bluetooth/Utils.java
+++ b/src/com/android/bluetooth/Utils.java
@@ -17,12 +17,17 @@
package com.android.bluetooth;
import android.app.ActivityManager;
+import android.app.ActivityThread;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.ParcelUuid;
+import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import java.io.IOException;
@@ -193,12 +198,19 @@ final public class Utils {
// Get the caller's user id then clear the calling identity
// which will be restored in the finally clause.
int callingUser = UserHandle.getCallingUserId();
+ int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
// With calling identity cleared the current user is the foreground user.
int foregroundUser = ActivityManager.getCurrentUser();
ok = (foregroundUser == callingUser);
+ if (!ok) {
+ // Always allow SystemUI/System access.
+ int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
+ "com.android.systemui", UserHandle.USER_OWNER);
+ ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
+ }
} catch (Exception ex) {
Log.e(TAG, "checkIfCallerIsSelfOrForegroundUser: Exception ex=" + ex);
ok = false;
@@ -208,6 +220,40 @@ final public class Utils {
return ok;
}
+ public static boolean checkCallerAllowManagedProfiles(Context mContext) {
+ if (mContext == null) {
+ return checkCaller();
+ }
+ boolean ok;
+ // Get the caller's user id and if it's a managed profile, get it's parents
+ // id, then clear the calling identity
+ // which will be restored in the finally clause.
+ int callingUser = UserHandle.getCallingUserId();
+ int callingUid = Binder.getCallingUid();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ UserInfo ui = um.getProfileParent(callingUser);
+ int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL;
+ // With calling identity cleared the current user is the foreground user.
+ int foregroundUser = ActivityManager.getCurrentUser();
+ ok = (foregroundUser == callingUser) ||
+ (foregroundUser == parentUser);
+ if (!ok) {
+ // Always allow SystemUI/System access.
+ int systemUiUid = ActivityThread.getPackageManager().getPackageUid(
+ "com.android.systemui", UserHandle.USER_OWNER);
+ ok = (systemUiUid == callingUid) || (Process.SYSTEM_UID == callingUid);
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "checkCallerAllowManagedProfiles: Exception ex=" + ex);
+ ok = false;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return ok;
+ }
+
/**
* Enforce the context has android.Manifest.permission.BLUETOOTH_ADMIN permission. A
* {@link SecurityException} would be thrown if neither the calling process or the application
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 97f6a7424..a0d1c30f1 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -312,4 +312,15 @@ public class A2dpService extends ProfileService {
return service.isA2dpPlaying(device);
}
};
+
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ if (mStateMachine != null) {
+ mStateMachine.dump(sb);
+ }
+ if (mAvrcp != null) {
+ mAvrcp.dump(sb);
+ }
+ }
}
diff --git a/src/com/android/bluetooth/a2dp/A2dpSinkService.java b/src/com/android/bluetooth/a2dp/A2dpSinkService.java
index 6a74e9935..433dcd34b 100644
--- a/src/com/android/bluetooth/a2dp/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpSinkService.java
@@ -255,4 +255,12 @@ public class A2dpSinkService extends ProfileService {
return service.getAudioConfig(device);
}
};
+
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ if (mStateMachine != null) {
+ mStateMachine.dump(sb);
+ }
+ }
}
diff --git a/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
index 13c4ade46..1d585309d 100644
--- a/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java
@@ -173,7 +173,14 @@ final class A2dpSinkStateMachine extends StateMachine {
mAudioConfigs.clear();
}
- private class Disconnected extends State {
+ public void dump(StringBuilder sb) {
+ ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
+ ProfileService.println(sb, "mTargetDevice: " + mTargetDevice);
+ ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice);
+ ProfileService.println(sb, "StateMachine: " + this.toString());
+ }
+
+ private class Disconnected extends State {
@Override
public void enter() {
log("Enter Disconnected: " + getCurrentMessage().what);
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 873e67ff7..3326b4375 100644
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -137,7 +137,6 @@ final class A2dpStateMachine extends StateMachine {
mIntentBroadcastHandler = new IntentBroadcastHandler();
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-
}
static A2dpStateMachine make(A2dpService svc, Context context) {
@@ -816,6 +815,13 @@ final class A2dpStateMachine extends StateMachine {
}
}
+ public void dump(StringBuilder sb) {
+ ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
+ ProfileService.println(sb, "mTargetDevice: " + mTargetDevice);
+ ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice);
+ ProfileService.println(sb, "mPlayingA2dpDevice: " + mPlayingA2dpDevice);
+ ProfileService.println(sb, "StateMachine: " + this.toString());
+ }
// Event types for STACK_EVENT message
final private static int EVENT_TYPE_NONE = 0;
diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java
index c70ac932c..fc4e89533 100644
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -3553,6 +3553,33 @@ public final class Avrcp {
mHandler.sendMessage(msg);
}
+ public void dump(StringBuilder sb) {
+ sb.append("AVRCP:\n");
+ ProfileService.println(sb, "mMetadata: " + mMetadata);
+ ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
+ ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
+ ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
+ ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
+ ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
+ ProfileService.println(sb, "mCurrentPosMs: " + mCurrentPosMs);
+ ProfileService.println(sb, "mPlayStartTimeMs: " + mPlayStartTimeMs);
+ ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
+ ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
+ ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
+ ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);
+ ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs);
+ ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime);
+ ProfileService.println(sb, "mFeatures: " + mFeatures);
+ ProfileService.println(sb, "mAbsoluteVolume: " + mAbsoluteVolume);
+ ProfileService.println(sb, "mLastSetVolume: " + mLastSetVolume);
+ ProfileService.println(sb, "mLastDirection: " + mLastDirection);
+ ProfileService.println(sb, "mVolumeStep: " + mVolumeStep);
+ ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax);
+ ProfileService.println(sb, "mVolCmdInProgress: " + mVolCmdInProgress);
+ ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
+ ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
+ }
+
// Do not modify without updating the HAL bt_rc.h files.
// match up with btrc_play_status_t enum of bt_rc.h
diff --git a/src/com/android/bluetooth/avrcp/AvrcpControllerService.java b/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
index b7275d694..0c7e0b6cb 100644
--- a/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcp/AvrcpControllerService.java
@@ -265,6 +265,11 @@ public class AvrcpControllerService extends ProfileService {
return Utils.getBytesFromAddress(device.getAddress());
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ }
+
private native static void classInitNative();
private native void initNative();
private native void cleanupNative();
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 27c8e5ed0..aaafd2f85 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -57,8 +57,10 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
+
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.a2dp.A2dpSinkService;
import com.android.bluetooth.hid.HidService;
@@ -67,9 +69,10 @@ import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hdp.HealthService;
import com.android.bluetooth.hfpclient.HeadsetClientService;
import com.android.bluetooth.pan.PanService;
-import com.android.bluetooth.R;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
+import com.android.internal.R;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
@@ -80,6 +83,7 @@ import java.util.Map;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.List;
+
import android.content.pm.PackageManager;
import android.os.ServiceManager;
@@ -100,6 +104,8 @@ public class AdapterService extends Service {
private int mIdleTimeTotalMs;
private int mEnergyUsedTotalVoltAmpSecMicro;
+ private final ArrayList<ProfileService> mProfiles = new ArrayList<ProfileService>();
+
public static final String ACTION_LOAD_ADAPTER_PROPERTIES =
"com.android.bluetooth.btservice.action.LOAD_ADAPTER_PROPERTIES";
public static final String ACTION_SERVICE_STATE_CHANGED =
@@ -280,6 +286,18 @@ public class AdapterService extends Service {
}
}
+ public void addProfile(ProfileService profile) {
+ synchronized (mProfiles) {
+ mProfiles.add(profile);
+ }
+ }
+
+ public void removeProfile(ProfileService profile) {
+ synchronized (mProfiles) {
+ mProfiles.remove(profile);
+ }
+ }
+
public void onProfileServiceStateChanged(String serviceName, int state) {
Message m = mHandler.obtainMessage(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
m.obj=serviceName;
@@ -533,6 +551,8 @@ public class AdapterService extends Service {
private static final int MESSAGE_PROFILE_CONNECTION_STATE_CHANGED = 20;
private static final int MESSAGE_CONNECT_OTHER_PROFILES = 30;
private static final int MESSAGE_PROFILE_INIT_PRIORITIES=40;
+ private static final int MESSAGE_SET_WAKE_ALARM = 100;
+ private static final int MESSAGE_RELEASE_WAKE_ALARM = 110;
private static final int CONNECT_OTHER_PROFILES_TIMEOUT= 6000;
private static final int CONNECT_OTHER_PROFILES_TIMEOUT_DEYALED = 10000;
private static final int MESSAGE_AUTO_CONNECT_PROFILES = 50;
@@ -574,6 +594,17 @@ public class AdapterService extends Service {
autoConnectProfilesDelayed();
break;
}
+ case MESSAGE_SET_WAKE_ALARM: {
+ debugLog( "handleMessage() - MESSAGE_SET_WAKE_ALARM");
+ processSetWakeAlarm((Long) msg.obj, msg.arg1);
+ }
+ break;
+ case MESSAGE_RELEASE_WAKE_ALARM: {
+ debugLog( "handleMessage() - MESSAGE_RELEASE_WAKE_ALARM");
+ mPendingAlarm = null;
+ alarmFiredNative();
+ }
+ break;
}
}
};
@@ -596,7 +627,7 @@ public class AdapterService extends Service {
String serviceName = services[i].getName();
Integer serviceState = mProfileServicesState.get(serviceName);
if(serviceState != null && serviceState != expectedCurrentState) {
- debugLog("setProfileServiceState() - Unable to "
+ debugLog("setProfileServiceState() - Unable to "
+ (state == BluetoothAdapter.STATE_OFF ? "start" : "stop" )
+ " service " + serviceName
+ ". Invalid state: " + serviceState);
@@ -715,7 +746,7 @@ public class AdapterService extends Service {
public String getAddress() {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
- (!Utils.checkCaller())) {
+ (!Utils.checkCallerAllowManagedProfiles(mService))) {
Log.w(TAG, "getAddress() - Not allowed for non-active user and non system user");
return null;
}
@@ -760,7 +791,7 @@ public class AdapterService extends Service {
}
public int getScanMode() {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getScanMode() - Not allowed for non-active user");
return BluetoothAdapter.SCAN_MODE_NONE;
}
@@ -825,7 +856,7 @@ public class AdapterService extends Service {
return service.cancelDiscovery();
}
public boolean isDiscovering() {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "isDiscovering() - Not allowed for non-active user");
return false;
}
@@ -850,7 +881,7 @@ public class AdapterService extends Service {
}
public int getProfileConnectionState(int profile) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getProfileConnectionState- Not allowed for non-active user");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -900,16 +931,14 @@ public class AdapterService extends Service {
return service.getBondState(device);
}
- public boolean isConnected(BluetoothDevice device) {
+ public int getConnectionState(BluetoothDevice device) {
AdapterService service = getService();
- if (service == null) {
- return false;
- }
- return service.isConnected(device);
+ if (service == null) return 0;
+ return service.getConnectionState(device);
}
public String getRemoteName(BluetoothDevice device) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getRemoteName() - Not allowed for non-active user");
return null;
}
@@ -920,7 +949,7 @@ public class AdapterService extends Service {
}
public int getRemoteType(BluetoothDevice device) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getRemoteType() - Not allowed for non-active user");
return BluetoothDevice.DEVICE_TYPE_UNKNOWN;
}
@@ -931,7 +960,7 @@ public class AdapterService extends Service {
}
public String getRemoteAlias(BluetoothDevice device) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getRemoteAlias() - Not allowed for non-active user");
return null;
}
@@ -978,7 +1007,7 @@ public class AdapterService extends Service {
}
public int getRemoteClass(BluetoothDevice device) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getRemoteClass() - Not allowed for non-active user");
return 0;
}
@@ -989,7 +1018,7 @@ public class AdapterService extends Service {
}
public ParcelUuid[] getRemoteUuids(BluetoothDevice device) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "getRemoteUuids() - Not allowed for non-active user");
return new ParcelUuid[0];
}
@@ -1000,7 +1029,7 @@ public class AdapterService extends Service {
}
public boolean fetchRemoteUuids(BluetoothDevice device) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "fetchRemoteUuids() - Not allowed for non-active user");
return false;
}
@@ -1107,7 +1136,7 @@ public class AdapterService extends Service {
public ParcelFileDescriptor connectSocket(BluetoothDevice device, int type,
ParcelUuid uuid, int port, int flag) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "connectSocket() - Not allowed for non-active user");
return null;
}
@@ -1119,7 +1148,7 @@ public class AdapterService extends Service {
public ParcelFileDescriptor createSocketChannel(int type, String serviceName,
ParcelUuid uuid, int port, int flag) {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG, "createSocketChannel() - Not allowed for non-active user");
return null;
}
@@ -1164,9 +1193,9 @@ public class AdapterService extends Service {
public boolean configHciSnoopLog(boolean enable) {
- if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
- (!Utils.checkCaller())) {
- Log.w(TAG, "configHciSnoopLog() - Not allowed for non-active user");
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ EventLog.writeEvent(0x534e4554 /* SNET */, "Bluetooth", Binder.getCallingUid(),
+ "configHciSnoopLog() - Not allowed for non-active user b/18643224");
return false;
}
@@ -1190,8 +1219,13 @@ public class AdapterService extends Service {
public boolean isMultiAdvertisementSupported() {
AdapterService service = getService();
if (service == null) return false;
- int val = service.getNumOfAdvertisementInstancesSupported();
- return (val >= MIN_ADVT_INSTANCES_FOR_MA);
+ return service.isMultiAdvertisementSupported();
+ }
+
+ public boolean isPeripheralModeSupported() {
+ AdapterService service = getService();
+ if (service == null) return false;
+ return service.isPeripheralModeSupported();
}
public boolean isOffloadedFilteringSupported() {
@@ -1225,6 +1259,14 @@ public class AdapterService extends Service {
if (service == null) return null;
return service.reportActivityInfo();
}
+
+ public String dump() {
+ AdapterService service = getService();
+ if (service == null) {
+ return "AdapterService is null";
+ }
+ return service.dump();
+ }
};
@@ -1674,10 +1716,10 @@ public class AdapterService extends Service {
return deviceProp.getBondState();
}
- boolean isConnected(BluetoothDevice device) {
+ int getConnectionState(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
byte[] addr = Utils.getBytesFromAddress(device.getAddress());
- return isConnectedNative(addr);
+ return getConnectionStateNative(addr);
}
String getRemoteName(BluetoothDevice device) {
@@ -1909,6 +1951,11 @@ public class AdapterService extends Service {
return mAdapterProperties.getNumOfAdvertisementInstancesSupported();
}
+ public boolean isMultiAdvertisementSupported() {
+ enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return getNumOfAdvertisementInstancesSupported() >= MIN_ADVT_INSTANCES_FOR_MA;
+ }
+
public boolean isRpaOffloadSupported() {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mAdapterProperties.isRpaOffloadSupported();
@@ -1924,6 +1971,10 @@ public class AdapterService extends Service {
return mAdapterProperties.getNumOfOffloadedScanFilterSupported();
}
+ public boolean isPeripheralModeSupported() {
+ return getResources().getBoolean(R.bool.config_bluetooth_le_peripheral_mode_supported);
+ }
+
public int getOffloadedScanResultStorage() {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mAdapterProperties.getOffloadedScanResultStorage();
@@ -1956,6 +2007,16 @@ public class AdapterService extends Service {
return info;
}
+ private String dump() {
+ StringBuilder sb = new StringBuilder();
+ synchronized (mProfiles) {
+ for (ProfileService profile : mProfiles) {
+ profile.dump(sb);
+ }
+ }
+ return sb.toString();
+ }
+
private static int convertScanModeToHal(int mode) {
switch (mode) {
case BluetoothAdapter.SCAN_MODE_NONE:
@@ -1983,24 +2044,30 @@ public class AdapterService extends Service {
}
// This function is called from JNI. It allows native code to set a single wake
- // alarm. If an alarm is already pending and a new request comes in, the alarm
- // will be rescheduled (i.e. the previously set alarm will be cancelled).
+ // alarm.
private boolean setWakeAlarm(long delayMillis, boolean shouldWake) {
- synchronized (this) {
- if (mPendingAlarm != null) {
- mAlarmManager.cancel(mPendingAlarm);
- }
+ Message m = mHandler.obtainMessage(MESSAGE_SET_WAKE_ALARM);
+ m.obj = new Long(delayMillis);
+ // alarm type
+ m.arg1 = shouldWake ? AlarmManager.ELAPSED_REALTIME_WAKEUP
+ : AlarmManager.ELAPSED_REALTIME;
+ mHandler.sendMessage(m);
- long wakeupTime = SystemClock.elapsedRealtime() + delayMillis;
- int type = shouldWake
- ? AlarmManager.ELAPSED_REALTIME_WAKEUP
- : AlarmManager.ELAPSED_REALTIME;
+ return true;
+ }
- Intent intent = new Intent(ACTION_ALARM_WAKEUP);
- mPendingAlarm = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
- mAlarmManager.setExact(type, wakeupTime, mPendingAlarm);
- return true;
+ // If an alarm is already pending and a new request comes in, the alarm
+ // will be rescheduled (i.e. the previously set alarm will be cancelled).
+ private void processSetWakeAlarm(long delayMillis, int alarmType) {
+ if (mPendingAlarm != null) {
+ mAlarmManager.cancel(mPendingAlarm);
}
+
+ long wakeupTime = SystemClock.elapsedRealtime() + delayMillis;
+
+ Intent intent = new Intent(ACTION_ALARM_WAKEUP);
+ mPendingAlarm = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ mAlarmManager.setExact(alarmType, wakeupTime, mPendingAlarm);
}
// This function is called from JNI. It allows native code to acquire a single wake lock.
@@ -2078,10 +2145,7 @@ public class AdapterService extends Service {
private final BroadcastReceiver mAlarmBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (AdapterService.this) {
- mPendingAlarm = null;
- alarmFiredNative();
- }
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_RELEASE_WAKE_ALARM));
}
};
@@ -2103,7 +2167,7 @@ public class AdapterService extends Service {
/*package*/ native boolean removeBondNative(byte[] address);
/*package*/ native boolean cancelBondNative(byte[] address);
- /*package*/ native boolean isConnectedNative(byte[] address);
+ /*package*/ native int getConnectionStateNative(byte[] address);
private native boolean startDiscoveryNative();
private native boolean cancelDiscoveryNative();
diff --git a/src/com/android/bluetooth/btservice/ProfileService.java b/src/com/android/bluetooth/btservice/ProfileService.java
index 0c1b70e3f..1b70944ae 100644
--- a/src/com/android/bluetooth/btservice/ProfileService.java
+++ b/src/com/android/bluetooth/btservice/ProfileService.java
@@ -32,6 +32,8 @@ import android.util.Log;
public abstract class ProfileService extends Service {
private static final boolean DBG = false;
+ private static final String TAG = "BluetoothProfileService";
+
//For Debugging only
private static HashMap<String, Integer> sReferenceCount = new HashMap<String,Integer>();
@@ -53,6 +55,8 @@ public abstract class ProfileService extends Service {
protected boolean mStartError=false;
private boolean mCleaningUp = false;
+ private AdapterService mAdapterService;
+
protected String getName() {
return getClass().getSimpleName();
}
@@ -105,6 +109,12 @@ public abstract class ProfileService extends Service {
super.onCreate();
mAdapter = BluetoothAdapter.getDefaultAdapter();
mBinder = initBinder();
+ mAdapterService = AdapterService.getAdapterService();
+ if (mAdapterService != null) {
+ mAdapterService.addProfile(this);
+ } else {
+ Log.w(TAG, "onCreate, null mAdapterService");
+ }
}
public int onStartCommand(Intent intent, int flags, int startId) {
@@ -149,9 +159,23 @@ public abstract class ProfileService extends Service {
return super.onUnbind(intent);
}
+ // for dumpsys support
+ public void dump(StringBuilder sb) {
+ sb.append("Profile: " + mName + "\n");
+ }
+
+ // with indenting for subclasses
+ public static void println(StringBuilder sb, String s) {
+ sb.append(" ");
+ sb.append(s);
+ sb.append("\n");
+ }
+
@Override
public void onDestroy() {
if (DBG) log("Destroying service.");
+ if (mAdapterService != null) mAdapterService.removeProfile(this);
+
if (mCleaningUp) {
if (DBG) log("Cleanup already started... Skipping cleanup()...");
} else {
@@ -194,17 +218,15 @@ public abstract class ProfileService extends Service {
protected void notifyProfileServiceStateChanged(int state) {
//Notify adapter service
- AdapterService sAdapter = AdapterService.getAdapterService();
- if (sAdapter!= null) {
- sAdapter.onProfileServiceStateChanged(getClass().getName(), state);
+ if (mAdapterService != null) {
+ mAdapterService.onProfileServiceStateChanged(getClass().getName(), state);
}
}
public void notifyProfileConnectionStateChanged(BluetoothDevice device,
int profileId, int newState, int prevState) {
- AdapterService svc = AdapterService.getAdapterService();
- if (svc != null) {
- svc.onProfileConnectionStateChanged(device, profileId, newState, prevState);
+ if (mAdapterService != null) {
+ mAdapterService.onProfileConnectionStateChanged(device, profileId, newState, prevState);
}
}
diff --git a/src/com/android/bluetooth/gatt/AdvertiseManager.java b/src/com/android/bluetooth/gatt/AdvertiseManager.java
index 22b20dd6b..b556142b7 100644
--- a/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -40,7 +40,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
- * Manages Bluetooth LE advertising operations and interacts with bluedroid stack.
+ * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests.
*
* @hide
*/
@@ -56,6 +56,7 @@ class AdvertiseManager {
private static final int MSG_STOP_ADVERTISING = 1;
private final GattService mService;
+ private final AdapterService mAdapterService;
private final Set<AdvertiseClient> mAdvertiseClients;
private final AdvertiseNative mAdvertiseNative;
@@ -68,9 +69,10 @@ class AdvertiseManager {
/**
* Constructor of {@link AdvertiseManager}.
*/
- AdvertiseManager(GattService service) {
- mService = service;
+ AdvertiseManager(GattService service, AdapterService adapterService) {
logd("advertise manager created");
+ mService = service;
+ mAdapterService = adapterService;
mAdvertiseClients = new HashSet<AdvertiseClient>();
mAdvertiseNative = new AdvertiseNative();
}
@@ -272,21 +274,33 @@ class AdvertiseManager {
private static final int ADVERTISING_EVENT_TYPE_SCANNABLE = 2;
private static final int ADVERTISING_EVENT_TYPE_NON_CONNECTABLE = 3;
+ // TODO: Extract advertising logic into interface as we have multiple implementations now.
boolean startAdverising(AdvertiseClient client) {
- int clientIf = client.clientIf;
+ if (!mAdapterService.isMultiAdvertisementSupported() &&
+ !mAdapterService.isPeripheralModeSupported()) {
+ return false;
+ }
+ if (mAdapterService.isMultiAdvertisementSupported()) {
+ return startMultiAdvertising(client);
+ }
+ return startSingleAdvertising(client);
+ }
+
+ boolean startMultiAdvertising(AdvertiseClient client) {
+ logd("starting multi advertising");
resetCountDownLatch();
- mAdvertiseNative.enableAdvertising(client);
+ enableAdvertising(client);
if (!waitForCallback()) {
return false;
}
resetCountDownLatch();
- mAdvertiseNative.setAdvertisingData(clientIf, client.advertiseData, false);
+ setAdvertisingData(client, client.advertiseData, false);
if (!waitForCallback()) {
return false;
}
if (client.scanResponse != null) {
resetCountDownLatch();
- mAdvertiseNative.setAdvertisingData(clientIf, client.scanResponse, true);
+ setAdvertisingData(client, client.scanResponse, true);
if (!waitForCallback()) {
return false;
}
@@ -294,8 +308,29 @@ class AdvertiseManager {
return true;
}
+ boolean startSingleAdvertising(AdvertiseClient client) {
+ logd("starting single advertising");
+ resetCountDownLatch();
+ enableAdvertising(client);
+ if (!waitForCallback()) {
+ return false;
+ }
+ setAdvertisingData(client, client.advertiseData, false);
+ return true;
+ }
+
void stopAdvertising(AdvertiseClient client) {
- gattClientDisableAdvNative(client.clientIf);
+ if (mAdapterService.isMultiAdvertisementSupported()) {
+ gattClientDisableAdvNative(client.clientIf);
+ } else {
+ gattAdvertiseNative(client.clientIf, false);
+ try {
+ mService.onAdvertiseInstanceDisabled(
+ AdvertiseCallback.ADVERTISE_SUCCESS, client.clientIf);
+ } catch (RemoteException e) {
+ Log.d(TAG, "failed onAdvertiseInstanceDisabled", e);
+ }
+ }
}
private void resetCountDownLatch() {
@@ -319,16 +354,21 @@ class AdvertiseManager {
int txPowerLevel = getTxPowerLevel(client.settings);
int advertiseTimeoutSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(
client.settings.getTimeout());
- gattClientEnableAdvNative(
- clientIf,
- minAdvertiseUnit, maxAdvertiseUnit,
- advertiseEventType,
- ADVERTISING_CHANNEL_ALL,
- txPowerLevel,
- advertiseTimeoutSeconds);
+ if (mAdapterService.isMultiAdvertisementSupported()) {
+ gattClientEnableAdvNative(
+ clientIf,
+ minAdvertiseUnit, maxAdvertiseUnit,
+ advertiseEventType,
+ ADVERTISING_CHANNEL_ALL,
+ txPowerLevel,
+ advertiseTimeoutSeconds);
+ } else {
+ gattAdvertiseNative(client.clientIf, true);
+ }
}
- private void setAdvertisingData(int clientIf, AdvertiseData data, boolean isScanResponse) {
+ private void setAdvertisingData(AdvertiseClient client, AdvertiseData data,
+ boolean isScanResponse) {
if (data == null) {
return;
}
@@ -354,9 +394,15 @@ class AdvertiseManager {
}
serviceUuids = advertisingUuidBytes.array();
}
- gattClientSetAdvDataNative(clientIf, isScanResponse, includeName, includeTxPower,
- appearance,
- manufacturerData, serviceData, serviceUuids);
+ if (mAdapterService.isMultiAdvertisementSupported()) {
+ gattClientSetAdvDataNative(client.clientIf, isScanResponse, includeName,
+ includeTxPower, appearance,
+ manufacturerData, serviceData, serviceUuids);
+ } else {
+ gattSetAdvDataNative(client.clientIf, isScanResponse, includeName,
+ includeTxPower, 0, 0, appearance,
+ manufacturerData, serviceData, serviceUuids);
+ }
}
// Combine manufacturer id and manufacturer data.
@@ -455,6 +501,12 @@ class AdvertiseManager {
private native void gattClientSetAdvDataNative(int client_if,
boolean set_scan_rsp, boolean incl_name, boolean incl_txpower, int appearance,
byte[] manufacturer_data, byte[] service_data, byte[] service_uuid);
+
+ private native void gattSetAdvDataNative(int serverIf, boolean setScanRsp, boolean inclName,
+ boolean inclTxPower, int minSlaveConnectionInterval, int maxSlaveConnectionInterval,
+ int appearance, byte[] manufacturerData, byte[] serviceData, byte[] serviceUuid);
+
+ private native void gattAdvertiseNative(int client_if, boolean start);
}
private void logd(String s) {
diff --git a/src/com/android/bluetooth/gatt/ContextMap.java b/src/com/android/bluetooth/gatt/ContextMap.java
index 73b3c7564..5aabf00b3 100644
--- a/src/com/android/bluetooth/gatt/ContextMap.java
+++ b/src/com/android/bluetooth/gatt/ContextMap.java
@@ -134,6 +134,23 @@ import java.util.UUID;
}
/**
+ * Remove the context for a given UUID
+ */
+ void remove(UUID uuid) {
+ synchronized (mApps) {
+ Iterator<App> i = mApps.iterator();
+ while(i.hasNext()) {
+ App entry = i.next();
+ if (entry.uuid.equals(uuid)) {
+ entry.unlinkToDeath();
+ i.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ /**
* Remove the context for a given application ID.
*/
void remove(int id) {
@@ -299,28 +316,23 @@ import java.util.UUID;
/**
* Logs debug information.
*/
- void dump() {
- StringBuilder b = new StringBuilder();
- b.append( "-------------- GATT Context Map ----------------");
- b.append("\nEntries: " + mApps.size());
+ void dump(StringBuilder sb) {
+ sb.append(" Entries: " + mApps.size() + "\n");
Iterator<App> i = mApps.iterator();
while(i.hasNext()) {
App entry = i.next();
List<Connection> connections = getConnectionByApp(entry.id);
- b.append("\n\nApplication Id: " + entry.id);
- b.append("\nUUID: " + entry.uuid);
- b.append("\nConnections: " + connections.size());
+ sb.append("\n Application Id: " + entry.id + "\n");
+ sb.append(" UUID: " + entry.uuid + "\n");
+ sb.append(" Connections: " + connections.size() + "\n");
Iterator<Connection> ii = connections.iterator();
while(ii.hasNext()) {
Connection connection = ii.next();
- b.append("\n " + connection.connId + ": " + connection.address);
+ sb.append(" " + connection.connId + ": " + connection.address + "\n");
}
}
-
- b.append("\n------------------------------------------------");
- Log.d(TAG, b.toString());
}
}
diff --git a/src/com/android/bluetooth/gatt/GattDebugUtils.java b/src/com/android/bluetooth/gatt/GattDebugUtils.java
index 5c42db6a2..a1b37a2ba 100644
--- a/src/com/android/bluetooth/gatt/GattDebugUtils.java
+++ b/src/com/android/bluetooth/gatt/GattDebugUtils.java
@@ -28,13 +28,6 @@ import java.util.UUID;
private static final String TAG = GattServiceConfig.TAG_PREFIX + "DebugUtils";
private static final boolean DEBUG_ADMIN = GattServiceConfig.DEBUG_ADMIN;
- private static final String ACTION_DEBUG_DUMP_CLIENTMAP =
- "android.bluetooth.action.DEBUG_DUMP_CLIENTMAP";
- private static final String ACTION_DEBUG_DUMP_SERVERMAP =
- "android.bluetooth.action.DEBUG_DUMP_SERVERMAP";
- private static final String ACTION_DEBUG_DUMP_HANDLEMAP =
- "android.bluetooth.action.DEBUG_DUMP_HANDLEMAP";
-
private static final String ACTION_GATT_PAIRING_CONFIG =
"android.bluetooth.action.GATT_PAIRING_CONFIG";
@@ -82,23 +75,10 @@ import java.util.UUID;
Log.d(TAG, "handleDebugAction() action=" + action);
/*
- * Debug log functinos
- */
-
- if (ACTION_DEBUG_DUMP_CLIENTMAP.equals(action)) {
- svc.mClientMap.dump();
-
- } else if (ACTION_DEBUG_DUMP_SERVERMAP.equals(action)) {
- svc.mServerMap.dump();
-
- } else if (ACTION_DEBUG_DUMP_HANDLEMAP.equals(action)) {
- svc.mHandleMap.dump();
-
- /*
* PTS test commands
*/
- } else if (ACTION_GATT_TEST_USAGE.equals(action)) {
+ if (ACTION_GATT_TEST_USAGE.equals(action)) {
logUsageInfo();
} else if (ACTION_GATT_TEST_ENABLE.equals(action)) {
diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java
index 654df69b9..3aa5b8067 100644
--- a/src/com/android/bluetooth/gatt/GattService.java
+++ b/src/com/android/bluetooth/gatt/GattService.java
@@ -40,7 +40,10 @@ import android.os.SystemClock;
import android.util.Log;
import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.util.NumberUtils;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
@@ -75,6 +78,13 @@ public class GattService extends ProfileService {
private static final int ADVT_STATE_ONFOUND = 0;
private static final int ADVT_STATE_ONLOST = 1;
+ private static final UUID[] HID_UUIDS = {
+ UUID.fromString("00002A4A-0000-1000-8000-00805F9B34FB"),
+ UUID.fromString("00002A4B-0000-1000-8000-00805F9B34FB"),
+ UUID.fromString("00002A4C-0000-1000-8000-00805F9B34FB"),
+ UUID.fromString("00002A4D-0000-1000-8000-00805F9B34FB")
+ };
+
/**
* Search queue to serialize remote onbject inspection.
*/
@@ -180,7 +190,7 @@ public class GattService extends ProfileService {
protected boolean start() {
if (DBG) Log.d(TAG, "start()");
initializeNative();
- mAdvertiseManager = new AdvertiseManager(this);
+ mAdvertiseManager = new AdvertiseManager(this, AdapterService.getAdapterService());
mAdvertiseManager.start();
mScanManager = new ScanManager(this);
@@ -567,7 +577,6 @@ public class GattService extends ProfileService {
void onScanResult(String address, int rssi, byte[] adv_data) {
if (VDBG) Log.d(TAG, "onScanResult() - address=" + address
+ ", rssi=" + rssi);
- ScanRecord record = ScanRecord.parseFromBytes(adv_data);
List<UUID> remoteUuids = parseUuids(adv_data);
for (ScanClient client : mScanManager.getRegularScanQueue()) {
if (client.uuids.length > 0) {
@@ -636,9 +645,7 @@ public class GattService extends ProfileService {
if (client.filters == null || client.filters.isEmpty()) {
return true;
}
- if (DBG) Log.d(TAG, "result: " + scanResult.toString());
for (ScanFilter filter : client.filters) {
- if (DBG) Log.d(TAG, "filter: " + filter.toString());
if (filter.matches(scanResult)) {
return true;
}
@@ -652,8 +659,12 @@ public class GattService extends ProfileService {
if (DBG) Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
ClientMap.App app = mClientMap.getByUuid(uuid);
if (app != null) {
- app.id = clientIf;
- app.linkToDeath(new ClientDeathRecipient(clientIf));
+ if (status == 0) {
+ app.id = clientIf;
+ app.linkToDeath(new ClientDeathRecipient(clientIf));
+ } else {
+ mClientMap.remove(uuid);
+ }
app.callback.onClientRegistered(status, clientIf);
}
}
@@ -828,6 +839,12 @@ public class GattService extends ProfileService {
if (VDBG) Log.d(TAG, "onNotify() - address=" + address
+ ", charUuid=" + charUuid + ", length=" + data.length);
+
+ if (isHidUuid(charUuid) &&
+ (0 != checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED))) {
+ return;
+ }
+
ClientMap.App app = mClientMap.getByConnId(connId);
if (app != null) {
app.callback.onNotify(address, srvcType,
@@ -1052,33 +1069,33 @@ public class GattService extends ProfileService {
private Set<ScanResult> parseTruncatedResults(int numRecords, byte[] batchRecord) {
if (DBG) Log.d(TAG, "batch record " + Arrays.toString(batchRecord));
Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
+ long now = SystemClock.elapsedRealtimeNanos();
for (int i = 0; i < numRecords; ++i) {
byte[] record = extractBytes(batchRecord, i * TRUNCATED_RESULT_SIZE,
TRUNCATED_RESULT_SIZE);
byte[] address = extractBytes(record, 0, 6);
- // TODO: remove temp hack.
reverse(address);
BluetoothDevice device = mAdapter.getRemoteDevice(address);
int rssi = record[8];
- // Timestamp is in every 50 ms.
- long timestampNanos = parseTimestampNanos(extractBytes(record, 9, 2));
+ long timestampNanos = now - parseTimestampNanos(extractBytes(record, 9, 2));
results.add(new ScanResult(device, ScanRecord.parseFromBytes(new byte[0]),
rssi, timestampNanos));
}
return results;
}
- private long parseTimestampNanos(byte[] data) {
- long timestampUnit = data[1] & 0xFF << 8 + data[0];
- long timestampNanos = SystemClock.elapsedRealtimeNanos() -
- TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50);
- return timestampNanos;
+ @VisibleForTesting
+ long parseTimestampNanos(byte[] data) {
+ long timestampUnit = NumberUtils.littleEndianByteArrayToInt(data);
+ // Timestamp is in every 50 ms.
+ return TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50);
}
private Set<ScanResult> parseFullResults(int numRecords, byte[] batchRecord) {
Log.d(TAG, "Batch record : " + Arrays.toString(batchRecord));
Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
int position = 0;
+ long now = SystemClock.elapsedRealtimeNanos();
while (position < batchRecord.length) {
byte[] address = extractBytes(batchRecord, position, 6);
// TODO: remove temp hack.
@@ -1090,7 +1107,7 @@ public class GattService extends ProfileService {
// Skip tx power level.
position++;
int rssi = batchRecord[position++];
- long timestampNanos = parseTimestampNanos(extractBytes(batchRecord, position, 2));
+ long timestampNanos = now - parseTimestampNanos(extractBytes(batchRecord, position, 2));
position += 2;
// Combine advertise packet and scan response packet.
@@ -1196,6 +1213,7 @@ public class GattService extends ProfileService {
// Callback for standard advertising instance.
void onAdvertiseCallback(int status, int clientIf) {
if (DBG) Log.d(TAG, "onAdvertiseCallback,- clientIf=" + clientIf + ", status=" + status);
+ mAdvertiseManager.callbackDone(clientIf, status);
}
// Followings are callbacks for Bluetooth LE Advertise operations.
@@ -1425,6 +1443,7 @@ public class GattService extends ProfileService {
int srvcInstanceId, UUID srvcUuid,
int charInstanceId, UUID charUuid, int authReq) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (isHidUuid(charUuid)) enforcePrivilegedPermission();
if (VDBG) Log.d(TAG, "readCharacteristic() - address=" + address);
@@ -1444,6 +1463,7 @@ public class GattService extends ProfileService {
int charInstanceId, UUID charUuid, int writeType,
int authReq, byte[] value) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (isHidUuid(charUuid)) enforcePrivilegedPermission();
if (VDBG) Log.d(TAG, "writeCharacteristic() - address=" + address);
@@ -1466,6 +1486,7 @@ public class GattService extends ProfileService {
int descrInstanceId, UUID descrUuid,
int authReq) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (isHidUuid(charUuid)) enforcePrivilegedPermission();
if (VDBG) Log.d(TAG, "readDescriptor() - address=" + address);
@@ -1489,6 +1510,7 @@ public class GattService extends ProfileService {
int descrInstanceId, UUID descrUuid,
int writeType, int authReq, byte[] value) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (isHidUuid(charUuid)) enforcePrivilegedPermission();
if (VDBG) Log.d(TAG, "writeDescriptor() - address=" + address);
@@ -1529,6 +1551,7 @@ public class GattService extends ProfileService {
int charInstanceId, UUID charUuid,
boolean enable) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (isHidUuid(charUuid)) enforcePrivilegedPermission();
if (DBG) Log.d(TAG, "registerForNotification() - address=" + address + " enable: " + enable);
@@ -1709,9 +1732,6 @@ public class GattService extends ProfileService {
HandleMap.Entry entry = mHandleMap.getByHandle(attrHandle);
if (entry == null) return;
- if (DBG) Log.d(TAG, "onAttributeRead() UUID=" + entry.uuid
- + ", serverIf=" + entry.serverIf + ", type=" + entry.type);
-
mHandleMap.addRequest(transId, attrHandle);
ServerMap.App app = mServerMap.getById(entry.serverIf);
@@ -1767,9 +1787,6 @@ public class GattService extends ProfileService {
HandleMap.Entry entry = mHandleMap.getByHandle(attrHandle);
if (entry == null) return;
- if (DBG) Log.d(TAG, "onAttributeWrite() UUID=" + entry.uuid
- + ", serverIf=" + entry.serverIf + ", type=" + entry.type);
-
mHandleMap.addRequest(transId, attrHandle);
ServerMap.App app = mServerMap.getById(entry.serverIf);
@@ -1830,7 +1847,7 @@ public class GattService extends ProfileService {
}
void onNotificationSent(int connId, int status) throws RemoteException {
- if (DBG) Log.d(TAG, "onNotificationSent() connId=" + connId + ", status=" + status);
+ if (VDBG) Log.d(TAG, "onNotificationSent() connId=" + connId + ", status=" + status);
String address = mServerMap.addressByConnId(connId);
if (address == null) return;
@@ -1862,6 +1879,18 @@ public class GattService extends ProfileService {
}
}
+ void onMtuChanged(int connId, int mtu) throws RemoteException {
+ if (DBG) Log.d(TAG, "onMtuChanged() - connId=" + connId + ", mtu=" + mtu);
+
+ String address = mServerMap.addressByConnId(connId);
+ if (address == null) return;
+
+ ServerMap.App app = mServerMap.getByConnId(connId);
+ if (app == null) return;
+
+ app.callback.onMtuChanged(address, mtu);
+ }
+
/**************************************************************************
* GATT Service functions - SERVER
*************************************************************************/
@@ -2036,6 +2065,13 @@ public class GattService extends ProfileService {
* Private functions
*************************************************************************/
+ private boolean isHidUuid(final UUID uuid) {
+ for (UUID hid_uuid : HID_UUIDS) {
+ if (hid_uuid.equals(uuid)) return true;
+ }
+ return false;
+ }
+
private int getDeviceType(BluetoothDevice device) {
int type = gattClientGetDeviceTypeNative(device.getAddress());
if (DBG) Log.d(TAG, "getDeviceType() - device=" + device
@@ -2246,6 +2282,33 @@ public class GattService extends ProfileService {
return uuids;
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ println(sb, "mAdvertisingServiceUuids:");
+ for (UUID uuid : mAdvertisingServiceUuids) {
+ println(sb, " " + uuid);
+ }
+ println(sb, "mOnFoundResults:");
+ for (ScanResult result : mOnFoundResults.values()) {
+ println(sb, " " + result);
+ }
+ println(sb, "mOnFoundResults:");
+ for (ServiceDeclaration declaration : mServiceDeclarations) {
+ println(sb, " " + declaration);
+ }
+ println(sb, "mMaxScanFilters: " + mMaxScanFilters);
+
+ sb.append("\nGATT Client Map\n");
+ mClientMap.dump(sb);
+
+ sb.append("\nGATT Server Map\n");
+ mServerMap.dump(sb);
+
+ sb.append("\nGATT Handle Map\n");
+ mHandleMap.dump(sb);
+ }
+
/**************************************************************************
* GATT Test functions
*************************************************************************/
@@ -2339,17 +2402,11 @@ public class GattService extends ProfileService {
private native void gattClientReadRemoteRssiNative(int clientIf,
String address);
- private native void gattAdvertiseNative(int client_if, boolean start);
-
private native void gattClientConfigureMTUNative(int conn_id, int mtu);
private native void gattConnectionParameterUpdateNative(int client_if, String address,
int minInterval, int maxInterval, int latency, int timeout);
- private native void gattSetAdvDataNative(int serverIf, boolean setScanRsp, boolean inclName,
- boolean inclTxPower, int minInterval, int maxInterval,
- int appearance, byte[] manufacturerData, byte[] serviceData, byte[] serviceUuid);
-
private native void gattServerRegisterAppNative(long app_uuid_lsb,
long app_uuid_msb);
diff --git a/src/com/android/bluetooth/gatt/HandleMap.java b/src/com/android/bluetooth/gatt/HandleMap.java
index 187625a43..4a2063984 100644
--- a/src/com/android/bluetooth/gatt/HandleMap.java
+++ b/src/com/android/bluetooth/gatt/HandleMap.java
@@ -196,31 +196,28 @@ class HandleMap {
/**
* Logs debug information.
*/
- void dump() {
- StringBuilder b = new StringBuilder();
- b.append( "-------------- GATT Handle Map -----------------");
- b.append("\nEntries: " + mEntries.size());
- b.append("\nRequests: " + mRequestMap.size());
+ void dump(StringBuilder sb) {
+ sb.append(" Entries: " + mEntries.size() + "\n");
+ sb.append(" Requests: " + mRequestMap.size() + "\n");
for (Entry entry : mEntries) {
- b.append("\n" + entry.serverIf + ": [" + entry.handle + "] ");
+ sb.append(" " + entry.serverIf + ": [" + entry.handle + "] ");
switch(entry.type) {
case TYPE_SERVICE:
- b.append("Service " + entry.uuid);
- b.append(", started " + entry.started);
+ sb.append("Service " + entry.uuid);
+ sb.append(", started " + entry.started);
break;
case TYPE_CHARACTERISTIC:
- b.append(" Characteristic " + entry.uuid);
+ sb.append(" Characteristic " + entry.uuid);
break;
case TYPE_DESCRIPTOR:
- b.append(" Descriptor " + entry.uuid);
+ sb.append(" Descriptor " + entry.uuid);
break;
}
- }
- b.append("\n------------------------------------------------");
- Log.d(TAG, b.toString());
+ sb.append("\n");
+ }
}
}
diff --git a/src/com/android/bluetooth/gatt/ScanManager.java b/src/com/android/bluetooth/gatt/ScanManager.java
index 705d49e72..fb3727adf 100644
--- a/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/src/com/android/bluetooth/gatt/ScanManager.java
@@ -452,7 +452,8 @@ public class ScanManager {
}
void startRegularScan(ScanClient client) {
- if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
+ if (isFilteringSupported() && mFilterIndexStack.isEmpty() &&
+ mClientFilterIndexMap.isEmpty()) {
initFilterIndexStack();
}
if (isFilteringSupported()) {
diff --git a/src/com/android/bluetooth/hdp/HealthService.java b/src/com/android/bluetooth/hdp/HealthService.java
index 54cc0ef4a..490930ce3 100644
--- a/src/com/android/bluetooth/hdp/HealthService.java
+++ b/src/com/android/bluetooth/hdp/HealthService.java
@@ -813,6 +813,23 @@ public class HealthService extends ProfileService {
return healthDevices;
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ println(sb, "mHealthChannels:");
+ for (HealthChannel channel : mHealthChannels) {
+ println(sb, " " + channel);
+ }
+ println(sb, "mApps:");
+ for (BluetoothHealthAppConfiguration conf : mApps.keySet()) {
+ println(sb, " " + conf + " : " + mApps.get(conf));
+ }
+ println(sb, "mHealthDevices:");
+ for (BluetoothDevice device : mHealthDevices.keySet()) {
+ println(sb, " " + device + " : " + mHealthDevices.get(device));
+ }
+ }
+
private static class AppInfo {
private IBluetoothHealthCallback mCallback;
private BluetoothHealthDeathRecipient mRcpObj;
diff --git a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
index e302788ce..95e82f02b 100644
--- a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
+++ b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java
@@ -21,6 +21,8 @@ import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.util.Log;
import android.bluetooth.BluetoothDevice;
@@ -69,30 +71,78 @@ class HeadsetPhoneState {
private boolean mListening = false;
+ // when HFP Service Level Connection is established
+ private boolean mSlcReady = false;
+
+ private Context mContext = null;
+
+ private PhoneStateListener mPhoneStateListener = null;
+
+ private SubscriptionManager mSubMgr;
+
+ private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
+ new OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ listenForPhoneState(false);
+ listenForPhoneState(true);
+ }
+ };
+
+
HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) {
mStateMachine = stateMachine;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mContext = context;
+
+ // Register for SubscriptionInfo list changes which is guaranteed
+ // to invoke onSubscriptionInfoChanged and which in turns calls
+ // loadInBackgroud.
+ mSubMgr = SubscriptionManager.from(mContext);
+ mSubMgr.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
}
public void cleanup() {
listenForPhoneState(false);
+ mSubMgr.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+
mTelephonyManager = null;
mStateMachine = null;
}
void listenForPhoneState(boolean start) {
+
+ mSlcReady = start;
+
if (start) {
- if (!mListening) {
+ startListenForPhoneState();
+ } else {
+ stopListenForPhoneState();
+ }
+
+ }
+
+ private void startListenForPhoneState() {
+ if (!mListening && mSlcReady) {
+
+ int subId = SubscriptionManager.getDefaultSubId();
+
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mPhoneStateListener = getPhoneStateListener(subId);
+
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE |
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
mListening = true;
}
- } else {
- if (mListening) {
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
- mListening = false;
- }
+ }
+ }
+
+ private void stopListenForPhoneState() {
+ if (mListening) {
+
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mListening = false;
}
}
@@ -203,31 +253,37 @@ class HeadsetPhoneState {
}
}
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onServiceStateChanged(ServiceState serviceState) {
- mServiceState = serviceState;
- mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ?
- HeadsetHalConstants.NETWORK_STATE_AVAILABLE :
- HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
- setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING
- : HeadsetHalConstants.SERVICE_TYPE_HOME);
- sendDeviceStateChanged();
- }
+ private PhoneStateListener getPhoneStateListener(int subId) {
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener(subId) {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- int prevSignal = mSignal;
- if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE)
- mSignal = 0;
- else
- mSignal = (signalStrength.getLevel() == 4) ? 5 : signalStrength.getLevel();
- // network signal strength is scaled to BT 1-5 levels.
- // This results in a lot of duplicate messages, hence this check
- if (prevSignal != mSignal)
- sendDeviceStateChanged();
- }
- };
+ mServiceState = serviceState;
+ mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ?
+ HeadsetHalConstants.NETWORK_STATE_AVAILABLE :
+ HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE;
+ setRoam(serviceState.getRoaming() ? HeadsetHalConstants.SERVICE_TYPE_ROAMING
+ : HeadsetHalConstants.SERVICE_TYPE_HOME);
+
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+
+ int prevSignal = mSignal;
+ if (mService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE)
+ mSignal = 0;
+ else
+ mSignal = (signalStrength.getLevel() == 4) ? 5 : signalStrength.getLevel();
+
+ // network signal strength is scaled to BT 1-5 levels.
+ // This results in a lot of duplicate messages, hence this check
+ if (prevSignal != mSignal)
+ sendDeviceStateChanged();
+ }
+ };
+ return mPhoneStateListener;
+ }
}
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index aabe56e68..46629c1e4 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -141,7 +141,7 @@ public class HeadsetService extends ProfileService {
}
private HeadsetService getService() {
- if (!Utils.checkCaller()) {
+ if (!Utils.checkCallerAllowManagedProfiles(mService)) {
Log.w(TAG,"Headset call not allowed for non-active user");
return null;
}
@@ -581,4 +581,11 @@ public class HeadsetService extends ProfileService {
return true;
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ if (mStateMachine != null) {
+ mStateMachine.dump(sb);
+ }
+ }
}
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 0bec29172..f77a613a0 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -57,11 +57,13 @@ import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.os.PowerManager.WakeLock;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.ProfileService;
import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -370,6 +372,20 @@ final class HeadsetStateMachine extends StateMachine {
}
}
+ public void dump(StringBuilder sb) {
+ ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
+ ProfileService.println(sb, "mTargetDevice: " + mTargetDevice);
+ ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice);
+ ProfileService.println(sb, "mActiveScoDevice: " + mActiveScoDevice);
+ ProfileService.println(sb, "mMultiDisconnectDevice: " + mMultiDisconnectDevice);
+ ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted);
+ ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted);
+ ProfileService.println(sb, "mWaitingForVoiceRecognition: " + mWaitingForVoiceRecognition);
+ ProfileService.println(sb, "StateMachine: " + this.toString());
+ ProfileService.println(sb, "mPhoneState: " + mPhoneState);
+ ProfileService.println(sb, "mAudioState: " + mAudioState);
+ }
+
private class Disconnected extends State {
@Override
public void enter() {
@@ -2643,7 +2659,8 @@ final class HeadsetStateMachine extends StateMachine {
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
+ mService.sendBroadcastAsUser(intent, UserHandle.ALL,
+ HeadsetService.BLUETOOTH_PERM);
}
private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
@@ -2656,7 +2673,7 @@ final class HeadsetStateMachine extends StateMachine {
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
+ mService.sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM);
Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState);
}
@@ -2681,7 +2698,8 @@ final class HeadsetStateMachine extends StateMachine {
intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
+ "." + Integer.toString(companyId));
- mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
+ mService.sendBroadcastAsUser(intent, UserHandle.ALL,
+ HeadsetService.BLUETOOTH_PERM);
}
private void configAudioParameters(BluetoothDevice device)
@@ -3196,14 +3214,14 @@ final class HeadsetStateMachine extends StateMachine {
String number = mPhoneProxy.getSubscriberNumber();
if (number != null) {
atResponseStringNative("+CNUM: ,\"" + number + "\"," +
- PhoneNumberUtils.toaFromString(number) +
- ",,4", getByteAddress(device));
+ PhoneNumberUtils.toaFromString(number) +
+ ",,4", getByteAddress(device));
atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
- 0, getByteAddress(device));
+ 0, getByteAddress(device));
} else {
- log("number is null");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0,
- getByteAddress(device));
+ Log.e(TAG, "getSubscriberNumber returns null");
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK,
+ 0, getByteAddress(device));
}
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 733d8300b..09e3855a7 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -748,4 +748,12 @@ public class HeadsetClientService extends ProfileService {
}
return mStateMachine.getCurrentAgFeatures();
}
+
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ if (mStateMachine != null) {
+ mStateMachine.dump(sb);
+ }
+ }
}
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index bf01bbf34..a523cdc91 100644
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -158,6 +158,35 @@ final class HeadsetClientStateMachine extends StateMachine {
classInitNative();
}
+ public void dump(StringBuilder sb) {
+ ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
+ ProfileService.println(sb, "mAudioOn: " + mAudioOn);
+ ProfileService.println(sb, "mAudioState: " + mAudioState);
+ ProfileService.println(sb, "mAudioWbs: " + mAudioWbs);
+ ProfileService.println(sb, "mIndicatorNetworkState: " + mIndicatorNetworkState);
+ ProfileService.println(sb, "mIndicatorNetworkType: " + mIndicatorNetworkType);
+ ProfileService.println(sb, "mIndicatorNetworkSignal: " + mIndicatorNetworkSignal);
+ ProfileService.println(sb, "mIndicatorBatteryLevel: " + mIndicatorBatteryLevel);
+ ProfileService.println(sb, "mIndicatorCall: " + mIndicatorCall);
+ ProfileService.println(sb, "mIndicatorCallSetup: " + mIndicatorCallSetup);
+ ProfileService.println(sb, "mIndicatorCallHeld: " + mIndicatorCallHeld);
+ ProfileService.println(sb, "mVgsFromStack: " + mVgsFromStack);
+ ProfileService.println(sb, "mVgmFromStack: " + mVgmFromStack);
+ ProfileService.println(sb, "mRingtone: " + mRingtone);
+ ProfileService.println(sb, "mOperatorName: " + mOperatorName);
+ ProfileService.println(sb, "mSubscriberInfo: " + mSubscriberInfo);
+ ProfileService.println(sb, "mVoiceRecognitionActive: " + mVoiceRecognitionActive);
+ ProfileService.println(sb, "mInBandRingtone: " + mInBandRingtone);
+ ProfileService.println(sb, "mCalls:");
+ for (BluetoothHeadsetClientCall call : mCalls.values()) {
+ ProfileService.println(sb, " " + call);
+ }
+ ProfileService.println(sb, "mCallsUpdate:");
+ for (BluetoothHeadsetClientCall call : mCallsUpdate.values()) {
+ ProfileService.println(sb, " " + call);
+ }
+ }
+
private void clearPendingAction() {
mPendingAction = new Pair<Integer, Object>(NO_ACTION, 0);
}
diff --git a/src/com/android/bluetooth/hid/HidService.java b/src/com/android/bluetooth/hid/HidService.java
index acfe468c5..239b9b6c7 100644
--- a/src/com/android/bluetooth/hid/HidService.java
+++ b/src/com/android/bluetooth/hid/HidService.java
@@ -778,6 +778,16 @@ public class HidService extends ProfileService {
}
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ println(sb, "mTargetDevice: " + mTargetDevice);
+ println(sb, "mInputDevices:");
+ for (BluetoothDevice device : mInputDevices.keySet()) {
+ println(sb, " " + device + " : " + mInputDevices.get(device));
+ }
+ }
+
// Constants matching Hal header file bt_hh.h
// bthh_connection_state_t
private final static int CONN_STATE_CONNECTED = 0;
diff --git a/src/com/android/bluetooth/map/BluetoothMapContent.java b/src/com/android/bluetooth/map/BluetoothMapContent.java
index 05cd7c46c..df15f66d8 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContent.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContent.java
@@ -56,6 +56,20 @@ import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
import com.google.android.mms.pdu.CharacterSets;
import com.google.android.mms.pdu.PduHeaders;
import android.database.sqlite.SQLiteException;
+import com.android.bluetooth.map.BluetoothMapAppParams;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.*;
@@ -311,8 +325,15 @@ public class BluetoothMapContent {
if (D) Log.d(TAG, sb.toString());
} catch (IOException e) {
- // do nothing for now
- e.printStackTrace();
+ // do nothing for now
+ e.printStackTrace();
+ }
+ }
+
+ private static void close(Closeable c) {
+ try {
+ if (c != null) c.close();
+ } catch (IOException e) {
}
}
@@ -929,10 +950,6 @@ public class BluetoothMapContent {
}
}
- if( name ==null) {
- //set empty value
- name = "";
- }
if (D) Log.d(TAG, "setSenderName: " + name);
e.setSenderName(name);
}
@@ -973,7 +990,7 @@ public class BluetoothMapContent {
Uri uriAddress = Uri.parse(uriStr);
Cursor c = mResolver.query(uriAddress, null, selection,
null, null);
-
+ // TODO: maybe use a projection with only "ct" and "text"
if (c != null && c.moveToFirst()) {
do {
String ct = c.getString(c.getColumnIndex("ct"));
@@ -1103,14 +1120,13 @@ public class BluetoothMapContent {
String orderBy = Contacts.DISPLAY_NAME + " ASC";
Cursor c = mResolver.query(uri, projection, selection, null, orderBy);
-
- if (c != null && c.getCount() >= 1) {
- c.moveToFirst();
- name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
- }
-
- if (c != null) {
- c.close();
+ try {
+ if (c != null && c.getCount() >= 1) {
+ c.moveToFirst();
+ name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
+ };
+ } finally {
+ close(c);
}
return name;
}
@@ -1120,15 +1136,16 @@ public class BluetoothMapContent {
String uriStr = String.format("content://mms/%d/addr", id);
Uri uriAddress = Uri.parse(uriStr);
String addr = null;
- Cursor c = r.query(uriAddress, null, selection, null, null);
- if (c != null && c.moveToFirst()) {
- addr = c.getString(c.getColumnIndex("address"));
+ Cursor c = r.query(uriAddress, null, selection, null, null);
+ try {
+ if (c != null && c.moveToFirst()) {
+ addr = c.getString(c.getColumnIndex(Mms.Addr.ADDRESS));
+ }
+ } finally {
+ close(c);
}
- if (c != null) {
- c.close();
- }
return addr;
}
@@ -1511,32 +1528,31 @@ public class BluetoothMapContent {
new String[]{str},
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
- while (c != null && c.moveToNext()) {
- String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
+ try {
+ while (c != null && c.moveToNext()) {
+ String contactId = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
- Cursor p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
- new String[]{contactId},
- null);
+ Cursor p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
+ new String[]{contactId},
+ null);
- while (p != null && p.moveToNext()) {
- String number = p.getString(
- p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
+ try {
+ while (p != null && p.moveToNext()) {
+ String number = p.getString(
+ p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
- where += " address = " + "'" + number + "'";
- if (!p.isLast()) {
- where += " OR ";
+ where += " address = " + "'" + number + "'";
+ if (!p.isLast()) where += " OR ";
+ }
+ } finally {
+ close(p);
}
+
+ if (!c.isLast()) where += " OR ";
}
- if (!c.isLast()) {
- where += " OR ";
- }
- if (p != null) {
- p.close();
- }
- }
- if (c != null) {
- c.close();
+ } finally {
+ close(c);
}
if (str != null && str.length() > 0) {
@@ -2217,16 +2233,7 @@ public class BluetoothMapContent {
fi.msgType = FilterInfo.TYPE_EMAIL;
String where = setWhereFilter(folder, fi, ap);
- where+= " AND "+ Message.FLAG_LOADED_SELECTION;
- where+= " order by timeStamp desc ";
- //Fetch only maxListCount emails from startOffset
- if(ap.getMaxListCount() > 0 && ap.getMaxListCount() < 65536) {
- where+=" LIMIT "+ap.getMaxListCount();
- }
- if(ap.getStartOffset() > 0 && ap.getStartOffset() < 65536) {
- where+=" OFFSET "+ap.getStartOffset();
- }
- if (V) Log.d(TAG, "where clause is = " + where);
+ Log.d(TAG, "where clause is = " + where);
try {
Cursor c = mResolver.query(uriEmail,
EMAIL_PROJECTION, where + " AND " + Message.FLAG_LOADED_SELECTION, null, "timeStamp desc");
@@ -2252,8 +2259,7 @@ public class BluetoothMapContent {
/* Enable this if post sorting and segmenting needed */
bmList.sort();
- //Handle OFFSET and MAXLISTCOUNT from DB query
- //bmList.segment(ap.getMaxListCount(), ap.getStartOffset());
+ bmList.segment(ap.getMaxListCount(), ap.getStartOffset());
return bmList;
}
@@ -2334,23 +2340,14 @@ public class BluetoothMapContent {
/* Cache some info used throughout filtering */
FilterInfo fi = new FilterInfo();
setFilterInfo(fi);
-
if (smsSelected(fi, ap)) {
fi.msgType = FilterInfo.TYPE_SMS;
if(ap.getFilterPriority() != 1){ /*SMS cannot have high priority*/
String where = setWhereFilter(folder, fi, ap);
- Cursor c =null;
- //Fetch only maxListCount messages from startOffset
- if(ap.getStartOffset() > 0 && ap.getStartOffset() < 65536) {
- c = mResolver.query(Sms.CONTENT_URI,
- SMS_PROJECTION, where, null, "date DESC" + " limit " +
- ap.getMaxListCount()+" offset "+ ap.getStartOffset());
- }else {
- c = mResolver.query(Sms.CONTENT_URI,
- SMS_PROJECTION, where, null, "date DESC" + " limit " +
- ap.getMaxListCount());
- }
- if (V) Log.d(TAG, "where clause is = " + where);
+
+ Cursor c = mResolver.query(Sms.CONTENT_URI,
+ SMS_PROJECTION, where, null, "date DESC");
+
if (c != null) {
while (c.moveToNext()) {
if (matchAddresses(c, fi, ap)) {
@@ -2366,20 +2363,12 @@ public class BluetoothMapContent {
if (mmsSelected(fi, ap)) {
fi.msgType = FilterInfo.TYPE_MMS;
+
String where = setWhereFilter(folder, fi, ap);
where += " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE;
- Cursor c =null;
- //Fetch only maxListCount messages from startOffset
- if(ap.getStartOffset() > 0 && ap.getStartOffset() < 65536) {
- c = mResolver.query(Mms.CONTENT_URI,
- MMS_PROJECTION, where, null, "date DESC" + " limit " +
- ap.getMaxListCount()+" offset "+ ap.getStartOffset());
- } else {
- c = mResolver.query(Mms.CONTENT_URI,
- MMS_PROJECTION, where, null, "date DESC" + " limit " +
- ap.getMaxListCount());
- }
- if (V) Log.d(TAG, "where clause is = " + where);
+ Cursor c = mResolver.query(Mms.CONTENT_URI,
+ MMS_PROJECTION, where, null, "date DESC");
+
if (c != null) {
int cnt = 0;
while (c.moveToNext()) {
@@ -2392,10 +2381,10 @@ public class BluetoothMapContent {
c.close();
}
}
+
/* Enable this if post sorting and segmenting needed */
bmList.sort();
- //Handle OFFSET and MAXLISTCOUNT from DB query
- //bmList.segment(ap.getMaxListCount(), ap.getStartOffset());
+ bmList.segment(ap.getMaxListCount(), ap.getStartOffset());
return bmList;
}
@@ -2415,8 +2404,8 @@ public class BluetoothMapContent {
SMS_PROJECTION, where, null, "date DESC");
if (c != null) {
- cnt = c.getCount();
- c.close();
+ cnt = c.getCount();
+ c.close();
}
}
@@ -2458,10 +2447,8 @@ public class BluetoothMapContent {
Cursor c = mResolver.query(Sms.CONTENT_URI,
SMS_PROJECTION, where, null, "date DESC");
- if (c != null) {
- cnt = c.getCount();
- c.close();
- }
+ if (c != null) cnt += c.getCount();
+ c.close();
}
if (mmsSelected(fi, ap)) {
@@ -2473,8 +2460,8 @@ public class BluetoothMapContent {
MMS_PROJECTION, where, null, "date DESC");
if (c != null) {
- cnt += c.getCount();
- c.close();
+ cnt += c.getCount();
+ c.close();
}
}
@@ -2568,7 +2555,6 @@ public class BluetoothMapContent {
String contactId = null, contactName = null;
String[] phoneNumbers = null;
String[] emailAddresses = null;
- Cursor p;
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phone));
@@ -2578,53 +2564,42 @@ public class BluetoothMapContent {
String orderBy = Contacts._ID + " ASC";
// Get the contact _ID and name
- p = mResolver.query(uri, projection, selection, null, orderBy);
- if (p != null && p.getCount() >= 1) {
- p.moveToFirst();
- contactId = p.getString(p.getColumnIndex(Contacts._ID));
- contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME));
- }
- if (p != null)
- p.close();
-
- // Add only original sender's contact number in VCARD.
- phoneNumbers = new String[1];
- phoneNumbers[0] = phone;
- if(contactId != null) {
- // Do not fetch and add all the contacts in vcard to avoid IOT issues with carkits
- /*
- // Fetch all contact phone numbers
- p = mResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
- new String[]{contactId},
- null);
- if(p != null) {
- int i = 0;
- phoneNumbers = new String[p.getCount()];
- while (p != null && p.moveToNext()) {
- String number = p.getString(
- p.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
- phoneNumbers[i++] = number;
- }
- p.close();
+ Cursor p = mResolver.query(uri, projection, selection, null, orderBy);
+
+ try {
+ if (p != null && p.getCount() >= 1) {
+ p.moveToFirst();
+ contactId = p.getString(p.getColumnIndex(Contacts._ID));
+ contactName = p.getString(p.getColumnIndex(Contacts.DISPLAY_NAME));
}
- */
- // Fetch contact e-mail addresses
- p = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
- new String[]{contactId},
- null);
- if(p != null) {
- int i = 0;
- emailAddresses = new String[p.getCount()];
- while (p != null && p.moveToNext()) {
- String emailAddress = p.getString(
- p.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS));
- emailAddresses[i++] = emailAddress;
+ // Bail out if we are unable to find a contact, based on the phone number
+ if(contactId == null) {
+ phoneNumbers = new String[1];
+ phoneNumbers[0] = phone;
+ } else {
+ // use only actual phone number
+ phoneNumbers = new String[1];
+ phoneNumbers[0] = phone;
+
+ // Fetch contact e-mail addresses
+ close (p);
+ p = mResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
+ new String[]{contactId},
+ null);
+ if (p != null) {
+ int i = 0;
+ emailAddresses = new String[p.getCount()];
+ while (p != null && p.moveToNext()) {
+ String emailAddress = p.getString(
+ p.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS));
+ emailAddresses[i++] = emailAddress;
+ }
}
- p.close();
}
+ } finally {
+ close(p);
}
if(incoming == true)
message.addOriginator(contactName, contactName, phoneNumbers, emailAddresses); // Use version 3.0 as we only have a formatted name
@@ -2641,11 +2616,13 @@ public class BluetoothMapContent {
String msgBody;
BluetoothMapbMessageSms message = new BluetoothMapbMessageSms();
TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
- Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null);
- if(c != null && c.moveToFirst())
- {
+ Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, "_ID = " + id, null, null);
+ if (c == null || !c.moveToFirst()) {
+ throw new IllegalArgumentException("SMS handle not found");
+ }
+ try {
if(V) Log.v(TAG,"c.count: " + c.getCount());
if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
@@ -2685,12 +2662,11 @@ public class BluetoothMapContent {
} else /*if (charset == MAP_MESSAGE_CHARSET_UTF8)*/ {
message.setSmsBody(msgBody);
}
-
- c.close();
-
- return message.encode();
+ } finally {
+ close(c);
}
- throw new IllegalArgumentException("SMS handle not found");
+
+ return message.encode();
}
private void extractMmsAddresses(long id, BluetoothMapbMessageMmsEmail message) {
@@ -2850,6 +2826,7 @@ public class BluetoothMapContent {
} while(c.moveToNext());
c.close();
}
+
message.updateCharset();
}
@@ -2865,8 +2842,11 @@ public class BluetoothMapContent {
int msgBox, threadId;
BluetoothMapbMessageMmsEmail message = new BluetoothMapbMessageMmsEmail();
Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, "_ID = " + id, null, null);
- if(c != null && c.moveToFirst())
- {
+ if (c == null || !c.moveToFirst()) {
+ throw new IllegalArgumentException("MMS handle not found");
+ }
+
+ try {
message.setType(TYPE.MMS);
// The MMS info:
@@ -2886,24 +2866,13 @@ public class BluetoothMapContent {
message.setDate(c.getLong(c.getColumnIndex(Mms.DATE)) * 1000L);
message.setTextOnly(c.getInt(c.getColumnIndex(Mms.TEXT_ONLY)) == 0 ? false : true); // - TODO: Do we need this - yes, if we have only text, we should not make this a multipart message
message.setIncludeAttachments(appParams.getAttachment() == 0 ? false : true);
- // c.getLong(c.getColumnIndex(Mms.DATE_SENT)); - this is never used
- // c.getInt(c.getColumnIndex(Mms.STATUS)); - don't know what this is
- // The parts
extractMmsParts(id, message);
-
- // The addresses
extractMmsAddresses(id, message);
-
- c.close();
-
- return message.encode();
- }
- else if(c != null) {
- c.close();
+ } finally {
+ close(c);
}
- throw new IllegalArgumentException("MMS handle not found");
+ return message.encode();
}
-
}
diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index 4a8caaafb..e746ce927 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -14,7 +14,10 @@
*/
package com.android.bluetooth.map;
+
import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
@@ -83,6 +86,13 @@ public class BluetoothMapContentObserver {
private TYPE mSmsType;
+ private static void close(Closeable c) {
+ try {
+ if (c != null) c.close();
+ } catch (IOException e) {
+ }
+ }
+
static final String[] SMS_PROJECTION = new String[] {
BaseColumns._ID,
Sms.THREAD_ID,
@@ -362,41 +372,53 @@ public class BluetoothMapContentObserver {
mMsgListSms.clear();
mMsgListMms.clear();
+
HashMap<Long, Msg> msgListSms = new HashMap<Long, Msg>();
Cursor c = mResolver.query(Sms.CONTENT_URI,
SMS_PROJECTION, null, null, null);
- if (c != null && c.moveToFirst()) {
- do {
- long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
- int type = c.getInt(c.getColumnIndex(Sms.TYPE));
- Msg msg = new Msg(id, type);
- msgListSms.put(id, msg);
- } while (c.moveToNext());
- c.close();
- }
+ try {
+ while (c != null && c.moveToNext()) {
+ long id = c.getLong(c.getColumnIndex(Sms._ID));
+ int type = c.getInt(c.getColumnIndex(Sms.TYPE));
+
+
+ Msg msg = new Msg(id, type);
+ msgListSms.put(id, msg);
+ }
+ } finally {
+ close(c);
+ }
+
+ synchronized(mMsgListSms) {
+ mMsgListSms.clear();
+ mMsgListSms = msgListSms;
+ }
+
+
- mMsgListSms = msgListSms;
HashMap<Long, Msg> msgListMms = new HashMap<Long, Msg>();
- c = mResolver.query(Mms.CONTENT_URI,
- MMS_PROJECTION, null, null, null);
+ try {
+ while (c != null && c.moveToNext()) {
+ long id = c.getLong(c.getColumnIndex(Mms._ID));
+ int type = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX));
- if (c != null && c.moveToFirst()) {
- do {
- long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
- int type = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX));
- Msg msg = new Msg(id, type);
- msgListMms.put(id, msg);
- } while (c.moveToNext());
- c.close();
- }
+ Msg msg = new Msg(id, type );
+ msgListMms.put(id, msg);
+ }
+ } finally {
+ close(c);
+ }
- mMsgListMms = msgListMms;
+ synchronized(mMsgListMms) {
+ mMsgListMms.clear();
+ mMsgListMms = msgListMms;
+ }
}
private void handleMsgListChangesSms() {
@@ -535,7 +557,9 @@ public class BluetoothMapContentObserver {
mResolver.update(uri, contentValues, null, null);
} else {
/* Delete from observer message list to avoid delete notifications */
- mMsgListMms.remove(handle);
+ synchronized(mMsgListMms) {
+ mMsgListMms.remove(handle);
+ }
/* Delete message */
mResolver.delete(uri, null, null);
}
@@ -544,6 +568,7 @@ public class BluetoothMapContentObserver {
if (c != null) {
c.close();
}
+
return res;
}
@@ -603,7 +628,9 @@ public class BluetoothMapContentObserver {
mResolver.update(uri, contentValues, null, null);
} else {
/* Delete from observer message list to avoid delete notifications */
- mMsgListSms.remove(handle);
+ synchronized(mMsgListSms) {
+ mMsgListSms.remove(handle);
+ }
/* Delete message */
mResolver.delete(uri, null, null);
}
@@ -757,15 +784,39 @@ public class BluetoothMapContentObserver {
case SMS_CDMA:
{
/* Add the message to the database */
+
String phone = recipient.getFirstPhoneNumber();
+
String msgBody = ((BluetoothMapbMessageSms) msg).getSmsBody();
- Uri contentUri = Uri.parse("content://sms/" + folder);
- Uri uri = Sms.addMessageToUri(mResolver, contentUri, phone, msgBody,
- "", System.currentTimeMillis(), read, deliveryReport);
- if (uri == null) {
- Log.d(TAG, "pushMessage - failure on add to uri " + contentUri);
- return -1;
+ /* We need to lock the SMS list while updating the database, to avoid sending
+ * events on MCE initiated operation. */
+ Uri contentUri = Uri.parse(Sms.CONTENT_URI+ "/" + folder);
+ Uri uri;
+ synchronized(mMsgListSms) {
+ uri = Sms.addMessageToUri(mResolver, contentUri, phone, msgBody,
+ "", System.currentTimeMillis(), read, deliveryReport);
+
+ if(V) Log.v(TAG, "Sms.addMessageToUri() returned: " + uri);
+ if (uri == null) {
+ if (D) Log.d(TAG, "pushMessage - failure on add to uri " + contentUri);
+ return -1;
+ }
+ Cursor c = mResolver.query(uri, SMS_PROJECTION, null, null, null);
+ try {
+ /* Extract the data for the inserted message, and store in local mirror, to
+ * avoid sending a NewMessage Event. */
+ if (c != null && c.moveToFirst()) {
+ long id = c.getLong(c.getColumnIndex(Sms._ID));
+ int type = c.getInt(c.getColumnIndex(Sms.TYPE));
+ Msg newMsg = new Msg(id, type );
+ mMsgListSms.put(id, newMsg);
+ } else {
+ return -1; // This can only happen, if the message is deleted just as it is added
+ }
+ } finally {
+ close(c);
+ }
}
handle = Long.parseLong(uri.getLastPathSegment());
@@ -836,7 +887,6 @@ public class BluetoothMapContentObserver {
}
-
private void moveDraftToOutbox(long handle) {
ContentResolver contentResolver = mContext.getContentResolver();
/*Move message by changing the msg_box value in the content provider database */
@@ -844,7 +894,8 @@ public class BluetoothMapContentObserver {
String whereClause = " _id= " + handle;
Uri uri = Uri.parse("content://mms");
Cursor queryResult = contentResolver.query(uri, null, whereClause, null, null);
- if (queryResult != null) {
+ try {
+ if (queryResult != null) {
if (queryResult.getCount() > 0) {
queryResult.moveToFirst();
ContentValues data = new ContentValues();
@@ -853,10 +904,14 @@ public class BluetoothMapContentObserver {
contentResolver.update(uri, data, whereClause, null);
Log.d(TAG, "moved draft MMS to outbox");
}
- queryResult.close();
+
addMceInitiatedOperation(Long.toString(handle));
- }else {
+ } else {
Log.d(TAG, "Could not move draft to outbox ");
+
+ }
+ } finally {
+ queryResult.close();
}
}
}
@@ -894,26 +949,51 @@ public class BluetoothMapContentObserver {
// Get thread id
Set<String> recipients = new HashSet<String>();
recipients.addAll(Arrays.asList(to_address));
- values.put("thread_id", Telephony.Threads.getOrCreateThreadId(mContext, recipients));
- Uri uri = Uri.parse("content://mms");
+ values.put(Mms.THREAD_ID, Telephony.Threads.getOrCreateThreadId(mContext, recipients));
+ Uri uri = Mms.CONTENT_URI;
- ContentResolver cr = mContext.getContentResolver();
- uri = cr.insert(uri, values);
+ synchronized (mMsgListMms) {
+ uri = mResolver.insert(uri, values);
- if (uri == null) {
- // unable to insert MMS
- Log.e(TAG, "Unabled to insert MMS " + values + "Uri: " + uri);
- return -1;
- }
+ if (uri == null) {
+ // unable to insert MMS
+ Log.e(TAG, "Unabled to insert MMS " + values + "Uri: " + uri);
+ return -1;
+ }
+ /* As we already have all the values we need, we could skip the query, but
+ doing the query ensures we get any changes made by the content provider
+ at insert. */
+ Cursor c = mResolver.query(uri, MMS_PROJECTION, null, null, null);
+ try {
+ if (c != null && c.moveToFirst()) {
+ long id = c.getLong(c.getColumnIndex(Mms._ID));
+ int type = c.getInt(c.getColumnIndex(Mms.MESSAGE_BOX));
+
+
+ /* We must filter out any actions made by the MCE. Add the new message to
+ * the list of known messages. */
+
+ Msg newMsg = new Msg(id, type);
+ mMsgListMms.put(id, newMsg);
+ }
+ } finally {
+ close(c);
+ }
+ } // Done adding changes, unlock access to mMsgListMms to allow sending MMS events again
long handle = Long.parseLong(uri.getLastPathSegment());
- ArrayList<MimePart> parts = msg.getMimeParts();
- if (parts != null) {
- Log.v(TAG, " NEW URI " + uri.toString());
- try {
- for(MimePart part : parts) {
- int count = 0;
- count++;
+ if (V) Log.v(TAG, " NEW URI " + uri.toString());
+
+ try {
+ if(msg.getMimeParts() == null) {
+ /* Perhaps this message have been deleted, and no longer have any content, but only headers */
+ Log.w(TAG, "No MMS parts present...");
+ } else {
+ if(V) Log.v(TAG, "Adding " + msg.getMimeParts().size() + " parts to the data base.");
+ int count = 0;
+ for(MimePart part : msg.getMimeParts()) {
+ ++count;
+
values.clear();
if(part.contentType != null &&
part.contentType.toUpperCase().contains("TEXT")) {
@@ -936,7 +1016,7 @@ public class BluetoothMapContentObserver {
values.put("cd", part.contentDisposition);
values.put("text", new String(part.data, "UTF-8"));
uri = Uri.parse("content://mms/" + handle + "/part");
- uri = cr.insert(uri, values);
+ uri = mResolver.insert(uri, values);
if(V) Log.v(TAG, "Added TEXT part");
} else if (part.contentType != null &&
part.contentType.toUpperCase().contains("SMIL")) {
@@ -953,7 +1033,7 @@ public class BluetoothMapContentObserver {
values.put("text", new String(part.data, "UTF-8"));
uri = Uri.parse("content://mms/" + handle + "/part");
- uri = cr.insert(uri, values);
+ uri = mResolver.insert(uri, values);
if(V) Log.v(TAG, "Added SMIL part");
} else /*VIDEO/AUDIO/IMAGE*/ {
writeMmsDataPart(handle, part, count);
@@ -965,12 +1045,13 @@ public class BluetoothMapContentObserver {
}
}
addMceInitiatedOperation("+");
- } catch (UnsupportedEncodingException e) {
+ }
+ }
+ catch (UnsupportedEncodingException e) {
Log.w(TAG, e);
} catch (IOException e) {
Log.w(TAG, e);
}
- }
values.clear();
values.put("contact_id", "null");
values.put("address", "insert-address-token");
@@ -978,7 +1059,7 @@ public class BluetoothMapContentObserver {
values.put("charset", 106);
uri = Uri.parse("content://mms/" + handle + "/addr");
- uri = cr.insert(uri, values);
+ uri = mResolver.insert(uri, values);
if (uri != null && V){
Log.v(TAG, " NEW URI " + uri.toString());
}
@@ -990,7 +1071,7 @@ public class BluetoothMapContentObserver {
values.put("charset", 106);
uri = Uri.parse("content://mms/" + handle + "/addr");
- uri = cr.insert(uri, values);
+ uri = mResolver.insert(uri, values);
if (uri != null && V){
Log.v(TAG, " NEW URI " + uri.toString());
}
@@ -1235,30 +1316,34 @@ public class BluetoothMapContentObserver {
String where = "type = " + Sms.MESSAGE_TYPE_OUTBOX;
Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null,
null);
+ if (c == null) return;
+ try {
+ while (c!= null && c.moveToNext()) {
+ long id = c.getLong(c.getColumnIndex(Sms._ID));
- if (c != null && c.moveToFirst()) {
- do {
- long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
String msgBody = c.getString(c.getColumnIndex(Sms.BODY));
PushMsgInfo msgInfo = mPushMsgList.get(id);
if (msgInfo == null || msgInfo.resend == false) {
continue;
}
sendMessage(msgInfo, msgBody);
- } while (c.moveToNext());
- c.close();
+ }
+ } finally {
+ close(c);
}
}
private void failPendingMessages() {
/* Move pending messages from outbox to failed */
String where = "type = " + Sms.MESSAGE_TYPE_OUTBOX;
- Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null,
- null);
+ Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null, null);
+ if (c == null) return;
+
+
+ try {
+ while (c!= null && c.moveToNext()) {
+ long id = c.getLong(c.getColumnIndex(Sms._ID));
- if (c != null && c.moveToFirst()) {
- do {
- long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
String msgBody = c.getString(c.getColumnIndex(Sms.BODY));
PushMsgInfo msgInfo = mPushMsgList.get(id);
if (msgInfo == null || msgInfo.resend == false) {
@@ -1266,9 +1351,10 @@ public class BluetoothMapContentObserver {
}
Sms.moveMessageToFolder(mContext, msgInfo.uri,
Sms.MESSAGE_TYPE_FAILED, 0);
- } while (c.moveToNext());
+ }
+ } finally {
+ close(c);
}
- if (c != null) c.close();
}
private void removeDeletedMessages() {
@@ -1277,7 +1363,7 @@ public class BluetoothMapContentObserver {
"thread_id = " + DELETED_THREAD_ID, null);
}
- private PhoneStateListener mPhoneListener = new PhoneStateListener (Long.MAX_VALUE - 1,
+ private PhoneStateListener mPhoneListener = new PhoneStateListener (Integer.MAX_VALUE - 1,
Looper.getMainLooper()) {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
diff --git a/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/src/com/android/bluetooth/map/BluetoothMapObexServer.java
index 644191f80..bcb3bcc0a 100755
--- a/src/com/android/bluetooth/map/BluetoothMapObexServer.java
+++ b/src/com/android/bluetooth/map/BluetoothMapObexServer.java
@@ -148,6 +148,26 @@ public class BluetoothMapObexServer extends ServerRequestHandler {
tmpFolder.addFolder("drafts");
}
+ private void addEmailFolders(BluetoothMapFolderElement parentFolder) throws RemoteException {
+ // Select all parent folders
+ BluetoothMapFolderElement newFolder;
+
+ String where = BluetoothMapContract.FolderColumns.PARENT_FOLDER_ID +
+ " = " + parentFolder.getEmailFolderId();
+ Cursor c = mProviderClient.query(mEmailFolderUri,
+ BluetoothMapContract.BT_FOLDER_PROJECTION, where, null, null);
+ try {
+ while (c != null && c.moveToNext()) {
+ String name = c.getString(c.getColumnIndex(BluetoothMapContract.FolderColumns.NAME));
+ long id = c.getLong(c.getColumnIndex(BluetoothMapContract.FolderColumns._ID));
+ newFolder = parentFolder.addEmailFolder(name, id);
+ addEmailFolders(newFolder); // Use recursion to add any sub folders
+ }
+ } finally {
+ if (c != null) c.close();
+ }
+ }
+
@Override
public int onConnect(final HeaderSet request, HeaderSet reply) {
if (D) Log.d(TAG, "onConnect():");
diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java
index 73963b48d..672079822 100644
--- a/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -1205,4 +1205,26 @@ public class BluetoothMapService extends ProfileService {
return service.getPriority(device);
}
};
+
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ println(sb, "mRemoteDevice: " + mRemoteDevice);
+ println(sb, "sRemoteDeviceName: " + sRemoteDeviceName);
+ println(sb, "mState: " + mState);
+ println(sb, "mAppObserver: " + mAppObserver);
+ println(sb, "mIsWaitingAuthorization: " + mIsWaitingAuthorization);
+ println(sb, "mRemoveTimeoutMsg: " + mRemoveTimeoutMsg);
+ println(sb, "mPermission: " + mPermission);
+ println(sb, "mAccountChanged: " + mAccountChanged);
+ println(sb, "mBluetoothMnsObexClient: " + mBluetoothMnsObexClient);
+ println(sb, "mMasInstanceMap:");
+ for (BluetoothMapEmailSettingsItem key : mMasInstanceMap.keySet()) {
+ println(sb, " " + key + " : " + mMasInstanceMap.get(key));
+ }
+ println(sb, "mEnabledAccounts:");
+ for (BluetoothMapEmailSettingsItem account : mEnabledAccounts) {
+ println(sb, " " + account);
+ }
+ }
}
diff --git a/src/com/android/bluetooth/map/BluetoothMapSmsPdu.java b/src/com/android/bluetooth/map/BluetoothMapSmsPdu.java
index caf8b02e3..bf91a56de 100644
--- a/src/com/android/bluetooth/map/BluetoothMapSmsPdu.java
+++ b/src/com/android/bluetooth/map/BluetoothMapSmsPdu.java
@@ -471,7 +471,7 @@ public class BluetoothMapSmsPdu {
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); // TODO: Change to use: ((TelephonyManager)myContext.getSystemService(Context.TELEPHONY_SERVICE))
int phoneType;
GsmAlphabet.TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
- com.android.internal.telephony.cdma.SmsMessage.calculateLength((CharSequence)messageText, false) :
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength((CharSequence)messageText, false, true) :
com.android.internal.telephony.gsm.SmsMessage.calculateLength((CharSequence)messageText, false);
SmsPdu newPdu;
diff --git a/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java b/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java
index 727b0366a..3c41f3aea 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java
@@ -198,10 +198,8 @@ public class BluetoothOppIncomingFileConfirmActivity extends AlertActivity imple
private void onTimeout() {
mTimeout = true;
- View view = getLayoutInflater().inflate(R.layout.incoming_dialog, null);
-
- ((TextView)view.findViewById(R.id.from_content)).setText(
- getString(R.string.incoming_file_confirm_timeout_content, mTransInfo.mDeviceName));
+ mAlert.setTitle(getString(R.string.incoming_file_confirm_timeout_content,
+ mTransInfo.mDeviceName));
mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText(
getString(R.string.incoming_file_confirm_timeout_ok));
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
index c7876409e..831ea811d 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
@@ -395,7 +395,8 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen
boolean needConfirm = true;
/** It's not first put if !serverBlocking, so we auto accept it */
- if (!mServerBlocking) {
+ if (!mServerBlocking && (mAccepted == BluetoothShare.USER_CONFIRMATION_CONFIRMED ||
+ mAccepted == BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED)) {
values.put(BluetoothShare.USER_CONFIRMATION,
BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED);
needConfirm = false;
diff --git a/src/com/android/bluetooth/opp/BluetoothOppService.java b/src/com/android/bluetooth/opp/BluetoothOppService.java
index c6fb79ad2..7318c793b 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -821,7 +821,7 @@ public class BluetoothOppService extends Service {
info.mDestination = stringFromCursor(info.mDestination, cursor, BluetoothShare.DESTINATION);
int newVisibility = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.VISIBILITY));
- boolean confirmed = false;
+ boolean confirmUpdated = false;
int newConfirm = cursor.getInt(cursor
.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION));
@@ -835,7 +835,7 @@ public class BluetoothOppService extends Service {
if (info.mConfirm == BluetoothShare.USER_CONFIRMATION_PENDING
&& newConfirm != BluetoothShare.USER_CONFIRMATION_PENDING) {
- confirmed = true;
+ confirmUpdated = true;
}
info.mConfirm = newConfirm;
int newStatus = cursor.getInt(statusColumn);
@@ -852,14 +852,14 @@ public class BluetoothOppService extends Service {
info.mTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
info.mMediaScanned = (cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) != Constants.MEDIA_SCANNED_NOT_SCANNED);
- if (confirmed) {
- if (V) Log.v(TAG, "Service handle info " + info.mId + " confirmed");
- /* Inbounds transfer get user confirmation, so we start it */
+ if (confirmUpdated) {
+ if (V) Log.v(TAG, "Service handle info " + info.mId + " confirmation updated");
+ /* Inbounds transfer user confirmation status changed, update the session server */
int i = findBatchWithTimeStamp(info.mTimestamp);
if (i != -1) {
BluetoothOppBatch batch = mBatchs.get(i);
if (mServerTransfer != null && batch.mId == mServerTransfer.getBatchId()) {
- mServerTransfer.setConfirmed();
+ mServerTransfer.confirmStatusChanged();
} //TODO need to think about else
}
}
@@ -1004,7 +1004,7 @@ public class BluetoothOppService extends Service {
BluetoothOppShareInfo mPendingShare = nextBatch.getPendingShare();
if ((mPendingShare != null) && (mPendingShare.mConfirm ==
BluetoothShare.USER_CONFIRMATION_CONFIRMED)) {
- mServerTransfer.setConfirmed();
+ mServerTransfer.confirmStatusChanged();
}
return;
}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
index acdd188de..917bf53ee 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
@@ -561,7 +561,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
if (V) Log.v(TAG, "processCurrentShare" + mCurrentShare.mId);
mSession.addShare(mCurrentShare);
if (mCurrentShare.mConfirm == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED) {
- setConfirmed();
+ confirmStatusChanged();
}
}
@@ -569,7 +569,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
* Set transfer confirmed status. It should only be called for inbound
* transfer
*/
- public void setConfirmed() {
+ public void confirmStatusChanged() {
/* unblock server session */
final Thread notifyThread = new Thread("Server Unblock thread") {
public void run() {
@@ -579,7 +579,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
}
}
};
- if (V) Log.v(TAG, "setConfirmed to unblock mSession" + mSession.toString());
+ if (V) Log.v(TAG, "confirmStatusChanged to unblock mSession" + mSession.toString());
notifyThread.start();
}
@@ -843,7 +843,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
if (V) Log.v(TAG, "Transfer continue session for info " + mCurrentShare.mId +
" from batch " + mBatch.mId);
processCurrentShare();
- setConfirmed();
+ confirmStatusChanged();
}
}
}
diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java
index aa85c7d2f..ea9b97ed5 100755
--- a/src/com/android/bluetooth/pan/PanService.java
+++ b/src/com/android/bluetooth/pan/PanService.java
@@ -50,9 +50,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-
/**
* Provides Bluetooth Pan Device profile, as a service in
* the Bluetooth application.
@@ -76,8 +73,6 @@ public class PanService extends ProfileService {
private static final int MESSAGE_DISCONNECT = 2;
private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
private boolean mTetherOn = false;
- private static final String PAN_PREFERENCE_FILE = "PANMGR";
- private static final String PAN_TETHER_SETTING = "TETHERSTATE";
private BluetoothTetheringNetworkFactory mNetworkFactory;
@@ -109,10 +104,6 @@ public class PanService extends ProfileService {
mNetworkFactory = new BluetoothTetheringNetworkFactory(getBaseContext(), getMainLooper(),
this);
- // Set mTetherOn based on the last saved tethering preference while starting the Pan service
- SharedPreferences tetherSetting = getSharedPreferences(PAN_PREFERENCE_FILE, 0);
- mTetherOn = tetherSetting.getBoolean(PAN_TETHER_SETTING, false);
-
return true;
}
@@ -239,6 +230,7 @@ public class PanService extends ProfileService {
return service.isPanUOn();
}
public boolean isTetheringOn() {
+ // TODO(BT) have a variable marking the on/off state
PanService service = getService();
if (service == null) return false;
return service.isTetheringOn();
@@ -298,6 +290,7 @@ public class PanService extends ProfileService {
return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
}
boolean isTetheringOn() {
+ // TODO(BT) have a variable marking the on/off state
return mTetherOn;
}
@@ -310,14 +303,6 @@ public class PanService extends ProfileService {
throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
}
if(mTetherOn != value) {
-
- SharedPreferences tetherSetting = getSharedPreferences(PAN_PREFERENCE_FILE, 0);
- SharedPreferences.Editor editor = tetherSetting.edit();
-
- editor.putBoolean(PAN_TETHER_SETTING, value);
-
- // Commit the edit!
- editor.commit();
//drop any existing panu or pan-nap connection when changing the tethering state
mTetherOn = value;
List<BluetoothDevice> DevList = getConnectedDevices();
@@ -577,6 +562,22 @@ public class PanService extends ProfileService {
return panDevice.mState;
}
+ @Override
+ public void dump(StringBuilder sb) {
+ super.dump(sb);
+ println(sb, "mMaxPanDevices: " + mMaxPanDevices);
+ println(sb, "mPanIfName: " + mPanIfName);
+ println(sb, "mTetherOn: " + mTetherOn);
+ println(sb, "mPanDevices:");
+ for (BluetoothDevice device : mPanDevices.keySet()) {
+ println(sb, " " + device + " : " + mPanDevices.get(device));
+ }
+ println(sb, "mBluetoothIfaceAddresses:");
+ for (String address : mBluetoothIfaceAddresses) {
+ println(sb, " " + address);
+ }
+ }
+
private class BluetoothPanDevice {
private int mState;
private String mIfaceAddr;
diff --git a/src/com/android/bluetooth/util/NumberUtils.java b/src/com/android/bluetooth/util/NumberUtils.java
new file mode 100644
index 000000000..1d2745660
--- /dev/null
+++ b/src/com/android/bluetooth/util/NumberUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.util;
+
+/**
+ * Utility for parsing numbers in Bluetooth.
+ */
+public class NumberUtils {
+
+ /**
+ * Convert a byte to unsigned int.
+ */
+ public static int unsignedByteToInt(byte b) {
+ return b & 0xFF;
+ }
+
+ /**
+ * Convert a little endian byte array to integer.
+ */
+ public static int littleEndianByteArrayToInt(byte[] bytes) {
+ int length = bytes.length;
+ if (length == 0) {
+ return 0;
+ }
+ int result = 0;
+ for (int i = length - 1; i >= 0; i--) {
+ int value = unsignedByteToInt(bytes[i]);
+ result += (value << (i * 8));
+ }
+ return result;
+ }
+}