Home | History | Annotate | Download | only in bluetooth
      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 
     17 package android.bluetooth;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.annotation.UnsupportedAppUsage;
     22 import android.content.Context;
     23 import android.os.Binder;
     24 import android.os.IBinder;
     25 import android.os.RemoteException;
     26 import android.util.Log;
     27 
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 
     31 /**
     32  * This class provides the APIs to control the Bluetooth Pan
     33  * Profile.
     34  *
     35  * <p>BluetoothPan is a proxy object for controlling the Bluetooth
     36  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
     37  * the BluetoothPan proxy object.
     38  *
     39  * <p>Each method is protected with its appropriate permission.
     40  *
     41  * @hide
     42  */
     43 public final class BluetoothPan implements BluetoothProfile {
     44     private static final String TAG = "BluetoothPan";
     45     private static final boolean DBG = true;
     46     private static final boolean VDBG = false;
     47 
     48     /**
     49      * Intent used to broadcast the change in connection state of the Pan
     50      * profile.
     51      *
     52      * <p>This intent will have 4 extras:
     53      * <ul>
     54      * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     55      * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
     56      * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     57      * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is
     58      * bound to. </li>
     59      * </ul>
     60      *
     61      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     62      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     63      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     64      *
     65      * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
     66      * {@link #LOCAL_PANU_ROLE}
     67      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     68      * receive.
     69      */
     70     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     71     public static final String ACTION_CONNECTION_STATE_CHANGED =
     72             "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
     73 
     74     /**
     75      * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent
     76      * The local role of the PAN profile that the remote device is bound to.
     77      * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}.
     78      */
     79     public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
     80 
     81     public static final int PAN_ROLE_NONE = 0;
     82     /**
     83      * The local device is acting as a Network Access Point.
     84      */
     85     public static final int LOCAL_NAP_ROLE = 1;
     86     public static final int REMOTE_NAP_ROLE = 1;
     87 
     88     /**
     89      * The local device is acting as a PAN User.
     90      */
     91     public static final int LOCAL_PANU_ROLE = 2;
     92     public static final int REMOTE_PANU_ROLE = 2;
     93 
     94     /**
     95      * Return codes for the connect and disconnect Bluez / Dbus calls.
     96      *
     97      * @hide
     98      */
     99     public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000;
    100 
    101     /**
    102      * @hide
    103      */
    104     public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001;
    105 
    106     /**
    107      * @hide
    108      */
    109     public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002;
    110 
    111     /**
    112      * @hide
    113      */
    114     public static final int PAN_OPERATION_GENERIC_FAILURE = 1003;
    115 
    116     /**
    117      * @hide
    118      */
    119     public static final int PAN_OPERATION_SUCCESS = 1004;
    120 
    121     private BluetoothAdapter mAdapter;
    122     private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector =
    123             new BluetoothProfileConnector(this, BluetoothProfile.PAN,
    124                     "BluetoothPan", IBluetoothPan.class.getName()) {
    125                 @Override
    126                 public IBluetoothPan getServiceInterface(IBinder service) {
    127                     return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
    128                 }
    129     };
    130 
    131 
    132     /**
    133      * Create a BluetoothPan proxy object for interacting with the local
    134      * Bluetooth Service which handles the Pan profile
    135      */
    136     @UnsupportedAppUsage
    137     /*package*/ BluetoothPan(Context context, ServiceListener listener) {
    138         mAdapter = BluetoothAdapter.getDefaultAdapter();
    139         mProfileConnector.connect(context, listener);
    140     }
    141 
    142     @UnsupportedAppUsage
    143     /*package*/ void close() {
    144         if (VDBG) log("close()");
    145         mProfileConnector.disconnect();
    146     }
    147 
    148     private IBluetoothPan getService() {
    149         return mProfileConnector.getService();
    150     }
    151 
    152 
    153     protected void finalize() {
    154         close();
    155     }
    156 
    157     /**
    158      * Initiate connection to a profile of the remote bluetooth device.
    159      *
    160      * <p> This API returns false in scenarios like the profile on the
    161      * device is already connected or Bluetooth is not turned on.
    162      * When this API returns true, it is guaranteed that
    163      * connection state intent for the profile will be broadcasted with
    164      * the state. Users can get the connection state of the profile
    165      * from this intent.
    166      *
    167      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    168      * permission.
    169      *
    170      * @param device Remote Bluetooth Device
    171      * @return false on immediate error, true otherwise
    172      * @hide
    173      */
    174     @UnsupportedAppUsage
    175     public boolean connect(BluetoothDevice device) {
    176         if (DBG) log("connect(" + device + ")");
    177         final IBluetoothPan service = getService();
    178         if (service != null && isEnabled() && isValidDevice(device)) {
    179             try {
    180                 return service.connect(device);
    181             } catch (RemoteException e) {
    182                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    183                 return false;
    184             }
    185         }
    186         if (service == null) Log.w(TAG, "Proxy not attached to service");
    187         return false;
    188     }
    189 
    190     /**
    191      * Initiate disconnection from a profile
    192      *
    193      * <p> This API will return false in scenarios like the profile on the
    194      * Bluetooth device is not in connected state etc. When this API returns,
    195      * true, it is guaranteed that the connection state change
    196      * intent will be broadcasted with the state. Users can get the
    197      * disconnection state of the profile from this intent.
    198      *
    199      * <p> If the disconnection is initiated by a remote device, the state
    200      * will transition from {@link #STATE_CONNECTED} to
    201      * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
    202      * host (local) device the state will transition from
    203      * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
    204      * state {@link #STATE_DISCONNECTED}. The transition to
    205      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
    206      * two scenarios.
    207      *
    208      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    209      * permission.
    210      *
    211      * @param device Remote Bluetooth Device
    212      * @return false on immediate error, true otherwise
    213      * @hide
    214      */
    215     @UnsupportedAppUsage
    216     public boolean disconnect(BluetoothDevice device) {
    217         if (DBG) log("disconnect(" + device + ")");
    218         final IBluetoothPan service = getService();
    219         if (service != null && isEnabled() && isValidDevice(device)) {
    220             try {
    221                 return service.disconnect(device);
    222             } catch (RemoteException e) {
    223                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    224                 return false;
    225             }
    226         }
    227         if (service == null) Log.w(TAG, "Proxy not attached to service");
    228         return false;
    229     }
    230 
    231     /**
    232      * {@inheritDoc}
    233      */
    234     @Override
    235     public List<BluetoothDevice> getConnectedDevices() {
    236         if (VDBG) log("getConnectedDevices()");
    237         final IBluetoothPan service = getService();
    238         if (service != null && isEnabled()) {
    239             try {
    240                 return service.getConnectedDevices();
    241             } catch (RemoteException e) {
    242                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    243                 return new ArrayList<BluetoothDevice>();
    244             }
    245         }
    246         if (service == null) Log.w(TAG, "Proxy not attached to service");
    247         return new ArrayList<BluetoothDevice>();
    248     }
    249 
    250     /**
    251      * {@inheritDoc}
    252      */
    253     @Override
    254     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    255         if (VDBG) log("getDevicesMatchingStates()");
    256         final IBluetoothPan service = getService();
    257         if (service != null && isEnabled()) {
    258             try {
    259                 return service.getDevicesMatchingConnectionStates(states);
    260             } catch (RemoteException e) {
    261                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    262                 return new ArrayList<BluetoothDevice>();
    263             }
    264         }
    265         if (service == null) Log.w(TAG, "Proxy not attached to service");
    266         return new ArrayList<BluetoothDevice>();
    267     }
    268 
    269     /**
    270      * {@inheritDoc}
    271      */
    272     @Override
    273     public int getConnectionState(BluetoothDevice device) {
    274         if (VDBG) log("getState(" + device + ")");
    275         final IBluetoothPan service = getService();
    276         if (service != null && isEnabled() && isValidDevice(device)) {
    277             try {
    278                 return service.getConnectionState(device);
    279             } catch (RemoteException e) {
    280                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    281                 return BluetoothProfile.STATE_DISCONNECTED;
    282             }
    283         }
    284         if (service == null) Log.w(TAG, "Proxy not attached to service");
    285         return BluetoothProfile.STATE_DISCONNECTED;
    286     }
    287 
    288     @UnsupportedAppUsage
    289     public void setBluetoothTethering(boolean value) {
    290         if (DBG) log("setBluetoothTethering(" + value + ")");
    291         final IBluetoothPan service = getService();
    292         if (service != null && isEnabled()) {
    293             try {
    294                 service.setBluetoothTethering(value);
    295             } catch (RemoteException e) {
    296                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    297             }
    298         }
    299     }
    300 
    301     @UnsupportedAppUsage
    302     public boolean isTetheringOn() {
    303         if (VDBG) log("isTetheringOn()");
    304         final IBluetoothPan service = getService();
    305         if (service != null && isEnabled()) {
    306             try {
    307                 return service.isTetheringOn();
    308             } catch (RemoteException e) {
    309                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    310             }
    311         }
    312         return false;
    313     }
    314 
    315     @UnsupportedAppUsage
    316     private boolean isEnabled() {
    317         return mAdapter.getState() == BluetoothAdapter.STATE_ON;
    318     }
    319 
    320     @UnsupportedAppUsage
    321     private static boolean isValidDevice(BluetoothDevice device) {
    322         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
    323     }
    324 
    325     @UnsupportedAppUsage
    326     private static void log(String msg) {
    327         Log.d(TAG, msg);
    328     }
    329 }
    330