diff options
author | Lalit Kansara <lkansara@codeaurora.org> | 2016-12-06 14:47:45 +0530 |
---|---|---|
committer | Lalit Kansara <lkansara@codeaurora.org> | 2016-12-06 14:47:45 +0530 |
commit | aa2a937e6c8c9c93cc1a5fe344c368b4276243c1 (patch) | |
tree | 05648e985b986d372f3a3d7f60cab8d6b77e244d /src/com | |
parent | c7ae94e96c0c7bb93ec80d548c024a968c411d32 (diff) | |
parent | 1c40f9218d815e4e67482fb8913fce6850e4fb1c (diff) | |
download | android_packages_apps_Bluetooth-aa2a937e6c8c9c93cc1a5fe344c368b4276243c1.tar.gz android_packages_apps_Bluetooth-aa2a937e6c8c9c93cc1a5fe344c368b4276243c1.tar.bz2 android_packages_apps_Bluetooth-aa2a937e6c8c9c93cc1a5fe344c368b4276243c1.zip |
Merge commit '1c40f9218d815e4e67482fb8913fce6850e4fb1c' into remote
Conflicts:
jni/com_android_bluetooth_hfp.cpp
src/com/android/bluetooth/avrcp/Avrcp.java
src/com/android/bluetooth/btservice/AdapterService.java
src/com/android/bluetooth/btservice/RemoteDevices.java
src/com/android/bluetooth/hfp/HeadsetStateMachine.java
src/com/android/bluetooth/map/BluetoothMapContentObserver.java
src/com/android/bluetooth/map/BluetoothMapService.java
src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
src/com/android/bluetooth/pan/PanService.java
src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java
Change-Id: I41068ac7787983519220fe6fee1e65ff66298db1
Diffstat (limited to 'src/com')
23 files changed, 411 insertions, 95 deletions
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java index 4dcca00b0..f4d029796 100644 --- a/src/com/android/bluetooth/btservice/AdapterService.java +++ b/src/com/android/bluetooth/btservice/AdapterService.java @@ -89,7 +89,8 @@ import android.os.SystemProperties; public class AdapterService extends Service { private static final String TAG = "BluetoothAdapterService"; - private static final boolean DBG = false; + private static final boolean DBG = true; + private static final boolean VERBOSE = false; private static final boolean TRACE_REF = false; private static final int MIN_ADVT_INSTANCES_FOR_MA = 5; private static final int MIN_OFFLOADED_FILTERS = 10; @@ -539,6 +540,8 @@ public class AdapterService extends Service { mProfileObserver = new ProfileObserver(getApplicationContext(), this, new Handler()); mProfileObserver.start(); mVendor.init(); + + setAdapterService(this); } @Override @@ -594,9 +597,6 @@ public class AdapterService extends Service { // Ignore. } - //FIXME: Set static instance here??? - setAdapterService(this); - //Start Gatt service setGattProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON); } @@ -2680,11 +2680,12 @@ public class AdapterService extends Service { } } - debugLog("energyInfoCallback() status = " + status + - "tx_time = " + tx_time + "rx_time = " + rx_time + - "idle_time = " + idle_time + "energy_used = " + energy_used + - "ctrl_state = " + ctrl_state + - "traffic = " + Arrays.toString(data)); + verboseLog("energyInfoCallback() status = " + status + + "tx_time = " + tx_time + "rx_time = " + rx_time + + "idle_time = " + idle_time + + "energy_used = " + energy_used + + "ctrl_state = " + ctrl_state + + "traffic = " + Arrays.toString(data)); } private int getIdleCurrentMa() { @@ -2730,8 +2731,8 @@ public class AdapterService extends Service { enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); if (args.length > 0) { - debugLog("dumpsys arguments, check for protobuf output: " + - TextUtils.join(" ", args)); + verboseLog("dumpsys arguments, check for protobuf output: " + + TextUtils.join(" ", args)); if (args[0].startsWith("--proto")) { if (args[0].equals("--proto-java-bin")) { dumpJava(fd); @@ -2779,6 +2780,7 @@ public class AdapterService extends Service { private void dumpJava(FileDescriptor fd) { BluetoothProto.BluetoothLog log = new BluetoothProto.BluetoothLog(); + log.setNumBondedDevices(getBondedDevices().length); for (ProfileService profile : mProfiles) { profile.dumpProto(log); @@ -2806,6 +2808,10 @@ public class AdapterService extends Service { if (DBG) Log.d(TAG, msg); } + private void verboseLog(String msg) { + if (VERBOSE) Log.v(TAG, msg); + } + private void errorLog(String msg) { Log.e(TAG, msg); } diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java index 19089a742..94c469d8f 100644 --- a/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -30,12 +30,15 @@ import com.android.bluetooth.Utils; import java.util.concurrent.atomic.AtomicInteger; import java.util.ArrayList; import java.util.HashMap; - +import java.util.LinkedList; +import java.util.Queue; final class RemoteDevices { private static final boolean DBG = false; private static final String TAG = "BluetoothRemoteDevices"; + // Maximum number of device properties to remember + private static final int MAX_DEVICE_QUEUE_SIZE = 200; private static BluetoothAdapter mAdapter; private static AdapterService mAdapterService; @@ -45,13 +48,15 @@ final class RemoteDevices { private static final int UUID_INTENT_DELAY = 6000; private static final int MESSAGE_UUID_INTENT = 1; - private HashMap<BluetoothDevice, DeviceProperties> mDevices; + private HashMap<String, DeviceProperties> mDevices; + private Queue<String> mDeviceQueue; RemoteDevices(AdapterService service) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mAdapterService = service; mSdpTracker = new ArrayList<BluetoothDevice>(); - mDevices = new HashMap<BluetoothDevice, DeviceProperties>(); + mDevices = new HashMap<String, DeviceProperties>(); + mDeviceQueue = new LinkedList<String>(); } @@ -61,6 +66,9 @@ final class RemoteDevices { if (mDevices != null) mDevices.clear(); + + if (mDeviceQueue != null) + mDeviceQueue.clear(); } @Override @@ -70,28 +78,36 @@ final class RemoteDevices { DeviceProperties getDeviceProperties(BluetoothDevice device) { synchronized (mDevices) { - return mDevices.get(device); + return mDevices.get(device.getAddress()); } } BluetoothDevice getDevice(byte[] address) { - synchronized (mDevices) { - for (BluetoothDevice dev : mDevices.keySet()) { - if (dev.getAddress().equals(Utils.getAddressStringFromByte(address))) { - return dev; - } - } - } - return null; + DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address)); + if (prop == null) + return null; + return prop.getDevice(); } DeviceProperties addDeviceProperties(byte[] address) { synchronized (mDevices) { DeviceProperties prop = new DeviceProperties(); - BluetoothDevice device = - mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); + prop.mDevice = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); prop.mAddress = address; - mDevices.put(device, prop); + String key = Utils.getAddressStringFromByte(address); + DeviceProperties pv = mDevices.put(key, prop); + + if (pv == null) { + mDeviceQueue.offer(key); + if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) { + String deleteKey = mDeviceQueue.poll(); + for (BluetoothDevice device : mAdapterService.getBondedDevices()) { + if (device.getAddress().equals(deleteKey)) return prop; + } + debugLog("Removing device " + deleteKey + " from property map"); + mDevices.remove(deleteKey); + } + } return prop; } } @@ -105,6 +121,7 @@ final class RemoteDevices { private int mDeviceType; private String mAlias; private int mBondState; + private BluetoothDevice mDevice; DeviceProperties() { mBondState = BluetoothDevice.BOND_NONE; @@ -147,6 +164,15 @@ final class RemoteDevices { } /** + * @return the mDevice + */ + BluetoothDevice getDevice() { + synchronized (mObject) { + return mDevice; + } + } + + /** * @return mRssi */ short getRssi() { @@ -235,6 +261,7 @@ final class RemoteDevices { BluetoothDevice bdDevice = getDevice(address); DeviceProperties device; if (bdDevice == null) { + debugLog("Added new device property"); device = addDeviceProperties(address); bdDevice = getDevice(address); } else { @@ -244,11 +271,12 @@ final class RemoteDevices { for (int j = 0; j < types.length && device != null; j++) { type = types[j]; val = values[j]; - if(val.length <= 0) + if (val.length <= 0) errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice + ", value is empty for type: " + type); else { synchronized(mObject) { + debugLog("Property type: " + type); switch (type) { case AbstractionLayer.BT_PROPERTY_BDNAME: device.mName = new String(val); diff --git a/src/com/android/bluetooth/btservice/bluetooth.proto b/src/com/android/bluetooth/btservice/bluetooth.proto index 11311ea58..77ded7807 100644 --- a/src/com/android/bluetooth/btservice/bluetooth.proto +++ b/src/com/android/bluetooth/btservice/bluetooth.proto @@ -25,6 +25,9 @@ message BluetoothLog { // Scan event information. repeated ScanEvent scan_event = 4; + + // Number of bonded devices. + optional int32 num_bonded_devices = 5; } // The information about the device. diff --git a/src/com/android/bluetooth/gatt/AdvertiseManager.java b/src/com/android/bluetooth/gatt/AdvertiseManager.java index 058bf4b59..3fb4aa945 100644 --- a/src/com/android/bluetooth/gatt/AdvertiseManager.java +++ b/src/com/android/bluetooth/gatt/AdvertiseManager.java @@ -194,7 +194,7 @@ class AdvertiseManager { private void handleStartAdvertising(AdvertiseClient client) { Utils.enforceAdminPermission(mService); int clientIf = client.clientIf; - if (mAdvertiseClients.contains(clientIf)) { + if (mAdvertiseClients.contains(client)) { postCallback(clientIf, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); return; } diff --git a/src/com/android/bluetooth/gatt/ContextMap.java b/src/com/android/bluetooth/gatt/ContextMap.java index e3044d586..024572792 100644 --- a/src/com/android/bluetooth/gatt/ContextMap.java +++ b/src/com/android/bluetooth/gatt/ContextMap.java @@ -190,6 +190,7 @@ import com.android.bluetooth.btservice.BluetoothProto; while (i.hasNext()) { App entry = i.next(); if (entry.id == id) { + removeConnectionsByAppId(id); entry.unlinkToDeath(); entry.appScanStats.isRegistered = false; i.remove(); @@ -205,7 +206,7 @@ import com.android.bluetooth.btservice.BluetoothProto; void addConnection(int id, int connId, String address) { synchronized (mConnections) { App entry = getById(id); - if (entry != null){ + if (entry != null) { mConnections.add(new Connection(connId, address, id)); } } @@ -228,6 +229,19 @@ import com.android.bluetooth.btservice.BluetoothProto; } /** + * Remove all connections for a given application ID. + */ + void removeConnectionsByAppId(int appId) { + Iterator<Connection> i = mConnections.iterator(); + while (i.hasNext()) { + Connection connection = i.next(); + if (connection.appId == appId) { + i.remove(); + } + } + } + + /** * Get an application context by ID. */ App getById(int id) { diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java index 0595ae510..f8bef759d 100644 --- a/src/com/android/bluetooth/gatt/GattService.java +++ b/src/com/android/bluetooth/gatt/GattService.java @@ -237,6 +237,7 @@ public class GattService extends ProfileService { boolean permissionCheck(int connId, int handle) { List<BluetoothGattService> db = gattClientDatabases.get(connId); + if (db == null) return true; for (BluetoothGattService service : db) { for (BluetoothGattCharacteristic characteristic: service.getCharacteristics()) { @@ -759,7 +760,15 @@ public class GattService extends ProfileService { void onSearchCompleted(int connId, int status) throws RemoteException { if (DBG) Log.d(TAG, "onSearchCompleted() - connId=" + connId+ ", status=" + status); // Gatt DB is ready! - gattClientGetGattDbNative(connId); + + // This callback was called from the jni_workqueue thread. If we make request to the stack + // on the same thread, it might cause deadlock. Schedule request on a new thread instead. + Thread t = new Thread(new Runnable() { + public void run() { + gattClientGetGattDbNative(connId); + } + }); + t.start(); } GattDbElement GetSampleGattDbElement() { @@ -1638,12 +1647,11 @@ public class GattService extends ProfileService { void connectionParameterUpdate(int clientIf, String address, int connectionPriority) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - // Default spec recommended interval is 30->50 ms - int minInterval = 24; // 24 * 1.25ms = 30ms - int maxInterval = 40; // 40 * 1.25ms = 50ms + int minInterval; + int maxInterval; // Slave latency - int latency = 0; + int latency; // Link supervision timeout is measured in N * 10ms int timeout = 2000; // 20s @@ -1653,12 +1661,22 @@ public class GattService extends ProfileService { case BluetoothGatt.CONNECTION_PRIORITY_HIGH: minInterval = getResources().getInteger(R.integer.gatt_high_priority_min_interval); maxInterval = getResources().getInteger(R.integer.gatt_high_priority_max_interval); + latency = getResources().getInteger(R.integer.gatt_high_priority_latency); break; case BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER: minInterval = getResources().getInteger(R.integer.gatt_low_power_min_interval); maxInterval = getResources().getInteger(R.integer.gatt_low_power_max_interval); - latency = 2; + latency = getResources().getInteger(R.integer.gatt_low_power_latency); + break; + + default: + // Using the values for CONNECTION_PRIORITY_BALANCED. + minInterval = + getResources().getInteger(R.integer.gatt_balanced_priority_min_interval); + maxInterval = + getResources().getInteger(R.integer.gatt_balanced_priority_max_interval); + latency = getResources().getInteger(R.integer.gatt_balanced_priority_latency); break; } diff --git a/src/com/android/bluetooth/gatt/HandleMap.java b/src/com/android/bluetooth/gatt/HandleMap.java index 4a2063984..27a7a7194 100644 --- a/src/com/android/bluetooth/gatt/HandleMap.java +++ b/src/com/android/bluetooth/gatt/HandleMap.java @@ -69,7 +69,6 @@ class HandleMap { this.type = type; this.handle = handle; this.uuid = uuid; - this.instance = instance; this.serviceHandle = serviceHandle; } @@ -78,7 +77,6 @@ class HandleMap { this.type = type; this.handle = handle; this.uuid = uuid; - this.instance = instance; this.serviceHandle = serviceHandle; this.charHandle = charHandle; } diff --git a/src/com/android/bluetooth/gatt/ScanManager.java b/src/com/android/bluetooth/gatt/ScanManager.java index b20a54f3f..621359724 100644 --- a/src/com/android/bluetooth/gatt/ScanManager.java +++ b/src/com/android/bluetooth/gatt/ScanManager.java @@ -40,11 +40,13 @@ import com.android.bluetooth.btservice.AdapterService; import com.android.internal.app.IBatteryStats; import java.util.ArrayDeque; +import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -95,8 +97,8 @@ public class ScanManager { private CountDownLatch mLatch; ScanManager(GattService service) { - mRegularScanClients = new HashSet<ScanClient>(); - mBatchClients = new HashSet<ScanClient>(); + mRegularScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); + mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); mService = service; mScanNative = new ScanNative(); curUsedTrackableAdvertisements = 0; @@ -238,11 +240,11 @@ public class ScanManager { if (!mScanNative.isOpportunisticScanClient(client)) { mScanNative.configureRegularScanParams(); - if (!mScanNative.isFirstMatchScanClient(client)) { + if (!mScanNative.isExemptFromScanDowngrade(client)) { Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT); msg.obj = client; // Only one timeout message should exist at any time - mHandler.removeMessages(SCAN_TIMEOUT_MS); + mHandler.removeMessages(MSG_SCAN_TIMEOUT); mHandler.sendMessageDelayed(msg, SCAN_TIMEOUT_MS); } } @@ -261,10 +263,6 @@ public class ScanManager { if (client == null) return; if (mRegularScanClients.contains(client)) { - // The ScanClient passed in just holds the clientIf. We retrieve the real client, - // which may have workSource set. - client = mScanNative.getRegularScanClient(client.clientIf); - if (client == null) return; mScanNative.stopRegularScan(client); @@ -278,7 +276,11 @@ public class ScanManager { // Update BatteryStats with this workload. try { - mBatteryStats.noteBleScanStopped(client.workSource); + // The ScanClient passed in just holds the clientIf. We retrieve the real client, + // which may have workSource set. + ScanClient workClient = mScanNative.getRegularScanClient(client.clientIf); + if (workClient != null) + mBatteryStats.noteBleScanStopped(workClient.workSource); } catch (RemoteException e) { /* ignore */ } @@ -534,6 +536,12 @@ public class ScanManager { } } + private boolean isExemptFromScanDowngrade(ScanClient client) { + return isOpportunisticScanClient(client) + || isFirstMatchScanClient(client) + || !shouldUseAllPassFilter(client); + } + private boolean isOpportunisticScanClient(ScanClient client) { return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; } @@ -683,8 +691,9 @@ public class ScanManager { void regularScanTimeout() { for (ScanClient client : mRegularScanClients) { - if (!isOpportunisticScanClient(client) && !isFirstMatchScanClient(client)) { - logd("clientIf set to scan opportunisticly: " + client.clientIf); + if (!isExemptFromScanDowngrade(client)) { + Log.w(TAG, "Moving scan client to opportunistic (clientIf " + + client.clientIf + ")"); setOpportunisticScanClient(client); client.stats.setScanTimeout(); } diff --git a/src/com/android/bluetooth/hfp/HeadsetHalConstants.java b/src/com/android/bluetooth/hfp/HeadsetHalConstants.java index 0742917c1..dee633f2d 100644 --- a/src/com/android/bluetooth/hfp/HeadsetHalConstants.java +++ b/src/com/android/bluetooth/hfp/HeadsetHalConstants.java @@ -64,4 +64,8 @@ final public class HeadsetHalConstants { final static int CALL_STATE_INCOMING = 4; final static int CALL_STATE_WAITING = 5; final static int CALL_STATE_IDLE = 6; + + // Match up with bthf_hf_ind_type_t of bt_hf.h + final static int HF_INDICATOR_ENHANCED_DRIVER_SAFETY = 1; + final static int HF_INDICATOR_BATTERY_LEVEL_STATUS = 2; } diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java index c61cb3cb6..09d2e32f1 100644 --- a/src/com/android/bluetooth/hfp/HeadsetService.java +++ b/src/com/android/bluetooth/hfp/HeadsetService.java @@ -331,6 +331,12 @@ public class HeadsetService extends ProfileService { if (service == null) return false; return service.disableWBS(); } + + public void bindResponse(int ind_id, boolean ind_status) { + HeadsetService service = getService(); + if (service == null) return; + service.bindResponse(ind_id, ind_status); + } }; //API methods @@ -639,6 +645,24 @@ public class HeadsetService extends ProfileService { return true; } + private boolean bindResponse(int ind_id, boolean ind_status) { + for (BluetoothDevice device: getConnectedDevices()) { + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState != BluetoothProfile.STATE_CONNECTED && + connectionState != BluetoothProfile.STATE_CONNECTING) { + continue; + } + if (DBG) Log.d("Bind Response sent for", device.getAddress()); + Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.BIND_RESPONSE); + msg.obj = device; + msg.arg1 = ind_id; + msg.arg2 = (ind_status == true) ? 1 : 0; + mStateMachine.sendMessage(msg); + return true; + } + return false; + } + @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java index 05806fa59..76ef46301 100644..100755 --- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java @@ -339,6 +339,7 @@ final class HeadsetClientStateMachine extends StateMachine { Log.d(TAG, "Enter sendCallChangedIntent()"); Log.d(TAG, "sendCallChangedIntent " + c); Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c); mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); Log.d(TAG, "Exit sendCallChangedIntent()"); diff --git a/src/com/android/bluetooth/map/BluetoothMapContent.java b/src/com/android/bluetooth/map/BluetoothMapContent.java index 2370a4d35..4f42bfb58 100755 --- a/src/com/android/bluetooth/map/BluetoothMapContent.java +++ b/src/com/android/bluetooth/map/BluetoothMapContent.java @@ -950,10 +950,14 @@ public class BluetoothMapContent { address = c.getString(c.getColumnIndex(Sms.ADDRESS)); } if ((address == null) && msgType == Sms.MESSAGE_TYPE_DRAFT) { - //Fetch address for Drafts folder from "canonical_address" table + // Fetch address for Drafts folder from "canonical_address" table int threadIdInd = c.getColumnIndex(Sms.THREAD_ID); String threadIdStr = c.getString(threadIdInd); - address = getCanonicalAddressSms(mResolver, Integer.valueOf(threadIdStr)); + // If a draft message has no recipient, it has no thread ID + // hence threadIdStr could possibly be null + if (threadIdStr != null) { + address = getCanonicalAddressSms(mResolver, Integer.valueOf(threadIdStr)); + } if(V) Log.v(TAG, "threadId = " + threadIdStr + " adress:" + address +"\n"); } } else if (fi.mMsgType == FilterInfo.TYPE_MMS) { @@ -961,7 +965,7 @@ public class BluetoothMapContent { address = getAddressMms(mResolver, id, MMS_TO); } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) { /* Might be another way to handle addresses */ - address = getRecipientAddressingEmail(e, c,fi); + address = getRecipientAddressingEmail(e, c, fi); } if (V) Log.v(TAG, "setRecipientAddressing: " + address); if(address == null) @@ -1603,7 +1607,8 @@ public class BluetoothMapContent { } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1"; } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { - where = Sms.TYPE + " = 3 AND " + Sms.THREAD_ID + " <> -1"; + where = Sms.TYPE + " = 3 AND " + + "(" + Sms.THREAD_ID + " IS NULL OR " + Sms.THREAD_ID + " <> -1 )"; } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { where = Sms.THREAD_ID + " = -1"; } @@ -1620,7 +1625,8 @@ public class BluetoothMapContent { } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) { where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1"; } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) { - where = Mms.MESSAGE_BOX + " = 3 AND " + Mms.THREAD_ID + " <> -1"; + where = Mms.MESSAGE_BOX + " = 3 AND " + + "(" + Mms.THREAD_ID + " IS NULL OR " + Mms.THREAD_ID + " <> -1 )"; } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) { where = Mms.THREAD_ID + " = -1"; } diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java index 878f5bedf..c70408ccb 100755 --- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java +++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java @@ -37,6 +37,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; +import android.os.UserManager; import android.provider.Telephony; import android.provider.Telephony.Mms; import android.provider.Telephony.MmsSms; @@ -172,7 +173,9 @@ public class BluetoothMapContentObserver { public static final String EXTRA_MESSAGE_SENT_TIMESTAMP = "timestamp"; private SmsBroadcastReceiver mSmsBroadcastReceiver = new SmsBroadcastReceiver(); + private CeBroadcastReceiver mCeBroadcastReceiver = new CeBroadcastReceiver(); + private boolean mStorageUnlocked = false; private boolean mInitialized = false; @@ -480,6 +483,12 @@ public class BluetoothMapContentObserver { Log.w(TAG, "onChange() with URI == null - not handled."); return; } + + if (!mStorageUnlocked) { + Log.v(TAG, "Ignore events until storage is completely unlocked"); + return; + } + if (V) Log.d(TAG, "onChange on thread: " + Thread.currentThread().getId() + " Uri: " + uri.toString() + " selfchange: " + selfChange); @@ -1184,9 +1193,10 @@ public class BluetoothMapContentObserver { private void initMsgList() throws RemoteException { if (V) Log.d(TAG, "initMsgList"); + UserManager manager = UserManager.get(mContext); + if (manager == null || !manager.isUserUnlocked()) return; - if(mEnableSmsMms) { - + if (mEnableSmsMms) { HashMap<Long, Msg> msgListSms = new HashMap<Long, Msg>(); Cursor c = mResolver.query(Sms.CONTENT_URI, @@ -2361,21 +2371,19 @@ public class BluetoothMapContentObserver { /* Approved MAP spec errata 3445 states that read status initiated * by the MCE shall change the MSE read status. */ if (type == TYPE.SMS_GSM || type == TYPE.SMS_CDMA) { - Uri uri = Sms.Inbox.CONTENT_URI; + Uri uri = ContentUris.withAppendedId(Sms.CONTENT_URI, handle); ContentValues contentValues = new ContentValues(); contentValues.put(Sms.READ, statusValue); contentValues.put(Sms.SEEN, statusValue); - String where = Sms._ID+"="+handle; String values = contentValues.toString(); - if (D) Log.d(TAG, " -> SMS Uri: " + uri.toString() + - " Where " + where + " values " + values); + if (D) Log.d(TAG, " -> SMS Uri: " + uri.toString() + " values " + values); synchronized(getMsgListSms()) { Msg msg = getMsgListSms().get(handle); if(msg != null) { // This will always be the case msg.flagRead = statusValue; } } - count = mResolver.update(uri, contentValues, where, null); + count = mResolver.update(uri, contentValues, null, null); if (D) Log.d(TAG, " -> "+count +" rows updated!"); } else if (type == TYPE.MMS) { @@ -2455,8 +2463,16 @@ public class BluetoothMapContentObserver { long folderId = -1; if (recipientList == null) { - if (D) Log.d(TAG, "empty recipient list"); - return -1; + if (folderElement.getName().equalsIgnoreCase(BluetoothMapContract.FOLDER_NAME_DRAFT)) { + BluetoothMapbMessage.vCard empty = + new BluetoothMapbMessage.vCard("", "", null, null, 0); + recipientList = new ArrayList<BluetoothMapbMessage.vCard>(); + recipientList.add(empty); + Log.w(TAG, "Added empty recipient to draft message"); + } else { + Log.e(TAG, "Trying to send a message with no recipients"); + return -1; + } } if ( msg.getType().equals(TYPE.EMAIL) ) { @@ -3190,6 +3206,52 @@ public class BluetoothMapContentObserver { } } + private class CeBroadcastReceiver extends BroadcastReceiver { + public void register() { + UserManager manager = UserManager.get(mContext); + if (manager == null || manager.isUserUnlocked()) { + mStorageUnlocked = true; + return; + } + + Handler handler = new Handler(Looper.getMainLooper()); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(this, intentFilter, null, handler); + } + + public void unregister() { + try { + mContext.unregisterReceiver(this); + } catch (IllegalArgumentException e) { + /* do nothing */ + } + } + + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d(TAG, "onReceive: action" + action); + + if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { + try { + initMsgList(); + } catch (RemoteException e) { + Log.e(TAG, "Error initializing SMS/MMS message lists."); + } + + for (String folder : FOLDER_SMS_MAP.values()) { + Event evt = new Event(EVENT_TYPE_NEW, -1, folder, mSmsType); + sendEvent(evt); + } + mStorageUnlocked = true; + /* After unlock this BroadcastReceiver is never needed */ + unregister(); + } else { + Log.d(TAG, "onReceive: Unknown action " + action); + } + } + } + /** * Handle MMS sent intents in disconnected(MNS) state, where we do not need to send any * notifications. @@ -3331,6 +3393,8 @@ public class BluetoothMapContentObserver { private void resendPendingMessages() { /* Send pending messages in outbox */ String where = "type = " + Sms.MESSAGE_TYPE_OUTBOX; + UserManager manager = UserManager.get(mContext); + if (manager == null || !manager.isUserUnlocked()) return; Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null, null); try { @@ -3398,6 +3462,11 @@ public class BluetoothMapContentObserver { if (mSmsBroadcastReceiver != null) { mSmsBroadcastReceiver.register(); } + + if (mCeBroadcastReceiver != null) { + mCeBroadcastReceiver.register(); + } + registerPhoneServiceStateListener(); mInitialized = true; } diff --git a/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java b/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java index 88e5605de..832060e86 100644 --- a/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java +++ b/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java @@ -27,6 +27,7 @@ import android.util.Log; import android.util.Xml; import com.android.bluetooth.map.BluetoothMapUtils.TYPE; +import com.android.bluetooth.util.Interop; public class BluetoothMapMessageListingElement implements Comparable<BluetoothMapMessageListingElement> { @@ -276,9 +277,17 @@ public class BluetoothMapMessageListingElement BluetoothMapUtils.getMapHandle(mCpHandle, mType)); if(mSubject != null){ String stripped = BluetoothMapUtils.stripInvalidChars(mSubject); + + if (Interop.matchByAddress(Interop.INTEROP_MAP_ASCIIONLY, + BluetoothMapService.getRemoteDevice().getAddress())) { + stripped = stripped.replaceAll("[\\P{ASCII}&\"><]", ""); + if (stripped.isEmpty()) stripped = "---"; + } + xmlMsgElement.attribute(null, "subject", stripped.substring(0, stripped.length() < 256 ? stripped.length() : 256)); } + if(mDateTime != 0) xmlMsgElement.attribute(null, "datetime", this.getDateTimeString()); if(mSenderName != null) diff --git a/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/src/com/android/bluetooth/map/BluetoothMapObexServer.java index f0c26b0de..00b290215 100644 --- a/src/com/android/bluetooth/map/BluetoothMapObexServer.java +++ b/src/com/android/bluetooth/map/BluetoothMapObexServer.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.UserManager; import android.text.format.DateUtils; import android.util.Log; @@ -402,6 +403,11 @@ public class BluetoothMapObexServer extends ServerRequestHandler { return ResponseCodes.OBEX_HTTP_OK; } + private boolean isUserUnlocked() { + UserManager manager = UserManager.get(mContext); + return (manager == null || manager.isUserUnlocked()); + } + @Override public int onPut(final Operation op) { if (D) Log.d(TAG, "onPut(): enter"); @@ -431,26 +437,34 @@ public class BluetoothMapObexServer extends ServerRequestHandler { return ResponseCodes.OBEX_HTTP_OK; } return updateInbox(); - }else if(type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) { + } else if (type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) { if(V) { Log.d(TAG,"TYPE_SET_NOTIFICATION_REGISTRATION: NotificationStatus: " + appParams.getNotificationStatus()); } return mObserver.setNotificationRegistration(appParams.getNotificationStatus()); - }else if(type.equals(TYPE_SET_NOTIFICATION_FILTER)) { + } else if (type.equals(TYPE_SET_NOTIFICATION_FILTER)) { if(V) { Log.d(TAG,"TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: " + appParams.getNotificationFilter()); } + if (!isUserUnlocked()) { + Log.e(TAG, "Storage locked, " + type + " failed"); + return ResponseCodes.OBEX_HTTP_UNAVAILABLE; + } mObserver.setNotificationFilter(appParams.getNotificationFilter()); return ResponseCodes.OBEX_HTTP_OK; - } else if(type.equals(TYPE_SET_MESSAGE_STATUS)) { + } else if (type.equals(TYPE_SET_MESSAGE_STATUS)) { if(V) { Log.d(TAG,"TYPE_SET_MESSAGE_STATUS: " + "StatusIndicator: " + appParams.getStatusIndicator() + ", StatusValue: " + appParams.getStatusValue() + ", ExtentedData: " + "" ); // TODO: appParams.getExtendedImData()); } + if (!isUserUnlocked()) { + Log.e(TAG, "Storage locked, " + type + " failed"); + return ResponseCodes.OBEX_HTTP_UNAVAILABLE; + } return setMessageStatus(name, appParams); } else if (type.equals(TYPE_MESSAGE)) { if(V) { @@ -458,6 +472,10 @@ public class BluetoothMapObexServer extends ServerRequestHandler { + ", retry: " + appParams.getRetry() + ", charset: " + appParams.getCharset()); } + if (!isUserUnlocked()) { + Log.e(TAG, "Storage locked, " + type + " failed"); + return ResponseCodes.OBEX_HTTP_UNAVAILABLE; + } return pushMessage(op, name, appParams, mMessageVersion); } else if (type.equals(TYPE_SET_OWNER_STATUS)) { if(V) { @@ -916,6 +934,10 @@ public class BluetoothMapObexServer extends ServerRequestHandler { Log.d(TAG,"FilterConvoId = " + ((tmpLongLong == null) ? "" : Long.toHexString(tmpLongLong.getLeastSignificantBits()) ) ); } + if (!isUserUnlocked()) { + Log.e(TAG, "Storage locked, " + type + " failed"); + return ResponseCodes.OBEX_HTTP_UNAVAILABLE; + } // Block until all packets have been send. return sendMessageListingRsp(op, appParams, name); @@ -930,6 +952,10 @@ public class BluetoothMapObexServer extends ServerRequestHandler { Log.d(TAG,"FilterReadStatus = " + appParams.getFilterReadStatus()); Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient()); } + if (!isUserUnlocked()) { + Log.e(TAG, "Storage locked, " + type + " failed"); + return ResponseCodes.OBEX_HTTP_UNAVAILABLE; + } // Block until all packets have been send. return sendConvoListingRsp(op, appParams,name); } else if (type.equals(TYPE_GET_MAS_INSTANCE_INFORMATION)) { @@ -947,6 +973,10 @@ public class BluetoothMapObexServer extends ServerRequestHandler { ", Charset = " + appParams.getCharset() + ", FractionRequest = " + appParams.getFractionRequest()); } + if (!isUserUnlocked()) { + Log.e(TAG, "Storage locked, " + type + " failed"); + return ResponseCodes.OBEX_HTTP_UNAVAILABLE; + } // Block until all packets have been send. return sendGetMessageRsp(op, name, appParams, mMessageVersion); } else { @@ -1028,8 +1058,8 @@ public class BluetoothMapObexServer extends ServerRequestHandler { return ResponseCodes.OBEX_HTTP_BAD_REQUEST; } Log.v(TAG,"sendMessageListingRsp: has sms " + folderToList.hasSmsMmsContent() + - "has email " + folderToList.hasEmailContent() + - "has IM " + folderToList.hasImContent() ); + ", has email " + folderToList.hasEmailContent() + + ", has IM " + folderToList.hasImContent() ); } try { diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java index 552bb09eb..addebf0c5 100755 --- a/src/com/android/bluetooth/map/BluetoothMapService.java +++ b/src/com/android/bluetooth/map/BluetoothMapService.java @@ -867,6 +867,7 @@ public class BluetoothMapService extends ProfileService { sendShutdownMessage(); } mStartError = true; + setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED); return true; } diff --git a/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java b/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java index 2a51aa067..38873da12 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java +++ b/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java @@ -42,31 +42,31 @@ public class BluetoothOppHandoverReceiver extends BroadcastReceiver { if (D) Log.d(TAG, "No device attached to handover intent."); return; } + + String mimeType = intent.getType(); + ArrayList<Uri> uris = new ArrayList<Uri>(); if (action.equals(Constants.ACTION_HANDOVER_SEND)) { - String type = intent.getType(); Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (stream != null && type != null) { - // Save type/stream, will be used when adding transfer - // session to DB. - BluetoothOppManager.getInstance(context).saveSendingFileInfo(type, - stream.toString(), true); - } else { - if (D) Log.d(TAG, "No mimeType or stream attached to handover request"); - } + if (stream != null) uris.add(stream); } else if (action.equals(Constants.ACTION_HANDOVER_SEND_MULTIPLE)) { - ArrayList<Uri> uris = new ArrayList<Uri>(); - String mimeType = intent.getType(); uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - if (mimeType != null && uris != null) { - BluetoothOppManager.getInstance(context).saveSendingFileInfo(mimeType, - uris, true); - } else { - if (D) Log.d(TAG, "No mimeType or stream attached to handover request"); - return; - } } - // we already know where to send to - BluetoothOppManager.getInstance(context).startTransfer(device); + + if (mimeType != null && uris != null && !uris.isEmpty()) { + final String finalType = mimeType; + final ArrayList<Uri> finalUris = uris; + Thread t = new Thread(new Runnable() { + public void run() { + BluetoothOppManager.getInstance(context).saveSendingFileInfo(finalType, + finalUris, true); + BluetoothOppManager.getInstance(context).startTransfer(device); + } + }); + t.start(); + } else { + if (D) Log.d(TAG, "No mimeType or stream attached to handover request"); + return; + } } else if (action.equals(Constants.ACTION_WHITELIST_DEVICE)) { BluetoothDevice device = (BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java index 0f23bd382..59e7848b6 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java +++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java @@ -476,13 +476,13 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { outputStream.write(buffer, 0, readLength); position += readLength; + /* check remote accept or reject */ + responseCode = putOperation.getResponseCode(); mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT); synchronized (this) { mWaitingForRemote = false; } - /* check remote accept or reject */ - responseCode = putOperation.getResponseCode(); if (responseCode == ResponseCodes.OBEX_HTTP_CONTINUE || responseCode == ResponseCodes.OBEX_HTTP_OK) { @@ -595,7 +595,7 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { } finally { try { if (outputStream != null) { - outputStream.close(); + outputStream.close(); } // Close InputStream and remove SendFileInfo from map diff --git a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java index 2a9cadb8b..f60f06cab 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +++ b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java @@ -123,6 +123,7 @@ public class BluetoothOppSendFileInfo { Log.e(TAG, "generateFileInfo: " + e); return new BluetoothOppSendFileInfo(fileName, contentType, length, null, 0); } + if (metadataCursor != null) { try { if (metadataCursor.moveToFirst()) { diff --git a/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/src/com/android/bluetooth/opp/BluetoothOppUtility.java index 8294246f0..99777a5ae 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppUtility.java +++ b/src/com/android/bluetooth/opp/BluetoothOppUtility.java @@ -367,6 +367,9 @@ public class BluetoothOppUtility { static void putSendFileInfo(Uri uri, BluetoothOppSendFileInfo sendFileInfo) { if (D) Log.d(TAG, "putSendFileInfo: uri=" + uri + " sendFileInfo=" + sendFileInfo); + if (sendFileInfo == BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR) { + Log.e(TAG, "putSendFileInfo: bad sendFileInfo, URI: " + uri); + } sSendFileMap.put(uri, sendFileInfo); } diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java index 05f92ffe0..6a43b9342 100755 --- a/src/com/android/bluetooth/pan/PanService.java +++ b/src/com/android/bluetooth/pan/PanService.java @@ -440,8 +440,8 @@ public class PanService extends ProfileService { if (prevState == state) return; if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) { if (state == BluetoothProfile.STATE_CONNECTED) { - if((!mTetherOn)||(local_role == BluetoothPan.LOCAL_PANU_ROLE)){ - if(DBG) Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role" + if ((!mTetherOn) || (local_role == BluetoothPan.LOCAL_PANU_ROLE)) { + if (DBG) Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role" + " is PANU drop the connection"); mPanDevices.remove(device); disconnectPanNative(Utils.getByteAddress(device)); diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java b/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java index a35ae5659..75388ee64 100644 --- a/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java @@ -42,6 +42,8 @@ public class BluetoothPbapReceiver extends BroadcastReceiver { private static final String TAG = "BluetoothPbapReceiver"; + private static final boolean D = BluetoothPbapService.DEBUG; + private static final boolean V = Log.isLoggable(BluetoothPbapService.LOG_TAG, Log.VERBOSE); @Override @@ -52,12 +54,13 @@ public class BluetoothPbapReceiver extends BroadcastReceiver { in.setClass(context, BluetoothPbapService.class); String action = intent.getAction(); in.putExtra("action", action); - Log.i(TAG, "Enter - onReceive for intent:" + action); + if (D) Log.d(TAG, "PbapReceiver onReceive action = " + action); + boolean startService = true; if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); in.putExtra(BluetoothAdapter.EXTRA_STATE, state); - Log.i(TAG, "State :" + state); + if (D) Log.d(TAG, "state = " + state); if ((state == BluetoothAdapter.STATE_TURNING_ON) || (state == BluetoothAdapter.STATE_OFF)) { //FIX: We turn on PBAP after BluetoothAdapter.STATE_ON, @@ -68,11 +71,13 @@ public class BluetoothPbapReceiver extends BroadcastReceiver { // Don't forward intent unless device has bluetooth and bluetooth is enabled. BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter == null || !adapter.isEnabled()) { + if (D) Log.d(TAG, "BluetoothAdapter is not enabled (" + + adapter + "). Would not start service."); startService = false; } } if (startService) { - if (V) Log.v(TAG, "Calling start service!!!! with action = " + in.getAction()); + if (D) Log.d(TAG, "Calling start service with action = " + in.getAction()); context.startService(in); } Log.i(TAG, "Exit - onReceive for intent:" + action); diff --git a/src/com/android/bluetooth/util/Interop.java b/src/com/android/bluetooth/util/Interop.java new file mode 100644 index 000000000..4861c154e --- /dev/null +++ b/src/com/android/bluetooth/util/Interop.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 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; + +import java.util.ArrayList; +import java.util.List; + +/** + * Centralized Bluetooth Interoperability workaround utilities and database. + * This is the Java version. An analagous native version can be found + * in /system/bt/devices/include/interop_database.h. + */ +public class Interop { + + /** + * Simple interop entry consisting of a workarond id (see below) + * and a (partial or complete) Bluetooth device address string + * to match against. + */ + private static class Entry { + String address; + int workaround_id; + + public Entry(int workaround_id, String address) { + this.workaround_id = workaround_id; + this.address = address; + } + } + + /** + * The actual "database" of interop entries. + */ + private static List<Entry> entries = null; + + /** + * Workaround ID for deivces which do not accept non-ASCII + * characters in SMS messages. + */ + public static final int INTEROP_MAP_ASCIIONLY = 1; + + /** + * Initializes the interop datbase with the relevant workaround + * entries. + * When adding entries, please provide a description for each + * device as to what problem the workaround addresses. + */ + private static void lazyInitInteropDatabase() { + if (entries != null) return; + entries = new ArrayList<Entry>(); + + /** Mercedes Benz NTG 4.5 does not handle non-ASCII characters in SMS */ + entries.add(new Entry(INTEROP_MAP_ASCIIONLY, "00:26:e8")); + } + + /** + * Checks wheter a given device identified by |address| is a match + * for a given workaround identified by |workaround_id|. + * Return true if the address matches, false otherwise. + */ + public static boolean matchByAddress(int workaround_id, String address) { + if (address == null || address.isEmpty()) return false; + + lazyInitInteropDatabase(); + for (Entry entry : entries) { + if (entry.workaround_id == workaround_id && + entry.address.startsWith(address.toLowerCase())) { + return true; + } + } + + return false; + } +} |