1/*
2 * Copyright (C) 2008 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 android.bluetooth;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.nio.ByteBuffer;
23import java.nio.ByteOrder;
24import java.util.Arrays;
25
26/**
27 * Represents a Bluetooth class, which describes general characteristics
28 * and capabilities of a device. For example, a Bluetooth class will
29 * specify the general device type such as a phone, a computer, or
30 * headset, and whether it's capable of services such as audio or telephony.
31 *
32 * <p>Every Bluetooth class is composed of zero or more service classes, and
33 * exactly one device class. The device class is further broken down into major
34 * and minor device class components.
35 *
36 * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device
37 * (for example to show an icon in the UI), but does not reliably describe which
38 * Bluetooth profiles or services are actually supported by a device. Accurate
39 * service discovery is done through SDP requests, which are automatically
40 * performed when creating an RFCOMM socket with {@link
41 * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link
42 * BluetoothAdapter#listenUsingRfcommWithServiceRecord}</p>
43 *
44 * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
45 * a remote device.
46 *
47 * <!--
48 * The Bluetooth class is a 32 bit field. The format of these bits is defined at
49 * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
50 * (login required). This class contains that 32 bit field, and provides
51 * constants and methods to determine which Service Class(es) and Device Class
52 * are encoded in that field.
53 * -->
54 */
55public final class BluetoothClass implements Parcelable {
56    /**
57     * Legacy error value. Applications should use null instead.
58     *
59     * @hide
60     */
61    public static final int ERROR = 0xFF000000;
62
63    private final int mClass;
64
65    /** @hide */
66    public BluetoothClass(int classInt) {
67        mClass = classInt;
68    }
69
70    @Override
71    public boolean equals(Object o) {
72        if (o instanceof BluetoothClass) {
73            return mClass == ((BluetoothClass) o).mClass;
74        }
75        return false;
76    }
77
78    @Override
79    public int hashCode() {
80        return mClass;
81    }
82
83    @Override
84    public String toString() {
85        return Integer.toHexString(mClass);
86    }
87
88    @Override
89    public int describeContents() {
90        return 0;
91    }
92
93    public static final Parcelable.Creator<BluetoothClass> CREATOR =
94            new Parcelable.Creator<BluetoothClass>() {
95                public BluetoothClass createFromParcel(Parcel in) {
96                    return new BluetoothClass(in.readInt());
97                }
98
99                public BluetoothClass[] newArray(int size) {
100                    return new BluetoothClass[size];
101                }
102            };
103
104    @Override
105    public void writeToParcel(Parcel out, int flags) {
106        out.writeInt(mClass);
107    }
108
109    /**
110     * Defines all service class constants.
111     * <p>Each {@link BluetoothClass} encodes zero or more service classes.
112     */
113    public static final class Service {
114        private static final int BITMASK = 0xFFE000;
115
116        public static final int LIMITED_DISCOVERABILITY = 0x002000;
117        public static final int POSITIONING = 0x010000;
118        public static final int NETWORKING = 0x020000;
119        public static final int RENDER = 0x040000;
120        public static final int CAPTURE = 0x080000;
121        public static final int OBJECT_TRANSFER = 0x100000;
122        public static final int AUDIO = 0x200000;
123        public static final int TELEPHONY = 0x400000;
124        public static final int INFORMATION = 0x800000;
125    }
126
127    /**
128     * Return true if the specified service class is supported by this
129     * {@link BluetoothClass}.
130     * <p>Valid service classes are the public constants in
131     * {@link BluetoothClass.Service}. For example, {@link
132     * BluetoothClass.Service#AUDIO}.
133     *
134     * @param service valid service class
135     * @return true if the service class is supported
136     */
137    public boolean hasService(int service) {
138        return ((mClass & Service.BITMASK & service) != 0);
139    }
140
141    /**
142     * Defines all device class constants.
143     * <p>Each {@link BluetoothClass} encodes exactly one device class, with
144     * major and minor components.
145     * <p>The constants in {@link
146     * BluetoothClass.Device} represent a combination of major and minor
147     * device components (the complete device class). The constants in {@link
148     * BluetoothClass.Device.Major} represent only major device classes.
149     * <p>See {@link BluetoothClass.Service} for service class constants.
150     */
151    public static class Device {
152        private static final int BITMASK = 0x1FFC;
153
154        /**
155         * Defines all major device class constants.
156         * <p>See {@link BluetoothClass.Device} for minor classes.
157         */
158        public static class Major {
159            private static final int BITMASK = 0x1F00;
160
161            public static final int MISC = 0x0000;
162            public static final int COMPUTER = 0x0100;
163            public static final int PHONE = 0x0200;
164            public static final int NETWORKING = 0x0300;
165            public static final int AUDIO_VIDEO = 0x0400;
166            public static final int PERIPHERAL = 0x0500;
167            public static final int IMAGING = 0x0600;
168            public static final int WEARABLE = 0x0700;
169            public static final int TOY = 0x0800;
170            public static final int HEALTH = 0x0900;
171            public static final int UNCATEGORIZED = 0x1F00;
172        }
173
174        // Devices in the COMPUTER major class
175        public static final int COMPUTER_UNCATEGORIZED = 0x0100;
176        public static final int COMPUTER_DESKTOP = 0x0104;
177        public static final int COMPUTER_SERVER = 0x0108;
178        public static final int COMPUTER_LAPTOP = 0x010C;
179        public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110;
180        public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114;
181        public static final int COMPUTER_WEARABLE = 0x0118;
182
183        // Devices in the PHONE major class
184        public static final int PHONE_UNCATEGORIZED = 0x0200;
185        public static final int PHONE_CELLULAR = 0x0204;
186        public static final int PHONE_CORDLESS = 0x0208;
187        public static final int PHONE_SMART = 0x020C;
188        public static final int PHONE_MODEM_OR_GATEWAY = 0x0210;
189        public static final int PHONE_ISDN = 0x0214;
190
191        // Minor classes for the AUDIO_VIDEO major class
192        public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400;
193        public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404;
194        public static final int AUDIO_VIDEO_HANDSFREE = 0x0408;
195        //public static final int AUDIO_VIDEO_RESERVED              = 0x040C;
196        public static final int AUDIO_VIDEO_MICROPHONE = 0x0410;
197        public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414;
198        public static final int AUDIO_VIDEO_HEADPHONES = 0x0418;
199        public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C;
200        public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420;
201        public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424;
202        public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428;
203        public static final int AUDIO_VIDEO_VCR = 0x042C;
204        public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430;
205        public static final int AUDIO_VIDEO_CAMCORDER = 0x0434;
206        public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438;
207        public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C;
208        public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440;
209        //public static final int AUDIO_VIDEO_RESERVED              = 0x0444;
210        public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448;
211
212        // Devices in the WEARABLE major class
213        public static final int WEARABLE_UNCATEGORIZED = 0x0700;
214        public static final int WEARABLE_WRIST_WATCH = 0x0704;
215        public static final int WEARABLE_PAGER = 0x0708;
216        public static final int WEARABLE_JACKET = 0x070C;
217        public static final int WEARABLE_HELMET = 0x0710;
218        public static final int WEARABLE_GLASSES = 0x0714;
219
220        // Devices in the TOY major class
221        public static final int TOY_UNCATEGORIZED = 0x0800;
222        public static final int TOY_ROBOT = 0x0804;
223        public static final int TOY_VEHICLE = 0x0808;
224        public static final int TOY_DOLL_ACTION_FIGURE = 0x080C;
225        public static final int TOY_CONTROLLER = 0x0810;
226        public static final int TOY_GAME = 0x0814;
227
228        // Devices in the HEALTH major class
229        public static final int HEALTH_UNCATEGORIZED = 0x0900;
230        public static final int HEALTH_BLOOD_PRESSURE = 0x0904;
231        public static final int HEALTH_THERMOMETER = 0x0908;
232        public static final int HEALTH_WEIGHING = 0x090C;
233        public static final int HEALTH_GLUCOSE = 0x0910;
234        public static final int HEALTH_PULSE_OXIMETER = 0x0914;
235        public static final int HEALTH_PULSE_RATE = 0x0918;
236        public static final int HEALTH_DATA_DISPLAY = 0x091C;
237
238        // Devices in PERIPHERAL major class
239        /**
240         * @hide
241         */
242        public static final int PERIPHERAL_NON_KEYBOARD_NON_POINTING = 0x0500;
243        /**
244         * @hide
245         */
246        public static final int PERIPHERAL_KEYBOARD = 0x0540;
247        /**
248         * @hide
249         */
250        public static final int PERIPHERAL_POINTING = 0x0580;
251        /**
252         * @hide
253         */
254        public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0;
255    }
256
257    /**
258     * Return the major device class component of this {@link BluetoothClass}.
259     * <p>Values returned from this function can be compared with the
260     * public constants in {@link BluetoothClass.Device.Major} to determine
261     * which major class is encoded in this Bluetooth class.
262     *
263     * @return major device class component
264     */
265    public int getMajorDeviceClass() {
266        return (mClass & Device.Major.BITMASK);
267    }
268
269    /**
270     * Return the (major and minor) device class component of this
271     * {@link BluetoothClass}.
272     * <p>Values returned from this function can be compared with the
273     * public constants in {@link BluetoothClass.Device} to determine which
274     * device class is encoded in this Bluetooth class.
275     *
276     * @return device class component
277     */
278    public int getDeviceClass() {
279        return (mClass & Device.BITMASK);
280    }
281
282    /**
283     * Return the Bluetooth Class of Device (CoD) value including the
284     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
285     * minor device fields.
286     *
287     * <p>This value is an integer representation of Bluetooth CoD as in
288     * Bluetooth specification.
289     *
290     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
291     *
292     * @hide
293     */
294    public int getClassOfDevice() {
295        return mClass;
296    }
297
298    /**
299     * Return the Bluetooth Class of Device (CoD) value including the
300     * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and
301     * minor device fields.
302     *
303     * <p>This value is a byte array representation of Bluetooth CoD as in
304     * Bluetooth specification.
305     *
306     * <p>Bluetooth COD information is 3 bytes, but stored as an int. Hence the
307     * MSB is useless and needs to be thrown away. The lower 3 bytes are
308     * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN.
309     *
310     * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a>
311     *
312     * @hide
313     */
314    public byte[] getClassOfDeviceBytes() {
315        byte[] bytes = ByteBuffer.allocate(4)
316                .order(ByteOrder.BIG_ENDIAN)
317                .putInt(mClass)
318                .array();
319
320        // Discard the top byte
321        return Arrays.copyOfRange(bytes, 1, bytes.length);
322    }
323
324    /** @hide */
325    public static final int PROFILE_HEADSET = 0;
326    /** @hide */
327    public static final int PROFILE_A2DP = 1;
328    /** @hide */
329    public static final int PROFILE_OPP = 2;
330    /** @hide */
331    public static final int PROFILE_HID = 3;
332    /** @hide */
333    public static final int PROFILE_PANU = 4;
334    /** @hide */
335    public static final int PROFILE_NAP = 5;
336    /** @hide */
337    public static final int PROFILE_A2DP_SINK = 6;
338
339    /**
340     * Check class bits for possible bluetooth profile support.
341     * This is a simple heuristic that tries to guess if a device with the
342     * given class bits might support specified profile. It is not accurate for all
343     * devices. It tries to err on the side of false positives.
344     *
345     * @param profile The profile to be checked
346     * @return True if this device might support specified profile.
347     * @hide
348     */
349    public boolean doesClassMatch(int profile) {
350        if (profile == PROFILE_A2DP) {
351            if (hasService(Service.RENDER)) {
352                return true;
353            }
354            // By the A2DP spec, sinks must indicate the RENDER service.
355            // However we found some that do not (Chordette). So lets also
356            // match on some other class bits.
357            switch (getDeviceClass()) {
358                case Device.AUDIO_VIDEO_HIFI_AUDIO:
359                case Device.AUDIO_VIDEO_HEADPHONES:
360                case Device.AUDIO_VIDEO_LOUDSPEAKER:
361                case Device.AUDIO_VIDEO_CAR_AUDIO:
362                    return true;
363                default:
364                    return false;
365            }
366        } else if (profile == PROFILE_A2DP_SINK) {
367            if (hasService(Service.CAPTURE)) {
368                return true;
369            }
370            // By the A2DP spec, srcs must indicate the CAPTURE service.
371            // However if some device that do not, we try to
372            // match on some other class bits.
373            switch (getDeviceClass()) {
374                case Device.AUDIO_VIDEO_HIFI_AUDIO:
375                case Device.AUDIO_VIDEO_SET_TOP_BOX:
376                case Device.AUDIO_VIDEO_VCR:
377                    return true;
378                default:
379                    return false;
380            }
381        } else if (profile == PROFILE_HEADSET) {
382            // The render service class is required by the spec for HFP, so is a
383            // pretty good signal
384            if (hasService(Service.RENDER)) {
385                return true;
386            }
387            // Just in case they forgot the render service class
388            switch (getDeviceClass()) {
389                case Device.AUDIO_VIDEO_HANDSFREE:
390                case Device.AUDIO_VIDEO_WEARABLE_HEADSET:
391                case Device.AUDIO_VIDEO_CAR_AUDIO:
392                    return true;
393                default:
394                    return false;
395            }
396        } else if (profile == PROFILE_OPP) {
397            if (hasService(Service.OBJECT_TRANSFER)) {
398                return true;
399            }
400
401            switch (getDeviceClass()) {
402                case Device.COMPUTER_UNCATEGORIZED:
403                case Device.COMPUTER_DESKTOP:
404                case Device.COMPUTER_SERVER:
405                case Device.COMPUTER_LAPTOP:
406                case Device.COMPUTER_HANDHELD_PC_PDA:
407                case Device.COMPUTER_PALM_SIZE_PC_PDA:
408                case Device.COMPUTER_WEARABLE:
409                case Device.PHONE_UNCATEGORIZED:
410                case Device.PHONE_CELLULAR:
411                case Device.PHONE_CORDLESS:
412                case Device.PHONE_SMART:
413                case Device.PHONE_MODEM_OR_GATEWAY:
414                case Device.PHONE_ISDN:
415                    return true;
416                default:
417                    return false;
418            }
419        } else if (profile == PROFILE_HID) {
420            return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL;
421        } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) {
422            // No good way to distinguish between the two, based on class bits.
423            if (hasService(Service.NETWORKING)) {
424                return true;
425            }
426            return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING;
427        } else {
428            return false;
429        }
430    }
431}
432