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