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
17package com.android.settingslib.bluetooth;
18
19import android.bluetooth.BluetoothClass;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothUuid;
22import android.os.ParcelUuid;
23import 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 */
31public final class BluetoothDeviceFilter {
32    private static final String TAG = "BluetoothDeviceFilter";
33
34    /** The filter interface to external classes. */
35    public interface Filter {
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. */
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     */
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 {
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 {
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 {
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 {
100        abstract boolean matches(ParcelUuid[] uuids, BluetoothClass btClass);
101
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
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
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
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
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