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.util.Log;
     28 
     29 import java.util.ArrayList;
     30 import java.util.List;
     31 
     32 /**
     33  * This class provides the APIs to control the Bluetooth Pan
     34  * Profile.
     35  *
     36  *<p>BluetoothPan is a proxy object for controlling the Bluetooth
     37  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
     38  * the BluetoothPan proxy object.
     39  *
     40  *<p>Each method is protected with its appropriate permission.
     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      * @hide
     97      */
     98     public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000;
     99 
    100     /**
    101      * @hide
    102      */
    103     public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001;
    104 
    105     /**
    106      * @hide
    107      */
    108     public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002;
    109 
    110     /**
    111      * @hide
    112      */
    113     public static final int PAN_OPERATION_GENERIC_FAILURE = 1003;
    114 
    115     /**
    116      * @hide
    117      */
    118     public static final int PAN_OPERATION_SUCCESS = 1004;
    119 
    120     private Context mContext;
    121     private ServiceListener mServiceListener;
    122     private BluetoothAdapter mAdapter;
    123     private IBluetoothPan mPanService;
    124 
    125     /**
    126      * Create a BluetoothPan proxy object for interacting with the local
    127      * Bluetooth Service which handles the Pan profile
    128      *
    129      */
    130     /*package*/ BluetoothPan(Context context, ServiceListener l) {
    131         mContext = context;
    132         mServiceListener = l;
    133         mAdapter = BluetoothAdapter.getDefaultAdapter();
    134         try {
    135             mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback);
    136         } catch (RemoteException re) {
    137             Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
    138         }
    139         if (VDBG) Log.d(TAG, "BluetoothPan() call bindService");
    140         doBind();
    141         if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called");
    142     }
    143 
    144     boolean doBind() {
    145         Intent intent = new Intent(IBluetoothPan.class.getName());
    146         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    147         intent.setComponent(comp);
    148         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
    149                 android.os.Process.myUserHandle())) {
    150             Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
    151             return false;
    152         }
    153         return true;
    154     }
    155 
    156     /*package*/ void close() {
    157         if (VDBG) log("close()");
    158 
    159         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    160         if (mgr != null) {
    161             try {
    162                 mgr.unregisterStateChangeCallback(mStateChangeCallback);
    163             } catch (RemoteException re) {
    164                 Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re);
    165             }
    166         }
    167 
    168         synchronized (mConnection) {
    169             if (mPanService != null) {
    170                 try {
    171                     mPanService = null;
    172                     mContext.unbindService(mConnection);
    173                 } catch (Exception re) {
    174                     Log.e(TAG,"",re);
    175                 }
    176             }
    177         }
    178         mServiceListener = null;
    179     }
    180 
    181     protected void finalize() {
    182         close();
    183     }
    184 
    185     final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() {
    186 
    187         @Override
    188         public void onBluetoothStateChange(boolean on) throws RemoteException {
    189             //Handle enable request to bind again.
    190             if (on) {
    191                 Log.d(TAG, "onBluetoothStateChange(on) call bindService");
    192                 doBind();
    193                 if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called");
    194             } else {
    195                 if (VDBG) Log.d(TAG,"Unbinding service...");
    196                 synchronized (mConnection) {
    197                     try {
    198                         mPanService = null;
    199                         mContext.unbindService(mConnection);
    200                     } catch (Exception re) {
    201                         Log.e(TAG,"",re);
    202                     }
    203                 }
    204             }
    205         }
    206     };
    207 
    208     /**
    209      * Initiate connection to a profile of the remote bluetooth device.
    210      *
    211      * <p> This API returns false in scenarios like the profile on the
    212      * device is already connected or Bluetooth is not turned on.
    213      * When this API returns true, it is guaranteed that
    214      * connection state intent for the profile will be broadcasted with
    215      * the state. Users can get the connection state of the profile
    216      * from this intent.
    217      *
    218      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    219      * permission.
    220      *
    221      * @param device Remote Bluetooth Device
    222      * @return false on immediate error,
    223      *               true otherwise
    224      * @hide
    225      */
    226     public boolean connect(BluetoothDevice device) {
    227         if (DBG) log("connect(" + device + ")");
    228         if (mPanService != null && isEnabled() &&
    229             isValidDevice(device)) {
    230             try {
    231                 return mPanService.connect(device);
    232             } catch (RemoteException e) {
    233                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    234                 return false;
    235             }
    236         }
    237         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    238         return false;
    239     }
    240 
    241     /**
    242      * Initiate disconnection from a profile
    243      *
    244      * <p> This API will return false in scenarios like the profile on the
    245      * Bluetooth device is not in connected state etc. When this API returns,
    246      * true, it is guaranteed that the connection state change
    247      * intent will be broadcasted with the state. Users can get the
    248      * disconnection state of the profile from this intent.
    249      *
    250      * <p> If the disconnection is initiated by a remote device, the state
    251      * will transition from {@link #STATE_CONNECTED} to
    252      * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
    253      * host (local) device the state will transition from
    254      * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
    255      * state {@link #STATE_DISCONNECTED}. The transition to
    256      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
    257      * two scenarios.
    258      *
    259      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    260      * permission.
    261      *
    262      * @param device Remote Bluetooth Device
    263      * @return false on immediate error,
    264      *               true otherwise
    265      * @hide
    266      */
    267     public boolean disconnect(BluetoothDevice device) {
    268         if (DBG) log("disconnect(" + device + ")");
    269         if (mPanService != null && isEnabled() &&
    270             isValidDevice(device)) {
    271             try {
    272                 return mPanService.disconnect(device);
    273             } catch (RemoteException e) {
    274                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    275                 return false;
    276             }
    277         }
    278         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    279         return false;
    280     }
    281 
    282     /**
    283      * {@inheritDoc}
    284      */
    285     public List<BluetoothDevice> getConnectedDevices() {
    286         if (VDBG) log("getConnectedDevices()");
    287         if (mPanService != null && isEnabled()) {
    288             try {
    289                 return mPanService.getConnectedDevices();
    290             } catch (RemoteException e) {
    291                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    292                 return new ArrayList<BluetoothDevice>();
    293             }
    294         }
    295         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    296         return new ArrayList<BluetoothDevice>();
    297     }
    298 
    299     /**
    300      * {@inheritDoc}
    301      */
    302     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    303         if (VDBG) log("getDevicesMatchingStates()");
    304         if (mPanService != null && isEnabled()) {
    305             try {
    306                 return mPanService.getDevicesMatchingConnectionStates(states);
    307             } catch (RemoteException e) {
    308                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    309                 return new ArrayList<BluetoothDevice>();
    310             }
    311         }
    312         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    313         return new ArrayList<BluetoothDevice>();
    314     }
    315 
    316     /**
    317      * {@inheritDoc}
    318      */
    319     public int getConnectionState(BluetoothDevice device) {
    320         if (VDBG) log("getState(" + device + ")");
    321         if (mPanService != null && isEnabled()
    322             && isValidDevice(device)) {
    323             try {
    324                 return mPanService.getConnectionState(device);
    325             } catch (RemoteException e) {
    326                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    327                 return BluetoothProfile.STATE_DISCONNECTED;
    328             }
    329         }
    330         if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
    331         return BluetoothProfile.STATE_DISCONNECTED;
    332     }
    333 
    334     public void setBluetoothTethering(boolean value) {
    335         if (DBG) log("setBluetoothTethering(" + value + ")");
    336         try {
    337             mPanService.setBluetoothTethering(value);
    338         } catch (RemoteException e) {
    339             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    340         }
    341     }
    342 
    343     public boolean isTetheringOn() {
    344         if (VDBG) log("isTetheringOn()");
    345         try {
    346             return mPanService.isTetheringOn();
    347         } catch (RemoteException e) {
    348             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
    349         }
    350         return false;
    351     }
    352 
    353     private final ServiceConnection mConnection = new ServiceConnection() {
    354         public void onServiceConnected(ComponentName className, IBinder service) {
    355             if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
    356             mPanService = IBluetoothPan.Stub.asInterface(service);
    357 
    358             if (mServiceListener != null) {
    359                 mServiceListener.onServiceConnected(BluetoothProfile.PAN,
    360                                                     BluetoothPan.this);
    361             }
    362         }
    363         public void onServiceDisconnected(ComponentName className) {
    364             if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected");
    365             mPanService = null;
    366             if (mServiceListener != null) {
    367                 mServiceListener.onServiceDisconnected(BluetoothProfile.PAN);
    368             }
    369         }
    370     };
    371 
    372     private boolean isEnabled() {
    373        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
    374        return false;
    375     }
    376 
    377     private boolean isValidDevice(BluetoothDevice device) {
    378        if (device == null) return false;
    379 
    380        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
    381        return false;
    382     }
    383 
    384     private static void log(String msg) {
    385       Log.d(TAG, msg);
    386     }
    387 }
    388