Home | History | Annotate | Download | only in gatt
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.bluetooth.gatt;
     18 
     19 import android.bluetooth.BluetoothUuid;
     20 import android.bluetooth.le.ScanFilter;
     21 import android.os.ParcelUuid;
     22 import android.util.Log;
     23 
     24 import java.util.Arrays;
     25 import java.util.HashSet;
     26 import java.util.Iterator;
     27 import java.util.Objects;
     28 import java.util.Set;
     29 import java.util.UUID;
     30 
     31 /**
     32  * Helper class used to manage advertisement package filters.
     33  *
     34  * @hide
     35  */
     36 /* package */class ScanFilterQueue {
     37     public static final int TYPE_DEVICE_ADDRESS = 0;
     38     public static final int TYPE_SERVICE_DATA_CHANGED = 1;
     39     public static final int TYPE_SERVICE_UUID = 2;
     40     public static final int TYPE_SOLICIT_UUID = 3;
     41     public static final int TYPE_LOCAL_NAME = 4;
     42     public static final int TYPE_MANUFACTURER_DATA = 5;
     43     public static final int TYPE_SERVICE_DATA = 6;
     44 
     45     // Max length is 31 - 3(flags) - 2 (one byte for length and one byte for type).
     46     private static final int MAX_LEN_PER_FIELD = 26;
     47 
     48     // Values defined in bluedroid.
     49     private static final byte DEVICE_TYPE_ALL = 0;
     50 
     51     class Entry {
     52         public String address;
     53         public byte addr_type;
     54         public byte type;
     55         public UUID uuid;
     56         public UUID uuid_mask;
     57         public String name;
     58         public int company;
     59         public int company_mask;
     60         public byte[] data;
     61         public byte[] data_mask;
     62 
     63         @Override
     64         public int hashCode() {
     65             return Objects.hash(address, addr_type, type, uuid, uuid_mask, name, company,
     66                     company_mask, data, data_mask);
     67         }
     68 
     69         @Override
     70         public boolean equals(Object obj) {
     71             if (this == obj) {
     72                 return true;
     73             }
     74             if (obj == null || getClass() != obj.getClass()) {
     75                 return false;
     76             }
     77             Entry other = (Entry) obj;
     78             return Objects.equals(address, other.address) &&
     79                     addr_type == other.addr_type && type == other.type &&
     80                     Objects.equals(uuid, other.uuid) &&
     81                     Objects.equals(uuid_mask, other.uuid_mask) &&
     82                     Objects.equals(name, other.name) &&
     83                             company == other.company && company_mask == other.company_mask &&
     84                     Objects.deepEquals(data, other.data) &&
     85                     Objects.deepEquals(data_mask, other.data_mask);
     86         }
     87     }
     88 
     89     private Set<Entry> mEntries = new HashSet<Entry>();
     90 
     91     void addDeviceAddress(String address, byte type) {
     92         Entry entry = new Entry();
     93         entry.type = TYPE_DEVICE_ADDRESS;
     94         entry.address = address;
     95         entry.addr_type = type;
     96         mEntries.add(entry);
     97     }
     98 
     99     void addServiceChanged() {
    100         Entry entry = new Entry();
    101         entry.type = TYPE_SERVICE_DATA_CHANGED;
    102         mEntries.add(entry);
    103     }
    104 
    105     void addUuid(UUID uuid) {
    106         Entry entry = new Entry();
    107         entry.type = TYPE_SERVICE_UUID;
    108         entry.uuid = uuid;
    109         entry.uuid_mask = new UUID(0, 0);
    110         mEntries.add(entry);
    111     }
    112 
    113     void addUuid(UUID uuid, UUID uuid_mask) {
    114         Entry entry = new Entry();
    115         entry.type = TYPE_SERVICE_UUID;
    116         entry.uuid = uuid;
    117         entry.uuid_mask = uuid_mask;
    118         mEntries.add(entry);
    119     }
    120 
    121     void addSolicitUuid(UUID uuid) {
    122         Entry entry = new Entry();
    123         entry.type = TYPE_SOLICIT_UUID;
    124         entry.uuid = uuid;
    125         mEntries.add(entry);
    126     }
    127 
    128     void addName(String name) {
    129         Entry entry = new Entry();
    130         entry.type = TYPE_LOCAL_NAME;
    131         entry.name = name;
    132         mEntries.add(entry);
    133     }
    134 
    135     void addManufacturerData(int company, byte[] data) {
    136         Entry entry = new Entry();
    137         entry.type = TYPE_MANUFACTURER_DATA;
    138         entry.company = company;
    139         entry.company_mask = 0xFFFF;
    140         entry.data = data;
    141         entry.data_mask = new byte[data.length];
    142         Arrays.fill(entry.data_mask, (byte) 0xFF);
    143         mEntries.add(entry);
    144     }
    145 
    146     void addManufacturerData(int company, int company_mask, byte[] data, byte[] data_mask) {
    147         Entry entry = new Entry();
    148         entry.type = TYPE_MANUFACTURER_DATA;
    149         entry.company = company;
    150         entry.company_mask = company_mask;
    151         entry.data = data;
    152         entry.data_mask = data_mask;
    153         mEntries.add(entry);
    154     }
    155 
    156     void addServiceData(byte[] data, byte[] dataMask) {
    157         Entry entry = new Entry();
    158         entry.type = TYPE_SERVICE_DATA;
    159         entry.data = data;
    160         entry.data_mask = dataMask;
    161         mEntries.add(entry);
    162     }
    163 
    164     Entry pop() {
    165         if (isEmpty()) {
    166             return null;
    167         }
    168         Iterator<Entry> iterator = mEntries.iterator();
    169         Entry entry = iterator.next();
    170         iterator.remove();
    171         return entry;
    172     }
    173 
    174     boolean isEmpty() {
    175         return mEntries.isEmpty();
    176     }
    177 
    178     void clearUuids() {
    179         for (Iterator<Entry> it = mEntries.iterator(); it.hasNext();) {
    180             Entry entry = it.next();
    181             if (entry.type == TYPE_SERVICE_UUID)
    182                 it.remove();
    183         }
    184     }
    185 
    186     void clear() {
    187         mEntries.clear();
    188     }
    189 
    190     /**
    191      * Compute feature selection based on the filters presented.
    192      */
    193     int getFeatureSelection() {
    194         int selc = 0;
    195         for (Entry entry : mEntries) {
    196             selc |= (1 << entry.type);
    197         }
    198         return selc;
    199     }
    200 
    201     /**
    202      * Add ScanFilter to scan filter queue.
    203      */
    204     void addScanFilter(ScanFilter filter) {
    205         if (filter == null)
    206             return;
    207         if (filter.getDeviceName() != null) {
    208             addName(filter.getDeviceName());
    209         }
    210         if (filter.getDeviceAddress() != null) {
    211             addDeviceAddress(filter.getDeviceAddress(), DEVICE_TYPE_ALL);
    212         }
    213         if (filter.getServiceUuid() != null) {
    214             if (filter.getServiceUuidMask() == null) {
    215                 addUuid(filter.getServiceUuid().getUuid());
    216             } else {
    217                 addUuid(filter.getServiceUuid().getUuid(),
    218                         filter.getServiceUuidMask().getUuid());
    219             }
    220         }
    221         if (filter.getManufacturerData() != null) {
    222             if (filter.getManufacturerDataMask() == null) {
    223                 addManufacturerData(filter.getManufacturerId(), filter.getManufacturerData());
    224             } else {
    225                 addManufacturerData(filter.getManufacturerId(), 0xFFFF,
    226                         filter.getManufacturerData(), filter.getManufacturerDataMask());
    227             }
    228         }
    229         if (filter.getServiceDataUuid() != null && filter.getServiceData() != null) {
    230             ParcelUuid serviceDataUuid = filter.getServiceDataUuid();
    231             byte[] serviceData = filter.getServiceData();
    232             byte[] serviceDataMask = filter.getServiceDataMask();
    233             if (serviceDataMask == null) {
    234                 serviceDataMask = new byte[serviceData.length];
    235                 Arrays.fill(serviceDataMask, (byte) 0xFF);
    236             }
    237             serviceData = concate(serviceDataUuid, serviceData);
    238             serviceDataMask = concate(serviceDataUuid, serviceDataMask);
    239             if (serviceData != null && serviceDataMask != null) {
    240                 addServiceData(serviceData, serviceDataMask);
    241             }
    242         }
    243     }
    244 
    245     private byte[] concate(ParcelUuid serviceDataUuid, byte[] serviceData) {
    246         int dataLen = 2 + (serviceData == null ? 0 : serviceData.length);
    247         // If data is too long, don't add it to hardware scan filter.
    248         if (dataLen > MAX_LEN_PER_FIELD) {
    249             return null;
    250         }
    251         byte[] concated = new byte[dataLen];
    252         // Extract 16 bit UUID value.
    253         int uuidValue = BluetoothUuid.getServiceIdentifierFromParcelUuid(
    254                 serviceDataUuid);
    255         // First two bytes are service data UUID in little-endian.
    256         concated[0] = (byte) (uuidValue & 0xFF);
    257         concated[1] = (byte) ((uuidValue >> 8) & 0xFF);
    258         if (serviceData != null) {
    259             System.arraycopy(serviceData, 0, concated, 2, serviceData.length);
    260         }
    261         return concated;
    262     }
    263 }
    264