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