blob: 8af451c7a878542298dc42486dc14bcad18c56fb [file] [log] [blame]
/*
* Copyright (c) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bluetooth.pbapclient;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothPbapClient;
import android.bluetooth.IBluetoothHeadsetClient;
import android.content.BroadcastReceiver;
import android.content.ContentProviderOperation;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.OperationApplicationException;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
import android.provider.ContactsContract;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.Utils;
import com.android.vcard.VCardEntry;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
/**
* Provides Bluetooth Phone Book Access Profile Client profile.
*
* @hide
*/
public class PbapClientService extends ProfileService {
private static final boolean DBG = false;
private static final String TAG = "PbapClientService";
private PbapPCEClient mClient;
private HandlerThread mHandlerThread;
private AccountManager mAccountManager;
private static PbapClientService sPbapClientService;
private PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver();
@Override
protected String getName() {
return TAG;
}
@Override
public IProfileServiceBinder initBinder() {
return new BluetoothPbapClientBinder(this);
}
@Override
protected boolean start() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
try {
registerReceiver(mPbapBroadcastReceiver, filter);
} catch (Exception e) {
Log.w(TAG,"Unable to register pbapclient receiver",e);
}
mClient = new PbapPCEClient(this);
mAccountManager = AccountManager.get(this);
setPbapClientService(this);
mClient.start();
return true;
}
@Override
protected boolean stop() {
try {
unregisterReceiver(mPbapBroadcastReceiver);
} catch (Exception e) {
Log.w(TAG,"Unable to unregister sap receiver",e);
}
mClient.disconnect(null);
return true;
}
@Override
protected boolean cleanup() {
clearPbapClientService();
return true;
}
private class PbapBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.v(TAG, "onReceive");
String action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(getPriority(device) >= BluetoothProfile.PRIORITY_ON) {
connect(device);
}
} else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
disconnect(device);
}
}
}
/**
* Handler for incoming service calls
*/
private static class BluetoothPbapClientBinder extends IBluetoothPbapClient.Stub
implements IProfileServiceBinder {
private PbapClientService mService;
public BluetoothPbapClientBinder(PbapClientService svc) {
mService = svc;
}
@Override
public boolean cleanup() {
mService = null;
return true;
}
private PbapClientService getService() {
if (!Utils.checkCaller()) {
Log.w(TAG, "PbapClient call not allowed for non-active user");
return null;
}
if (mService != null && mService.isAvailable()) {
return mService;
}
return null;
}
@Override
public boolean connect(BluetoothDevice device) {
PbapClientService service = getService();
if (service == null) {
return false;
}
return service.connect(device);
}
@Override
public boolean disconnect(BluetoothDevice device) {
PbapClientService service = getService();
if (service == null) {
return false;
}
return service.disconnect(device);
}
@Override
public List<BluetoothDevice> getConnectedDevices() {
PbapClientService service = getService();
if (service == null) {
return new ArrayList<BluetoothDevice>(0);
}
return service.getConnectedDevices();
}
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
PbapClientService service = getService();
if (service == null) {
return new ArrayList<BluetoothDevice>(0);
}
return service.getDevicesMatchingConnectionStates(states);
}
@Override
public int getConnectionState(BluetoothDevice device) {
PbapClientService service = getService();
if (service == null) {
return BluetoothProfile.STATE_DISCONNECTED;
}
return service.getConnectionState(device);
}
public boolean setPriority(BluetoothDevice device, int priority) {
PbapClientService service = getService();
if (service == null) {
return false;
}
return service.setPriority(device, priority);
}
public int getPriority(BluetoothDevice device) {
PbapClientService service = getService();
if (service == null) {
return BluetoothProfile.PRIORITY_UNDEFINED;
}
return service.getPriority(device);
}
}
// API methods
public static synchronized PbapClientService getPbapClientService() {
if (sPbapClientService != null && sPbapClientService.isAvailable()) {
if (DBG) {
Log.d(TAG, "getPbapClientService(): returning " + sPbapClientService);
}
return sPbapClientService;
}
if (DBG) {
if (sPbapClientService == null) {
Log.d(TAG, "getPbapClientService(): service is NULL");
} else if (!(sPbapClientService.isAvailable())) {
Log.d(TAG, "getPbapClientService(): service is not available");
}
}
return null;
}
private static synchronized void setPbapClientService(PbapClientService instance) {
if (instance != null && instance.isAvailable()) {
if (DBG) {
Log.d(TAG, "setPbapClientService(): set to: " + sPbapClientService);
}
sPbapClientService = instance;
} else {
if (DBG) {
if (sPbapClientService == null) {
Log.d(TAG, "setPbapClientService(): service not available");
} else if (!sPbapClientService.isAvailable()) {
Log.d(TAG, "setPbapClientService(): service is cleaning up");
}
}
}
}
private static synchronized void clearPbapClientService() {
sPbapClientService = null;
}
public boolean connect(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
Log.d(TAG,"Received request to ConnectPBAPPhonebook " + device.getAddress());
int connectionState = mClient.getConnectionState();
if (connectionState == BluetoothProfile.STATE_CONNECTED ||
connectionState == BluetoothProfile.STATE_CONNECTING) {
return false;
}
if (getPriority(device)>BluetoothProfile.PRIORITY_OFF) {
mClient.connect(device);
return true;
}
return false;
}
boolean disconnect(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
mClient.disconnect(device);
return true;
}
public List<BluetoothDevice> getConnectedDevices() {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
int[] desiredStates = {BluetoothProfile.STATE_CONNECTED};
return getDevicesMatchingConnectionStates(desiredStates);
}
private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
int clientState = mClient.getConnectionState();
Log.d(TAG,"getDevicesMatchingConnectionStates " + Arrays.toString(states) + " == " + clientState);
List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
for (int state : states) {
if (clientState == state) {
BluetoothDevice currentDevice = mClient.getDevice();
if (currentDevice != null) {
deviceList.add(currentDevice);
}
}
}
return deviceList;
}
int getConnectionState(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (device == mClient.getDevice()) {
return mClient.getConnectionState();
}
return BluetoothProfile.STATE_DISCONNECTED;
}
public boolean setPriority(BluetoothDevice device, int priority) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
Settings.Global.putInt(getContentResolver(),
Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
priority);
if (DBG) {
Log.d(TAG,"Saved priority " + device + " = " + priority);
}
return true;
}
public int getPriority(BluetoothDevice device) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
int priority = Settings.Global.getInt(getContentResolver(),
Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
return priority;
}
}