diff options
author | Sudhir Sharma <sudhshar@codeaurora.org> | 2015-03-12 20:23:53 -0700 |
---|---|---|
committer | Sudhir Sharma <sudhshar@codeaurora.org> | 2015-03-12 20:23:53 -0700 |
commit | f0399274387e1912edfd1503d5b7f20cf604f357 (patch) | |
tree | ed1a0bd6f90ad235b25b6eaaa945dd633eb19378 /src | |
parent | 71a813fe10d3ac39676e5d7f9dda83c8ac482018 (diff) | |
parent | 490414ca1197017ccc3b4bf9dc75f673e49c6787 (diff) | |
download | android_packages_apps_Bluetooth-f0399274387e1912edfd1503d5b7f20cf604f357.tar.gz android_packages_apps_Bluetooth-f0399274387e1912edfd1503d5b7f20cf604f357.tar.bz2 android_packages_apps_Bluetooth-f0399274387e1912edfd1503d5b7f20cf604f357.zip |
Merge tag 'AU_LINUX_ANDROID_LA.BF64.1.2.1_RB2.05.00.02.081.002' into HEAD
"AU_LINUX_ANDROID_LA.BF64.1.2.1_RB2.05.00.02.081.002"
Conflicts:
src/com/android/bluetooth/opp/BluetoothOppService.java
Change-Id: Ib9af52d253cf33902a82552c821d8e14626d9f75
Diffstat (limited to 'src')
29 files changed, 980 insertions, 429 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 72e288d45..10090d38d 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 05ff17a7d..307a87887 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); @@ -579,7 +589,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) { @@ -648,9 +657,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; } @@ -664,8 +671,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); } } @@ -840,6 +851,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, @@ -1064,33 +1081,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. @@ -1102,7 +1119,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. @@ -1208,6 +1225,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. @@ -1437,6 +1455,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); @@ -1456,6 +1475,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); @@ -1478,6 +1498,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); @@ -1501,6 +1522,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); @@ -1541,6 +1563,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); @@ -1721,9 +1744,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); @@ -1779,9 +1799,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); @@ -1842,7 +1859,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; @@ -1874,6 +1891,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 *************************************************************************/ @@ -2048,6 +2077,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 @@ -2258,6 +2294,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 *************************************************************************/ @@ -2351,17 +2414,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..3b871f41c 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) { @@ -2218,16 +2234,17 @@ public class BluetoothMapContent { String where = setWhereFilter(folder, fi, ap); where+= " AND "+ Message.FLAG_LOADED_SELECTION; + Log.d(TAG, "where clause is = " + where); where+= " order by timeStamp desc "; - //Fetch only maxListCount emails from startOffset - if(ap.getMaxListCount() > 0 && ap.getMaxListCount() < 65536) { + //Fetch only maxListCount emails from startOffset + if(ap.getMaxListCount() > 0 && ap.getMaxListCount() < 65536) { where+=" LIMIT "+ap.getMaxListCount(); - } - if(ap.getStartOffset() > 0 && ap.getStartOffset() < 65536) { + } + if(ap.getStartOffset() > 0 && ap.getStartOffset() < 65536) { where+=" OFFSET "+ap.getStartOffset(); - } - if (V) Log.d(TAG, "where clause is = " + where); - try { + } + if (V) Log.d(TAG, "where clause is = " + where); + try { Cursor c = mResolver.query(uriEmail, EMAIL_PROJECTION, where + " AND " + Message.FLAG_LOADED_SELECTION, null, "timeStamp desc"); if(c == null) { @@ -2253,7 +2270,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 +2351,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 +2374,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 +2392,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 +2415,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 +2458,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 +2471,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 +2566,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 +2575,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 +2627,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 +2673,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 +2837,7 @@ public class BluetoothMapContent { } while(c.moveToNext()); c.close(); } + message.updateCharset(); } @@ -2865,8 +2853,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 +2877,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..f8f1c2bbf 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,52 @@ 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 +556,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 +567,7 @@ public class BluetoothMapContentObserver { if (c != null) { c.close(); } + return res; } @@ -603,7 +627,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 +783,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 +886,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 +893,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 +903,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 +948,50 @@ 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 +1014,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 +1031,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 +1043,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 +1057,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 +1069,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 +1314,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 +1349,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 +1361,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..53fedd9da 100755 --- a/src/com/android/bluetooth/map/BluetoothMapObexServer.java +++ b/src/com/android/bluetooth/map/BluetoothMapObexServer.java @@ -148,6 +148,7 @@ public class BluetoothMapObexServer extends ServerRequestHandler { tmpFolder.addFolder("drafts"); } + @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..d7d6a6178 100644 --- a/src/com/android/bluetooth/map/BluetoothMapService.java +++ b/src/com/android/bluetooth/map/BluetoothMapService.java @@ -1205,4 +1205,5 @@ public class BluetoothMapService extends ProfileService { return service.getPriority(device); } }; + } 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/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; + } +} |