Home | History | Annotate | Download | only in pbapclient
      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 com.android.bluetooth.pbapclient;
     18 
     19 import android.accounts.Account;
     20 import android.accounts.AccountManager;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.bluetooth.BluetoothProfile;
     23 import android.bluetooth.IBluetoothPbapClient;
     24 import android.bluetooth.IBluetoothHeadsetClient;
     25 import android.content.BroadcastReceiver;
     26 import android.content.ContentProviderOperation;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.OperationApplicationException;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.HandlerThread;
     34 import android.os.Message;
     35 import android.os.RemoteException;
     36 import android.provider.Settings;
     37 import android.util.Log;
     38 import android.provider.ContactsContract;
     39 
     40 import com.android.bluetooth.btservice.ProfileService;
     41 import com.android.bluetooth.Utils;
     42 import com.android.vcard.VCardEntry;
     43 
     44 
     45 import java.lang.ref.WeakReference;
     46 import java.util.Arrays;
     47 import java.util.ArrayList;
     48 import java.util.List;
     49 import java.util.HashMap;
     50 
     51 /**
     52  * Provides Bluetooth Phone Book Access Profile Client profile.
     53  *
     54  * @hide
     55  */
     56 public class PbapClientService extends ProfileService {
     57     private static final boolean DBG = false;
     58     private static final String TAG = "PbapClientService";
     59     private PbapPCEClient mClient;
     60     private HandlerThread mHandlerThread;
     61     private AccountManager mAccountManager;
     62     private static PbapClientService sPbapClientService;
     63     private PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver();
     64 
     65     @Override
     66     protected String getName() {
     67         return TAG;
     68     }
     69 
     70     @Override
     71     public IProfileServiceBinder initBinder() {
     72         return new BluetoothPbapClientBinder(this);
     73     }
     74 
     75     @Override
     76     protected boolean start() {
     77         IntentFilter filter = new IntentFilter();
     78         filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
     79         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
     80         try {
     81             registerReceiver(mPbapBroadcastReceiver, filter);
     82         } catch (Exception e) {
     83             Log.w(TAG,"Unable to register pbapclient receiver",e);
     84         }
     85         mClient = new PbapPCEClient(this);
     86         mAccountManager = AccountManager.get(this);
     87         setPbapClientService(this);
     88         mClient.start();
     89         return true;
     90     }
     91 
     92     @Override
     93     protected boolean stop() {
     94         try {
     95             unregisterReceiver(mPbapBroadcastReceiver);
     96         } catch (Exception e) {
     97             Log.w(TAG,"Unable to unregister sap receiver",e);
     98         }
     99         mClient.disconnect(null);
    100         return true;
    101     }
    102 
    103     @Override
    104     protected boolean cleanup() {
    105         clearPbapClientService();
    106         return true;
    107     }
    108 
    109     private class PbapBroadcastReceiver extends BroadcastReceiver {
    110         @Override
    111         public void onReceive(Context context, Intent intent) {
    112             Log.v(TAG, "onReceive");
    113             String action = intent.getAction();
    114             if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
    115                   BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    116                   if(getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
    117                       connect(device);
    118                   }
    119             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
    120                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    121                 disconnect(device);
    122             }
    123         }
    124     }
    125 
    126     /**
    127      * Handler for incoming service calls
    128      */
    129     private static class BluetoothPbapClientBinder extends IBluetoothPbapClient.Stub
    130             implements IProfileServiceBinder {
    131         private PbapClientService mService;
    132 
    133         public BluetoothPbapClientBinder(PbapClientService svc) {
    134             mService = svc;
    135         }
    136 
    137         @Override
    138         public boolean cleanup() {
    139             mService = null;
    140             return true;
    141         }
    142 
    143         private PbapClientService getService() {
    144             if (!Utils.checkCaller()) {
    145                 Log.w(TAG, "PbapClient call not allowed for non-active user");
    146                 return null;
    147             }
    148 
    149             if (mService != null && mService.isAvailable()) {
    150                 return mService;
    151             }
    152             return null;
    153         }
    154 
    155         @Override
    156         public boolean connect(BluetoothDevice device) {
    157             PbapClientService service = getService();
    158             if (service == null) {
    159                 return false;
    160             }
    161             return service.connect(device);
    162         }
    163 
    164         @Override
    165         public boolean disconnect(BluetoothDevice device) {
    166             PbapClientService service = getService();
    167             if (service == null) {
    168                 return false;
    169             }
    170             return service.disconnect(device);
    171         }
    172 
    173         @Override
    174         public List<BluetoothDevice> getConnectedDevices() {
    175             PbapClientService service = getService();
    176             if (service == null) {
    177                 return new ArrayList<BluetoothDevice>(0);
    178             }
    179             return service.getConnectedDevices();
    180         }
    181         @Override
    182         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    183             PbapClientService service = getService();
    184             if (service == null) {
    185                 return new ArrayList<BluetoothDevice>(0);
    186             }
    187             return service.getDevicesMatchingConnectionStates(states);
    188         }
    189 
    190         @Override
    191         public int getConnectionState(BluetoothDevice device) {
    192             PbapClientService service = getService();
    193             if (service == null) {
    194                 return BluetoothProfile.STATE_DISCONNECTED;
    195             }
    196             return service.getConnectionState(device);
    197         }
    198 
    199         public boolean setPriority(BluetoothDevice device, int priority) {
    200             PbapClientService service = getService();
    201             if (service == null) {
    202                 return false;
    203             }
    204             return service.setPriority(device, priority);
    205         }
    206 
    207         public int getPriority(BluetoothDevice device) {
    208             PbapClientService service = getService();
    209             if (service == null) {
    210                 return BluetoothProfile.PRIORITY_UNDEFINED;
    211             }
    212             return service.getPriority(device);
    213         }
    214 
    215 
    216     }
    217 
    218 
    219     // API methods
    220     public static synchronized PbapClientService getPbapClientService() {
    221         if (sPbapClientService != null && sPbapClientService.isAvailable()) {
    222             if (DBG) {
    223                 Log.d(TAG, "getPbapClientService(): returning " + sPbapClientService);
    224             }
    225             return sPbapClientService;
    226         }
    227         if (DBG) {
    228             if (sPbapClientService == null) {
    229                 Log.d(TAG, "getPbapClientService(): service is NULL");
    230             } else if (!(sPbapClientService.isAvailable())) {
    231                 Log.d(TAG, "getPbapClientService(): service is not available");
    232             }
    233         }
    234         return null;
    235     }
    236 
    237     private static synchronized void setPbapClientService(PbapClientService instance) {
    238         if (instance != null && instance.isAvailable()) {
    239             if (DBG) {
    240                 Log.d(TAG, "setPbapClientService(): set to: " + sPbapClientService);
    241             }
    242             sPbapClientService = instance;
    243         } else {
    244             if (DBG) {
    245                 if (sPbapClientService == null) {
    246                     Log.d(TAG, "setPbapClientService(): service not available");
    247                 } else if (!sPbapClientService.isAvailable()) {
    248                     Log.d(TAG, "setPbapClientService(): service is cleaning up");
    249                 }
    250             }
    251         }
    252     }
    253 
    254     private static synchronized void clearPbapClientService() {
    255         sPbapClientService = null;
    256     }
    257 
    258     public boolean connect(BluetoothDevice device) {
    259         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    260                 "Need BLUETOOTH ADMIN permission");
    261         Log.d(TAG,"Received request to ConnectPBAPPhonebook " + device.getAddress());
    262         int connectionState = mClient.getConnectionState();
    263         if (connectionState == BluetoothProfile.STATE_CONNECTED ||
    264                 connectionState == BluetoothProfile.STATE_CONNECTING) {
    265             return false;
    266         }
    267         if (getPriority(device)>BluetoothProfile.PRIORITY_OFF) {
    268             mClient.connect(device);
    269             return true;
    270         }
    271         return false;
    272     }
    273 
    274     boolean disconnect(BluetoothDevice device) {
    275         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    276                 "Need BLUETOOTH ADMIN permission");
    277         mClient.disconnect(device);
    278         return true;
    279     }
    280     public List<BluetoothDevice> getConnectedDevices() {
    281         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    282         int[] desiredStates = {BluetoothProfile.STATE_CONNECTED};
    283         return getDevicesMatchingConnectionStates(desiredStates);
    284     }
    285 
    286     private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    287         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    288         int clientState = mClient.getConnectionState();
    289         Log.d(TAG,"getDevicesMatchingConnectionStates " + Arrays.toString(states) + " == " + clientState);
    290         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
    291         for (int state : states) {
    292             if (clientState == state) {
    293                 BluetoothDevice currentDevice = mClient.getDevice();
    294                 if (currentDevice != null) {
    295                     deviceList.add(currentDevice);
    296                 }
    297             }
    298         }
    299         return deviceList;
    300     }
    301 
    302     int getConnectionState(BluetoothDevice device) {
    303         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    304         if (device == mClient.getDevice()) {
    305             return mClient.getConnectionState();
    306         }
    307         return BluetoothProfile.STATE_DISCONNECTED;
    308     }
    309 
    310     public boolean setPriority(BluetoothDevice device, int priority) {
    311         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    312                 "Need BLUETOOTH_ADMIN permission");
    313         Settings.Global.putInt(getContentResolver(),
    314                 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
    315                 priority);
    316         if (DBG) {
    317             Log.d(TAG,"Saved priority " + device + " = " + priority);
    318         }
    319         return true;
    320     }
    321 
    322     public int getPriority(BluetoothDevice device) {
    323         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    324                 "Need BLUETOOTH_ADMIN permission");
    325         int priority = Settings.Global.getInt(getContentResolver(),
    326                 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
    327                 BluetoothProfile.PRIORITY_UNDEFINED);
    328         return priority;
    329     }
    330 
    331 }
    332