BluetoothDevice.java revision e6ee3be1c254404dad842298f6f56c11cc6c7ac8
1/*
2 * Copyright (C) 2009 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.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.os.IBinder;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.os.ParcelUuid;
25import android.os.RemoteException;
26import android.os.ServiceManager;
27import android.util.Log;
28
29import java.io.IOException;
30import java.io.UnsupportedEncodingException;
31import java.util.UUID;
32
33/**
34 * Represents a remote Bluetooth device.
35 *
36 * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link
37 * BluetoothDevice}.
38 *
39 * <p>This class is really just a thin wrapper for a Bluetooth hardware
40 * address. Objects of this class are immutable. Operations on this class
41 * are performed on the remote Bluetooth hardware address, using the
42 * {@link BluetoothAdapter} that was used to create this {@link
43 * BluetoothDevice}.
44 */
45public final class BluetoothDevice implements Parcelable {
46    private static final String TAG = "BluetoothDevice";
47
48    /**
49     * Sentinel error value for this class. Guaranteed to not equal any other
50     * integer constant in this class. Provided as a convenience for functions
51     * that require a sentinel error value, for example:
52     * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
53     * BluetoothDevice.ERROR)</code>
54     */
55    public static final int ERROR = Integer.MIN_VALUE;
56
57    /**
58     * Broadcast Action: Remote device discovered.
59     * <p>Sent when a remote device is found during discovery.
60     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
61     * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
62     * {@link #EXTRA_RSSI} if they are available.
63     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
64     */
65     // TODO: Change API to not broadcast RSSI if not available (incoming connection)
66    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
67    public static final String ACTION_FOUND =
68            "android.bluetooth.device.action.FOUND";
69
70    /**
71     * Broadcast Action: Remote device disappeared.
72     * <p>Sent when a remote device that was found in the last discovery is not
73     * found in the current discovery.
74     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
75     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
76     * @hide
77     */
78    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
79    public static final String ACTION_DISAPPEARED =
80            "android.bluetooth.device.action.DISAPPEARED";
81
82    /**
83     * Broadcast Action: Bluetooth class of a remote device has changed.
84     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
85     * #EXTRA_CLASS}.
86     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
87     * @see {@link BluetoothClass}
88     */
89    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
90    public static final String ACTION_CLASS_CHANGED =
91            "android.bluetooth.device.action.CLASS_CHANGED";
92
93    /**
94     * Broadcast Action: Indicates a low level (ACL) connection has been
95     * established with a remote device.
96     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
97     * <p>ACL connections are managed automatically by the Android Bluetooth
98     * stack.
99     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
100     */
101    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
102    public static final String ACTION_ACL_CONNECTED =
103            "android.bluetooth.device.action.ACL_CONNECTED";
104
105    /**
106     * Broadcast Action: Indicates that a low level (ACL) disconnection has
107     * been requested for a remote device, and it will soon be disconnected.
108     * <p>This is useful for graceful disconnection. Applications should use
109     * this intent as a hint to immediately terminate higher level connections
110     * (RFCOMM, L2CAP, or profile connections) to the remote device.
111     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
112     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
113     */
114    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
115    public static final String ACTION_ACL_DISCONNECT_REQUESTED =
116            "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
117
118    /**
119     * Broadcast Action: Indicates a low level (ACL) disconnection from a
120     * remote device.
121     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
122     * <p>ACL connections are managed automatically by the Android Bluetooth
123     * stack.
124     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
125     */
126    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
127    public static final String ACTION_ACL_DISCONNECTED =
128            "android.bluetooth.device.action.ACL_DISCONNECTED";
129
130    /**
131     * Broadcast Action: Indicates the friendly name of a remote device has
132     * been retrieved for the first time, or changed since the last retrieval.
133     * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
134     * #EXTRA_NAME}.
135     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
136     */
137    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
138    public static final String ACTION_NAME_CHANGED =
139            "android.bluetooth.device.action.NAME_CHANGED";
140
141    /**
142     * Broadcast Action: Indicates a change in the bond state of a remote
143     * device. For example, if a device is bonded (paired).
144     * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
145     * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
146     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
147     */
148    // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
149    // contain a hidden extra field EXTRA_REASON with the result code.
150    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
151    public static final String ACTION_BOND_STATE_CHANGED =
152            "android.bluetooth.device.action.BOND_STATE_CHANGED";
153
154    /**
155     * Used as a Parcelable {@link BluetoothDevice} extra field in every intent
156     * broadcast by this class. It contains the {@link BluetoothDevice} that
157     * the intent applies to.
158     */
159    public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
160
161    /**
162     * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link
163     * #ACTION_FOUND} intents. It contains the friendly Bluetooth name.
164     */
165    public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
166
167    /**
168     * Used as an optional short extra field in {@link #ACTION_FOUND} intents.
169     * Contains the RSSI value of the remote device as reported by the
170     * Bluetooth hardware.
171     */
172    public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
173
174    /**
175     * Used as an Parcelable {@link BluetoothClass} extra field in {@link
176     * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
177     */
178    public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
179
180    /**
181     * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
182     * Contains the bond state of the remote device.
183     * <p>Possible values are:
184     * {@link #BOND_NONE},
185     * {@link #BOND_BONDING},
186     * {@link #BOND_BONDED}.
187      */
188    public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
189    /**
190     * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
191     * Contains the previous bond state of the remote device.
192     * <p>Possible values are:
193     * {@link #BOND_NONE},
194     * {@link #BOND_BONDING},
195     * {@link #BOND_BONDED}.
196      */
197    public static final String EXTRA_PREVIOUS_BOND_STATE =
198            "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
199    /**
200     * Indicates the remote device is not bonded (paired).
201     * <p>There is no shared link key with the remote device, so communication
202     * (if it is allowed at all) will be unauthenticated and unencrypted.
203     */
204    public static final int BOND_NONE = 10;
205    /**
206     * Indicates bonding (pairing) is in progress with the remote device.
207     */
208    public static final int BOND_BONDING = 11;
209    /**
210     * Indicates the remote device is bonded (paired).
211     * <p>A shared link keys exists locally for the remote device, so
212     * communication can be authenticated and encrypted.
213     * <p><i>Being bonded (paired) with a remote device does not necessarily
214     * mean the device is currently connected. It just means that the ponding
215     * procedure was compeleted at some earlier time, and the link key is still
216     * stored locally, ready to use on the next connection.
217     * </i>
218     */
219    public static final int BOND_BONDED = 12;
220
221    /** @hide */
222    public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
223    /** @hide */
224    public static final String EXTRA_PAIRING_VARIANT =
225            "android.bluetooth.device.extra.PAIRING_VARIANT";
226    /** @hide */
227    public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY";
228
229    /**
230     * Broadcast Action: This intent is used to broadcast the {@link UUID}
231     * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
232     * has been fetched. This intent is sent only when the UUIDs of the remote
233     * device are requested to be fetched using Service Discovery Protocol
234     * <p> Always contains the extra field {@link #EXTRA_DEVICE}
235     * <p> Always contains the extra filed {@link #EXTRA_UUID}
236     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
237     * @hide
238     */
239    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
240    public static final String ACTION_UUID =
241            "android.bleutooth.device.action.UUID";
242
243    /**
244     * Broadcast Action: Indicates a failure to retrieve the name of a remote
245     * device.
246     * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
247     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
248     * @hide
249     */
250    //TODO: is this actually useful?
251    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
252    public static final String ACTION_NAME_FAILED =
253            "android.bluetooth.device.action.NAME_FAILED";
254
255    /** @hide */
256    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
257    public static final String ACTION_PAIRING_REQUEST =
258            "android.bluetooth.device.action.PAIRING_REQUEST";
259    /** @hide */
260    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
261    public static final String ACTION_PAIRING_CANCEL =
262            "android.bluetooth.device.action.PAIRING_CANCEL";
263
264    /** A bond attempt succeeded
265     * @hide */
266    public static final int BOND_SUCCESS = 0;
267    /** A bond attempt failed because pins did not match, or remote device did
268     * not respond to pin request in time
269     * @hide */
270    public static final int UNBOND_REASON_AUTH_FAILED = 1;
271    /** A bond attempt failed because the other side explicilty rejected
272     * bonding
273     * @hide */
274    public static final int UNBOND_REASON_AUTH_REJECTED = 2;
275    /** A bond attempt failed because we canceled the bonding process
276     * @hide */
277    public static final int UNBOND_REASON_AUTH_CANCELED = 3;
278    /** A bond attempt failed because we could not contact the remote device
279     * @hide */
280    public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
281    /** A bond attempt failed because a discovery is in progress
282     * @hide */
283    public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
284    /** A bond attempt failed because of authentication timeout
285     * @hide */
286    public static final int UNBOND_REASON_AUTH_TIMEOUT = 6;
287    /** A bond attempt failed because of repeated attempts
288     * @hide */
289    public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7;
290    /** A bond attempt failed because we received an Authentication Cancel
291     *  by remote end
292     * @hide */
293    public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
294    /** An existing bond was explicitly revoked
295     * @hide */
296    public static final int UNBOND_REASON_REMOVED = 9;
297
298    /** The user will be prompted to enter a pin
299     * @hide */
300    public static final int PAIRING_VARIANT_PIN = 0;
301    /** The user will be prompted to enter a passkey
302     * @hide */
303    public static final int PAIRING_VARIANT_PASSKEY = 1;
304    /** The user will be prompted to confirm the passkey displayed on the screen
305     * @hide */
306    public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2;
307    /** The user will be prompted to accept or deny the incoming pairing request
308     * @hide */
309    public static final int PAIRING_VARIANT_CONSENT = 3;
310    /** The user will be prompted to enter the passkey displayed on remote device
311     * @hide */
312    public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
313
314    /**
315     * Used as an extra field in {@link #ACTION_UUID} intents,
316     * Contains the {@link android.os.ParcelUuid}s of the remote device which
317     * is a parcelable version of {@link UUID}.
318     * @hide
319     */
320    public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
321
322    /**
323     * Lazy initialization. Guaranteed final after first object constructed, or
324     * getService() called.
325     * TODO: Unify implementation of sService amongst BluetoothFoo API's
326     */
327    private static IBluetooth sService;
328
329    private final String mAddress;
330
331    /*package*/ static IBluetooth getService() {
332        synchronized (BluetoothDevice.class) {
333            if (sService == null) {
334                IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
335                if (b == null) {
336                    throw new RuntimeException("Bluetooth service not available");
337                }
338                sService = IBluetooth.Stub.asInterface(b);
339            }
340        }
341        return sService;
342    }
343
344    /**
345     * Create a new BluetoothDevice
346     * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
347     * and is validated in this constructor.
348     * @param address valid Bluetooth MAC address
349     * @throws RuntimeException Bluetooth is not available on this platform
350     * @throws IllegalArgumentException address is invalid
351     * @hide
352     */
353    /*package*/ BluetoothDevice(String address) {
354        getService();  // ensures sService is initialized
355        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
356            throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
357        }
358
359        mAddress = address;
360    }
361
362    @Override
363    public boolean equals(Object o) {
364        if (o instanceof BluetoothDevice) {
365            return mAddress.equals(((BluetoothDevice)o).getAddress());
366        }
367        return false;
368    }
369
370    @Override
371    public int hashCode() {
372        return mAddress.hashCode();
373    }
374
375    /**
376     * Returns a string representation of this BluetoothDevice.
377     * <p>Currently this is the Bluetooth hardware address, for example
378     * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
379     * if you explicitly require the Bluetooth hardware address in case the
380     * {@link #toString} representation changes in the future.
381     * @return string representation of this BluetoothDevice
382     */
383    @Override
384    public String toString() {
385        return mAddress;
386    }
387
388    public int describeContents() {
389        return 0;
390    }
391
392    public static final Parcelable.Creator<BluetoothDevice> CREATOR =
393            new Parcelable.Creator<BluetoothDevice>() {
394        public BluetoothDevice createFromParcel(Parcel in) {
395            return new BluetoothDevice(in.readString());
396        }
397        public BluetoothDevice[] newArray(int size) {
398            return new BluetoothDevice[size];
399        }
400    };
401
402    public void writeToParcel(Parcel out, int flags) {
403        out.writeString(mAddress);
404    }
405
406    /**
407     * Returns the hardware address of this BluetoothDevice.
408     * <p> For example, "00:11:22:AA:BB:CC".
409     * @return Bluetooth hardware address as string
410     */
411    public String getAddress() {
412        return mAddress;
413    }
414
415    /**
416     * Get the friendly Bluetooth name of the remote device.
417     *
418     * <p>The local adapter will automatically retrieve remote names when
419     * performing a device scan, and will cache them. This method just returns
420     * the name for this device from the cache.
421     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
422     *
423     * @return the Bluetooth name, or null if there was a problem.
424     */
425    public String getName() {
426        try {
427            return sService.getRemoteName(mAddress);
428        } catch (RemoteException e) {Log.e(TAG, "", e);}
429        return null;
430    }
431
432    /**
433     * Start the bonding (pairing) process with the remote device.
434     * <p>This is an asynchronous call, it will return immediately. Register
435     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
436     * the bonding process completes, and its result.
437     * <p>Android system services will handle the necessary user interactions
438     * to confirm and complete the bonding process.
439     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
440     *
441     * @return false on immediate error, true if bonding will begin
442     * @hide
443     */
444    public boolean createBond() {
445        try {
446            return sService.createBond(mAddress);
447        } catch (RemoteException e) {Log.e(TAG, "", e);}
448        return false;
449    }
450
451    /**
452     * Cancel an in-progress bonding request started with {@link #createBond}.
453     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
454     *
455     * @return true on sucess, false on error
456     * @hide
457     */
458    public boolean cancelBondProcess() {
459        try {
460            return sService.cancelBondProcess(mAddress);
461        } catch (RemoteException e) {Log.e(TAG, "", e);}
462        return false;
463    }
464
465    /**
466     * Remove bond (pairing) with the remote device.
467     * <p>Delete the link key associated with the remote device, and
468     * immediately terminate connections to that device that require
469     * authentication and encryption.
470     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
471     *
472     * @return true on sucess, false on error
473     * @hide
474     */
475    public boolean removeBond() {
476        try {
477            return sService.removeBond(mAddress);
478        } catch (RemoteException e) {Log.e(TAG, "", e);}
479        return false;
480    }
481
482    /**
483     * Get the bond state of the remote device.
484     * <p>Possible values for the bond state are:
485     * {@link #BOND_NONE},
486     * {@link #BOND_BONDING},
487     * {@link #BOND_BONDED}.
488     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
489     *
490     * @return the bond state
491     */
492    public int getBondState() {
493        try {
494            return sService.getBondState(mAddress);
495        } catch (RemoteException e) {Log.e(TAG, "", e);}
496        return BOND_NONE;
497    }
498
499    /**
500     * Get the Bluetooth class of the remote device.
501     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
502     *
503     * @return Bluetooth class object, or null on error
504     */
505    public BluetoothClass getBluetoothClass() {
506        try {
507            int classInt = sService.getRemoteClass(mAddress);
508            if (classInt == BluetoothClass.ERROR) return null;
509            return new BluetoothClass(classInt);
510        } catch (RemoteException e) {Log.e(TAG, "", e);}
511        return null;
512    }
513
514    /**
515     * Get trust state of a remote device.
516     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
517     * @hide
518     */
519    public boolean getTrustState() {
520        try {
521            return sService.getTrustState(mAddress);
522        } catch (RemoteException e) {
523            Log.e(TAG, "", e);
524        }
525        return false;
526    }
527
528    /**
529     * Set trust state for a remote device.
530     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
531     * @param value the trust state value (true or false)
532     * @hide
533     */
534    public boolean setTrust(boolean value) {
535        try {
536            return sService.setTrust(mAddress, value);
537        } catch (RemoteException e) {
538            Log.e(TAG, "", e);
539        }
540        return false;
541    }
542
543    /** @hide */
544     public ParcelUuid[] getUuids() {
545        try {
546            return sService.getRemoteUuids(mAddress);
547        } catch (RemoteException e) {Log.e(TAG, "", e);}
548        return null;
549    }
550
551     /**
552      *  Perform a SDP query on the remote device to get the UUIDs
553      *  supported. This API is asynchronous and an Intent is sent,
554      *  with the UUIDs supported by the remote end. If there is an error
555      *  in getting the SDP records or if the process takes a long time,
556      *  an Intent is sent with the UUIDs that is currently present in the
557      *  cache. Clients should use the {@link getUuids} to get UUIDs
558      *  is SDP is not to be performed.
559      *
560      *  @return False if the sanity check fails, True if the process
561      *               of initiating an ACL connection to the remote device
562      *               was started.
563      *  @hide
564      */
565     public boolean fetchUuidsWithSdp() {
566        try {
567            return sService.fetchRemoteUuids(mAddress, null, null);
568        } catch (RemoteException e) {Log.e(TAG, "", e);}
569        return false;
570    }
571
572    /** @hide */
573    public int getServiceChannel(ParcelUuid uuid) {
574         try {
575             return sService.getRemoteServiceChannel(mAddress, uuid);
576         } catch (RemoteException e) {Log.e(TAG, "", e);}
577         return BluetoothDevice.ERROR;
578    }
579
580    /** @hide */
581    public boolean setPin(byte[] pin) {
582        try {
583            return sService.setPin(mAddress, pin);
584        } catch (RemoteException e) {Log.e(TAG, "", e);}
585        return false;
586    }
587
588    /** @hide */
589    public boolean setPasskey(int passkey) {
590        try {
591            return sService.setPasskey(mAddress, passkey);
592        } catch (RemoteException e) {Log.e(TAG, "", e);}
593        return false;
594    }
595
596    /** @hide */
597    public boolean setPairingConfirmation(boolean confirm) {
598        try {
599            return sService.setPairingConfirmation(mAddress, confirm);
600        } catch (RemoteException e) {Log.e(TAG, "", e);}
601        return false;
602    }
603
604    /** @hide */
605    public boolean cancelPairingUserInput() {
606        try {
607            return sService.cancelPairingUserInput(mAddress);
608        } catch (RemoteException e) {Log.e(TAG, "", e);}
609        return false;
610    }
611
612    /**
613     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
614     * outgoing connection to this remote device on given channel.
615     * <p>The remote device will be authenticated and communication on this
616     * socket will be encrypted.
617     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
618     * connection.
619     * <p>Valid RFCOMM channels are in range 1 to 30.
620     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
621     *
622     * @param channel RFCOMM channel to connect to
623     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
624     * @throws IOException on error, for example Bluetooth not available, or
625     *                     insufficient permissions
626     * @hide
627     */
628    public BluetoothSocket createRfcommSocket(int channel) throws IOException {
629        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
630                null);
631    }
632
633    /**
634     * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
635     * outgoing connection to this remote device using SDP lookup of uuid.
636     * <p>This is designed to be used with {@link
637     * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
638     * Bluetooth applications.
639     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
640     * connection. This will also perform an SDP lookup of the given uuid to
641     * determine which channel to connect to.
642     * <p>The remote device will be authenticated and communication on this
643     * socket will be encrypted.
644     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
645     *
646     * @param uuid service record uuid to lookup RFCOMM channel
647     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
648     * @throws IOException on error, for example Bluetooth not available, or
649     *                     insufficient permissions
650     */
651    public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
652        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
653                new ParcelUuid(uuid));
654    }
655
656    /**
657     * Construct an insecure RFCOMM socket ready to start an outgoing
658     * connection.
659     * Call #connect on the returned #BluetoothSocket to begin the connection.
660     * The remote device will not be authenticated and communication on this
661     * socket will not be encrypted.
662     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
663     *
664     * @param port    remote port
665     * @return An RFCOMM BluetoothSocket
666     * @throws IOException On error, for example Bluetooth not available, or
667     *                     insufficient permissions.
668     * @hide
669     */
670    public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
671        return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
672                null);
673    }
674
675    /**
676     * Construct a SCO socket ready to start an outgoing connection.
677     * Call #connect on the returned #BluetoothSocket to begin the connection.
678     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
679     *
680     * @return a SCO BluetoothSocket
681     * @throws IOException on error, for example Bluetooth not available, or
682     *                     insufficient permissions.
683     * @hide
684     */
685    public BluetoothSocket createScoSocket() throws IOException {
686        return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
687    }
688
689    /**
690     * Check that a pin is valid and convert to byte array.
691     *
692     * Bluetooth pin's are 1 to 16 bytes of UTF8 characters.
693     * @param pin pin as java String
694     * @return the pin code as a UTF8 byte array, or null if it is an invalid
695     *         Bluetooth pin.
696     * @hide
697     */
698    public static byte[] convertPinToBytes(String pin) {
699        if (pin == null) {
700            return null;
701        }
702        byte[] pinBytes;
703        try {
704            pinBytes = pin.getBytes("UTF8");
705        } catch (UnsupportedEncodingException uee) {
706            Log.e(TAG, "UTF8 not supported?!?");  // this should not happen
707            return null;
708        }
709        if (pinBytes.length <= 0 || pinBytes.length > 16) {
710            return null;
711        }
712        return pinBytes;
713    }
714
715}
716