1 /*
2  * Copyright (C) 2011 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.settingslib.bluetooth;
18 
19 import android.bluetooth.BluetoothClass;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothUuid;
22 import android.os.ParcelUuid;
23 import android.util.Log;
24 
25 /**
26  * BluetoothDeviceFilter contains a static method that returns a
27  * Filter object that returns whether or not the BluetoothDevice
28  * passed to it matches the specified filter type constant from
29  * {@link android.bluetooth.BluetoothDevicePicker}.
30  */
31 public final class BluetoothDeviceFilter {
32     private static final String TAG = "BluetoothDeviceFilter";
33 
34     /** The filter interface to external classes. */
35     public interface Filter {
matches(BluetoothDevice device)36         boolean matches(BluetoothDevice device);
37     }
38 
39     /** All filter singleton (referenced directly). */
40     public static final Filter ALL_FILTER = new AllFilter();
41 
42     /** Bonded devices only filter (referenced directly). */
43     public static final Filter BONDED_DEVICE_FILTER = new BondedDeviceFilter();
44 
45     /** Unbonded devices only filter (referenced directly). */
46     public static final Filter UNBONDED_DEVICE_FILTER = new UnbondedDeviceFilter();
47 
48     /** Table of singleton filter objects. */
49     private static final Filter[] FILTERS = {
50             ALL_FILTER,             // FILTER_TYPE_ALL
51             new AudioFilter(),      // FILTER_TYPE_AUDIO
52             new TransferFilter(),   // FILTER_TYPE_TRANSFER
53             new PanuFilter(),       // FILTER_TYPE_PANU
54             new NapFilter()         // FILTER_TYPE_NAP
55     };
56 
57     /** Private constructor. */
BluetoothDeviceFilter()58     private BluetoothDeviceFilter() {
59     }
60 
61     /**
62      * Returns the singleton {@link Filter} object for the specified type,
63      * or {@link #ALL_FILTER} if the type value is out of range.
64      *
65      * @param filterType a constant from BluetoothDevicePicker
66      * @return a singleton object implementing the {@link Filter} interface.
67      */
getFilter(int filterType)68     public static Filter getFilter(int filterType) {
69         if (filterType >= 0 && filterType < FILTERS.length) {
70             return FILTERS[filterType];
71         } else {
72             Log.w(TAG, "Invalid filter type " + filterType + " for device picker");
73             return ALL_FILTER;
74         }
75     }
76 
77     /** Filter that matches all devices. */
78     private static final class AllFilter implements Filter {
matches(BluetoothDevice device)79         public boolean matches(BluetoothDevice device) {
80             return true;
81         }
82     }
83 
84     /** Filter that matches only bonded devices. */
85     private static final class BondedDeviceFilter implements Filter {
matches(BluetoothDevice device)86         public boolean matches(BluetoothDevice device) {
87             return device.getBondState() == BluetoothDevice.BOND_BONDED;
88         }
89     }
90 
91     /** Filter that matches only unbonded devices. */
92     private static final class UnbondedDeviceFilter implements Filter {
matches(BluetoothDevice device)93         public boolean matches(BluetoothDevice device) {
94             return device.getBondState() != BluetoothDevice.BOND_BONDED;
95         }
96     }
97 
98     /** Parent class of filters based on UUID and/or Bluetooth class. */
99     private abstract static class ClassUuidFilter implements Filter {
matches(ParcelUuid[] uuids, BluetoothClass btClass)100         abstract boolean matches(ParcelUuid[] uuids, BluetoothClass btClass);
101 
matches(BluetoothDevice device)102         public boolean matches(BluetoothDevice device) {
103             return matches(device.getUuids(), device.getBluetoothClass());
104         }
105     }
106 
107     /** Filter that matches devices that support AUDIO profiles. */
108     private static final class AudioFilter extends ClassUuidFilter {
109         @Override
matches(ParcelUuid[] uuids, BluetoothClass btClass)110         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
111             if (uuids != null) {
112                 if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS)) {
113                     return true;
114                 }
115                 if (BluetoothUuid.containsAnyUuid(uuids, HeadsetProfile.UUIDS)) {
116                     return true;
117                 }
118             } else if (btClass != null) {
119                 if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP) ||
120                         btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
121                     return true;
122                 }
123             }
124             return false;
125         }
126     }
127 
128     /** Filter that matches devices that support Object Transfer. */
129     private static final class TransferFilter extends ClassUuidFilter {
130         @Override
matches(ParcelUuid[] uuids, BluetoothClass btClass)131         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
132             if (uuids != null) {
133                 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
134                     return true;
135                 }
136             }
137             return btClass != null
138                     && btClass.doesClassMatch(BluetoothClass.PROFILE_OPP);
139         }
140     }
141 
142     /** Filter that matches devices that support PAN User (PANU) profile. */
143     private static final class PanuFilter extends ClassUuidFilter {
144         @Override
matches(ParcelUuid[] uuids, BluetoothClass btClass)145         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
146             if (uuids != null) {
147                 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU)) {
148                     return true;
149                 }
150             }
151             return btClass != null
152                     && btClass.doesClassMatch(BluetoothClass.PROFILE_PANU);
153         }
154     }
155 
156     /** Filter that matches devices that support NAP profile. */
157     private static final class NapFilter extends ClassUuidFilter {
158         @Override
matches(ParcelUuid[] uuids, BluetoothClass btClass)159         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
160             if (uuids != null) {
161                 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP)) {
162                     return true;
163                 }
164             }
165             return btClass != null
166                     && btClass.doesClassMatch(BluetoothClass.PROFILE_NAP);
167         }
168     }
169 }
170