Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2016 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.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.ServiceConnection;
     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 PBAP Client Profile.
     33  *
     34  * @hide
     35  */
     36 public final class BluetoothPbapClient implements BluetoothProfile {
     37 
     38     private static final String TAG = "BluetoothPbapClient";
     39     private static final boolean DBG = false;
     40     private static final boolean VDBG = false;
     41 
     42     public static final String ACTION_CONNECTION_STATE_CHANGED =
     43             "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
     44 
     45     private volatile IBluetoothPbapClient mService;
     46     private final Context mContext;
     47     private ServiceListener mServiceListener;
     48     private BluetoothAdapter mAdapter;
     49 
     50     /** There was an error trying to obtain the state */
     51     public static final int STATE_ERROR = -1;
     52 
     53     public static final int RESULT_FAILURE = 0;
     54     public static final int RESULT_SUCCESS = 1;
     55     /** Connection canceled before completion. */
     56     public static final int RESULT_CANCELED = 2;
     57 
     58     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
     59             new IBluetoothStateChangeCallback.Stub() {
     60                 public void onBluetoothStateChange(boolean up) {
     61                     if (DBG) {
     62                         Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up);
     63                     }
     64                     if (!up) {
     65                         if (VDBG) {
     66                             Log.d(TAG, "Unbinding service...");
     67                         }
     68                         synchronized (mConnection) {
     69                             try {
     70                                 mService = null;
     71                                 mContext.unbindService(mConnection);
     72                             } catch (Exception re) {
     73                                 Log.e(TAG, "", re);
     74                             }
     75                         }
     76                     } else {
     77                         synchronized (mConnection) {
     78                             try {
     79                                 if (mService == null) {
     80                                     if (VDBG) {
     81                                         Log.d(TAG, "Binding service...");
     82                                     }
     83                                     doBind();
     84                                 }
     85                             } catch (Exception re) {
     86                                 Log.e(TAG, "", re);
     87                             }
     88                         }
     89                     }
     90                 }
     91             };
     92 
     93     /**
     94      * Create a BluetoothPbapClient proxy object.
     95      */
     96     BluetoothPbapClient(Context context, ServiceListener l) {
     97         if (DBG) {
     98             Log.d(TAG, "Create BluetoothPbapClient proxy object");
     99         }
    100         mContext = context;
    101         mServiceListener = l;
    102         mAdapter = BluetoothAdapter.getDefaultAdapter();
    103         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    104         if (mgr != null) {
    105             try {
    106                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
    107             } catch (RemoteException e) {
    108                 Log.e(TAG, "", e);
    109             }
    110         }
    111         doBind();
    112     }
    113 
    114     private boolean doBind() {
    115         Intent intent = new Intent(IBluetoothPbapClient.class.getName());
    116         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
    117         intent.setComponent(comp);
    118         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
    119                 mContext.getUser())) {
    120             Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
    121             return false;
    122         }
    123         return true;
    124     }
    125 
    126     protected void finalize() throws Throwable {
    127         try {
    128             close();
    129         } finally {
    130             super.finalize();
    131         }
    132     }
    133 
    134     /**
    135      * Close the connection to the backing service.
    136      * Other public functions of BluetoothPbapClient will return default error
    137      * results once close() has been called. Multiple invocations of close()
    138      * are ok.
    139      */
    140     public synchronized void close() {
    141         IBluetoothManager mgr = mAdapter.getBluetoothManager();
    142         if (mgr != null) {
    143             try {
    144                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
    145             } catch (Exception e) {
    146                 Log.e(TAG, "", e);
    147             }
    148         }
    149 
    150         synchronized (mConnection) {
    151             if (mService != null) {
    152                 try {
    153                     mService = null;
    154                     mContext.unbindService(mConnection);
    155                 } catch (Exception re) {
    156                     Log.e(TAG, "", re);
    157                 }
    158             }
    159         }
    160         mServiceListener = null;
    161     }
    162 
    163     /**
    164      * Initiate connection.
    165      * Upon successful connection to remote PBAP server the Client will
    166      * attempt to automatically download the users phonebook and call log.
    167      *
    168      * @param device a remote device we want connect to
    169      * @return <code>true</code> if command has been issued successfully; <code>false</code>
    170      * otherwise;
    171      */
    172     public boolean connect(BluetoothDevice device) {
    173         if (DBG) {
    174             log("connect(" + device + ") for PBAP Client.");
    175         }
    176         final IBluetoothPbapClient service = mService;
    177         if (service != null && isEnabled() && isValidDevice(device)) {
    178             try {
    179                 return service.connect(device);
    180             } catch (RemoteException e) {
    181                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    182                 return false;
    183             }
    184         }
    185         if (service == null) {
    186             Log.w(TAG, "Proxy not attached to service");
    187         }
    188         return false;
    189     }
    190 
    191     /**
    192      * Initiate disconnect.
    193      *
    194      * @param device Remote Bluetooth Device
    195      * @return false on error, true otherwise
    196      */
    197     public boolean disconnect(BluetoothDevice device) {
    198         if (DBG) {
    199             log("disconnect(" + device + ")" + new Exception());
    200         }
    201         final IBluetoothPbapClient service = mService;
    202         if (service != null && isEnabled() && isValidDevice(device)) {
    203             try {
    204                 service.disconnect(device);
    205                 return true;
    206             } catch (RemoteException e) {
    207                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    208                 return false;
    209             }
    210         }
    211         if (service == null) {
    212             Log.w(TAG, "Proxy not attached to service");
    213         }
    214         return false;
    215     }
    216 
    217     /**
    218      * Get the list of connected devices.
    219      * Currently at most one.
    220      *
    221      * @return list of connected devices
    222      */
    223     @Override
    224     public List<BluetoothDevice> getConnectedDevices() {
    225         if (DBG) {
    226             log("getConnectedDevices()");
    227         }
    228         final IBluetoothPbapClient service = mService;
    229         if (service != null && isEnabled()) {
    230             try {
    231                 return service.getConnectedDevices();
    232             } catch (RemoteException e) {
    233                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    234                 return new ArrayList<BluetoothDevice>();
    235             }
    236         }
    237         if (service == null) {
    238             Log.w(TAG, "Proxy not attached to service");
    239         }
    240         return new ArrayList<BluetoothDevice>();
    241     }
    242 
    243     /**
    244      * Get the list of devices matching specified states. Currently at most one.
    245      *
    246      * @return list of matching devices
    247      */
    248     @Override
    249     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    250         if (DBG) {
    251             log("getDevicesMatchingStates()");
    252         }
    253         final IBluetoothPbapClient service = mService;
    254         if (service != null && isEnabled()) {
    255             try {
    256                 return service.getDevicesMatchingConnectionStates(states);
    257             } catch (RemoteException e) {
    258                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    259                 return new ArrayList<BluetoothDevice>();
    260             }
    261         }
    262         if (service == null) {
    263             Log.w(TAG, "Proxy not attached to service");
    264         }
    265         return new ArrayList<BluetoothDevice>();
    266     }
    267 
    268     /**
    269      * Get connection state of device
    270      *
    271      * @return device connection state
    272      */
    273     @Override
    274     public int getConnectionState(BluetoothDevice device) {
    275         if (DBG) {
    276             log("getConnectionState(" + device + ")");
    277         }
    278         final IBluetoothPbapClient service = mService;
    279         if (service != null && isEnabled() && isValidDevice(device)) {
    280             try {
    281                 return service.getConnectionState(device);
    282             } catch (RemoteException e) {
    283                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    284                 return BluetoothProfile.STATE_DISCONNECTED;
    285             }
    286         }
    287         if (service == null) {
    288             Log.w(TAG, "Proxy not attached to service");
    289         }
    290         return BluetoothProfile.STATE_DISCONNECTED;
    291     }
    292 
    293     private final ServiceConnection mConnection = new ServiceConnection() {
    294         public void onServiceConnected(ComponentName className, IBinder service) {
    295             if (DBG) {
    296                 log("Proxy object connected");
    297             }
    298             mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
    299             if (mServiceListener != null) {
    300                 mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT,
    301                         BluetoothPbapClient.this);
    302             }
    303         }
    304 
    305         public void onServiceDisconnected(ComponentName className) {
    306             if (DBG) {
    307                 log("Proxy object disconnected");
    308             }
    309             mService = null;
    310             if (mServiceListener != null) {
    311                 mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT);
    312             }
    313         }
    314     };
    315 
    316     private static void log(String msg) {
    317         Log.d(TAG, msg);
    318     }
    319 
    320     private boolean isEnabled() {
    321         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    322         if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) {
    323             return true;
    324         }
    325         log("Bluetooth is Not enabled");
    326         return false;
    327     }
    328 
    329     private static boolean isValidDevice(BluetoothDevice device) {
    330         return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
    331     }
    332 
    333     /**
    334      * Set priority of the profile
    335      *
    336      * <p> The device should already be paired.
    337      * Priority can be one of {@link #PRIORITY_ON} or
    338      * {@link #PRIORITY_OFF},
    339      *
    340      * @param device Paired bluetooth device
    341      * @param priority Priority of this profile
    342      * @return true if priority is set, false on error
    343      */
    344     public boolean setPriority(BluetoothDevice device, int priority) {
    345         if (DBG) {
    346             log("setPriority(" + device + ", " + priority + ")");
    347         }
    348         final IBluetoothPbapClient service = mService;
    349         if (service != null && isEnabled() && isValidDevice(device)) {
    350             if (priority != BluetoothProfile.PRIORITY_OFF
    351                     && priority != BluetoothProfile.PRIORITY_ON) {
    352                 return false;
    353             }
    354             try {
    355                 return service.setPriority(device, priority);
    356             } catch (RemoteException e) {
    357                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    358                 return false;
    359             }
    360         }
    361         if (service == null) {
    362             Log.w(TAG, "Proxy not attached to service");
    363         }
    364         return false;
    365     }
    366 
    367     /**
    368      * Get the priority of the profile.
    369      *
    370      * <p> The priority can be any of:
    371      * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
    372      * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
    373      *
    374      * @param device Bluetooth device
    375      * @return priority of the device
    376      */
    377     public int getPriority(BluetoothDevice device) {
    378         if (VDBG) {
    379             log("getPriority(" + device + ")");
    380         }
    381         final IBluetoothPbapClient service = mService;
    382         if (service != null && isEnabled() && isValidDevice(device)) {
    383             try {
    384                 return service.getPriority(device);
    385             } catch (RemoteException e) {
    386                 Log.e(TAG, Log.getStackTraceString(new Throwable()));
    387                 return PRIORITY_OFF;
    388             }
    389         }
    390         if (service == null) {
    391             Log.w(TAG, "Proxy not attached to service");
    392         }
    393         return PRIORITY_OFF;
    394     }
    395 }
    396