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