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.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.ServiceConnection;
     25 import android.os.IBinder;
     26 import android.os.RemoteException;
     27 import android.os.ServiceManager;
     28 import android.util.Log;
     29 
     30 import java.util.ArrayList;
     31 import java.util.List;
     32 
     33 /**
     34  * This class provides the APIs to control the Bluetooth Pan
     35  * Profile.
     36  *
     37  *<p>BluetoothPan is a proxy object for controlling the Bluetooth
     38  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
     39  * the BluetoothPan proxy object.
     40  *
     41  *<p>Each method is protected with its appropriate permission.
     42  *@hide
     43  */
     44 public final class BluetoothPan implements BluetoothProfile {
     45     private static final String TAG = "BluetoothPan";
     46     private static final boolean DBG = true;
     47     private static final boolean VDBG = false;
     48 
     49     /**
     50      * Intent used to broadcast the change in connection state of the Pan
     51      * profile.
     52      *
     53      * <p>This intent will have 4 extras:
     54      * <ul>
     55      *   <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     56      *   <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
     57      *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     58      *   <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is
     59      *   bound to. </li>
     60      * </ul>
     61      *
     62      * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     63      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     64      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     65      *
     66      * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
     67      * {@link #LOCAL_PANU_ROLE}
     68      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     69      * receive.
     70      */
     71     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     72     public static final String ACTION_CONNECTION_STATE_CHANGED =
     73         "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
     74 
     75     /**
     76      * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent
     77      * The local role of the PAN profile that the remote device is bound to.
     78      * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}.
     79      */
     80     public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
     81 
     82     public static final int PAN_ROLE_NONE = 0;
     83     /**
     84      * The local device is acting as a Network Access Point.
     85      */
     86     public static final int LOCAL_NAP_ROLE = 1;
     87     public static final int REMOTE_NAP_ROLE = 1;
     88 
     89     /**
     90      * The local device is acting as a PAN User.
     91      */
     92     public static final int LOCAL_PANU_ROLE = 2;
     93     public static final int REMOTE_PANU_ROLE = 2;
     94 
     95     /**
     96      * Return codes for the connect and disconnect Bluez / Dbus calls.
     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 Context mContext;
    122     private ServiceListener mServiceListener;
    123     private BluetoothAdapter mAdapter;
    124     private IBluetoothPan mPanService;
    125 
    126     /**
    127      * Create a BluetoothPan proxy object for interacting with the local
    128      * Bluetooth Service which handles the Pan profile
    129      *
    130      */
    131     /*package*/ BluetoothPan(Context context, ServiceListener l) {
    132         mContext = context;
    133         mServiceListener = l;
    134         mAdapter = BluetoothAdapter.getDefaultAdapter();
    135         try {
    136             mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback);
    137         } catch (RemoteException re) {
    138             Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
    139         }
    140         Log.d(TAG, "BluetoothPan() call bindService");
    141         if (!context.bindService(new Intent(IBluetoothPan.class.getName()),
    142                                  mConnection, 0)) {
    143             Log.e(TAG, "Could not bind to Bluetooth HID Service");
    144         }
    145         Log.d(TAG, "BluetoothPan(), bindService called");
    146     }
    147 
    148     /*package*/ void close() {
    149         if (VDBG) log("close()");
    150         if (mConnection != null) {
    151             mContext.unbindService(mConnection);
    152             mConnection = null;
    153         }
    154         mServiceListener = null;
    155         try {
    156             mAdapter.getBluetoothManager().unregisterStateChangeCallback(mStateChangeCallback);
    157         } catch (RemoteException re) {
    158             Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
    159         }
    160     }
    161 
    162     protected void finalize() {
    163         close();
    164     }
    165 
    166     private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() {
    167 
    168         @Override
    169         public void onBluetoothStateChange(boolean on) throws RemoteException {
    170             //Handle enable request to bind again.
    171             if (on) {
    172                 Log.d(TAG, "onBluetoothStateChange(on) call bindService");
    173                 if (!mContext.bindService(new Intent(IBluetoothPan.class.getName()),
    174                                      mConnection, 0)) {
    175                     Log.e(TAG, "Could not bind to Bluetooth HID Service");
    176                 }
    177                 Log.d(TAG, "BluetoothPan(), bindService called");
    178             } else {
    179                 if (VDBG) Log.d(TAG,"Unbinding service...");
    180                 synchronized (mConnection) {
    181                     try {
    182                         mPanService = null;
    183                         mContext.unbindService(mConnection);
    184                     } catch (Exception re) {
    185                         Log.e(TAG,"",re);
    186                     }
    187                 }
    188             }
    189         }
    190     };
    191 
    192     /**
    193      * Initiate connection to a profile of the remote bluetooth device.
    194      *
    195      * <p> This API returns false in scenarios like the profile on the
    196      * device is already connected or Bluetooth is not turned on.
    197      * When this API returns true, it is guaranteed that
    198      * connection state intent for the profile will be broadcasted with
    199      * the state. Users can get the connection state of the profile
    200      * from this intent.
    201      *
    202      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    203      * permission.
    204      *
    205      * @param device Remote Bluetooth Device
    206      * @return false on immediate error,
    207      *               true otherwise
    208      * @hide
    209      */
    210     public boolean connect(BluetoothDevice device) {
    211         if (DBG) log("connect(" + device + ")");
    212         if (mPanService != null && isEnabled() &&
    213             isValidDevice(device)) {
    214             try {
    215                 return mPanService.connect(device);
    216             } catch (RemoteException e) {
    217                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    218                 return false;
    219             }
    220         }
    221         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    222         return false;
    223     }
    224 
    225     /**
    226      * Initiate disconnection from a profile
    227      *
    228      * <p> This API will return false in scenarios like the profile on the
    229      * Bluetooth device is not in connected state etc. When this API returns,
    230      * true, it is guaranteed that the connection state change
    231      * intent will be broadcasted with the state. Users can get the
    232      * disconnection state of the profile from this intent.
    233      *
    234      * <p> If the disconnection is initiated by a remote device, the state
    235      * will transition from {@link #STATE_CONNECTED} to
    236      * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
    237      * host (local) device the state will transition from
    238      * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
    239      * state {@link #STATE_DISCONNECTED}. The transition to
    240      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
    241      * two scenarios.
    242      *
    243      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    244      * permission.
    245      *
    246      * @param device Remote Bluetooth Device
    247      * @return false on immediate error,
    248      *               true otherwise
    249      * @hide
    250      */
    251     public boolean disconnect(BluetoothDevice device) {
    252         if (DBG) log("disconnect(" + device + ")");
    253         if (mPanService != null && isEnabled() &&
    254             isValidDevice(device)) {
    255             try {
    256                 return mPanService.disconnect(device);
    257             } catch (RemoteException e) {
    258                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    259                 return false;
    260             }
    261         }
    262         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    263         return false;
    264     }
    265 
    266     /**
    267      * {@inheritDoc}
    268      */
    269     public List<BluetoothDevice> getConnectedDevices() {
    270         if (VDBG) log("getConnectedDevices()");
    271         if (mPanService != null && isEnabled()) {
    272             try {
    273                 return mPanService.getConnectedDevices();
    274             } catch (RemoteException e) {
    275                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    276                 return new ArrayList<BluetoothDevice>();
    277             }
    278         }
    279         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    280         return new ArrayList<BluetoothDevice>();
    281     }
    282 
    283     /**
    284      * {@inheritDoc}
    285      */
    286     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    287         if (VDBG) log("getDevicesMatchingStates()");
    288         if (mPanService != null && isEnabled()) {
    289             try {
    290                 return mPanService.getDevicesMatchingConnectionStates(states);
    291             } catch (RemoteException e) {
    292                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    293                 return new ArrayList<BluetoothDevice>();
    294             }
    295         }
    296         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    297         return new ArrayList<BluetoothDevice>();
    298     }
    299 
    300     /**
    301      * {@inheritDoc}
    302      */
    303     public int getConnectionState(BluetoothDevice device) {
    304         if (VDBG) log("getState(" + device + ")");
    305         if (mPanService != null && isEnabled()
    306             && isValidDevice(device)) {
    307             try {
    308                 return mPanService.getConnectionState(device);
    309             } catch (RemoteException e) {
    310                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    311                 return BluetoothProfile.STATE_DISCONNECTED;
    312             }
    313         }
    314         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    315         return BluetoothProfile.STATE_DISCONNECTED;
    316     }
    317 
    318     public void setBluetoothTethering(boolean value) {
    319         if (DBG) log("setBluetoothTethering(" + value + ")");
    320         try {
    321             mPanService.setBluetoothTethering(value);
    322         } catch (RemoteException e) {
    323             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    324         }
    325     }
    326 
    327     public boolean isTetheringOn() {
    328         if (VDBG) log("isTetheringOn()");
    329         try {
    330             return mPanService.isTetheringOn();
    331         } catch (RemoteException e) {
    332             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    333         }
    334         return false;
    335     }
    336 
    337     private ServiceConnection mConnection = new ServiceConnection() {
    338         public void onServiceConnected(ComponentName className, IBinder service) {
    339             if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
    340             mPanService = IBluetoothPan.Stub.asInterface(service);
    341 
    342             if (mServiceListener != null) {
    343                 mServiceListener.onServiceConnected(BluetoothProfile.PAN,
    344                                                     BluetoothPan.this);
    345             }
    346         }
    347         public void onServiceDisconnected(ComponentName className) {
    348             if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected");
    349             mPanService = null;
    350             if (mServiceListener != null) {
    351                 mServiceListener.onServiceDisconnected(BluetoothProfile.PAN);
    352             }
    353         }
    354     };
    355 
    356     private boolean isEnabled() {
    357        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
    358        return false;
    359     }
    360 
    361     private boolean isValidDevice(BluetoothDevice device) {
    362        if (device == null) return false;
    363 
    364        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
    365        return false;
    366     }
    367 
    368     private static void log(String msg) {
    369       Log.d(TAG, msg);
    370     }
    371 }
    372