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