blob: ce6aee27d4f4a8b598ff0afb02d5b8f2f7152acd [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.mapclient;
import android.Manifest;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothMapClient;
import android.net.Uri;
import android.provider.Settings;
import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class MapClientService extends ProfileService {
private static final String TAG = "MapClientService";
static final boolean DBG = false;
static final boolean VDBG = false;
private static final int MAXIMUM_CONNECTED_DEVICES = 1;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
MceStateMachine mMceStateMachine;
private MnsService mMnsServer;
private BluetoothAdapter mAdapter;
private static MapClientService sMapClientService;
public static synchronized MapClientService getMapClientService() {
if (sMapClientService != null && sMapClientService.isAvailable()) {
if (DBG) Log.d(TAG, "getMapClientService(): returning " + sMapClientService);
return sMapClientService;
}
if (DBG) {
if (sMapClientService == null) {
Log.d(TAG, "getMapClientService(): service is NULL");
} else if (!(sMapClientService.isAvailable())) {
Log.d(TAG, "getMapClientService(): service is not available");
}
}
return null;
}
private static synchronized void setService(MapClientService instance) {
if (instance != null && instance.isAvailable()) {
if (DBG) Log.d(TAG, "setMapMceService(): replacing old instance: " + sMapClientService);
sMapClientService = instance;
} else {
if (DBG) {
if (sMapClientService == null) {
Log.d(TAG, "setA2dpService(): service not available");
} else if (!sMapClientService.isAvailable()) {
Log.d(TAG, "setA2dpService(): service is cleaning up");
}
}
}
}
public synchronized boolean connect(BluetoothDevice device) {
Log.d(TAG, "MAP Mce connect " + device.toString());
return mMceStateMachine.connect(device);
}
public synchronized boolean disconnect(BluetoothDevice device) {
Log.d(TAG, "MAP Mce disconnect " + device.toString());
return mMceStateMachine.disconnect(device);
}
public List<BluetoothDevice> getConnectedDevices() {
return getDevicesMatchingConnectionStates(new int[]{BluetoothAdapter.STATE_CONNECTED});
}
public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states));
List<BluetoothDevice> deviceList = new ArrayList<>();
Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
int connectionState;
for (BluetoothDevice device : bondedDevices) {
connectionState = getConnectionState(device);
Log.d(TAG, "Device: " + device + "State: " + connectionState);
for (int i = 0; i < states.length; i++) {
if (connectionState == states[i]) {
deviceList.add(device);
}
}
}
Log.d(TAG, deviceList.toString());
return deviceList;
}
public synchronized int getConnectionState(BluetoothDevice device) {
if (mMceStateMachine != null && device.equals(mMceStateMachine.getDevice())) {
return mMceStateMachine.getState();
} else {
return BluetoothProfile.STATE_DISCONNECTED;
}
}
public boolean setPriority(BluetoothDevice device, int priority) {
Settings.Global.putInt(getContentResolver(),
Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()),
priority);
if (VDBG) Log.v(TAG, "Saved priority " + device + " = " + priority);
return true;
}
public int getPriority(BluetoothDevice device) {
int priority = Settings.Global.getInt(getContentResolver(),
Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
return priority;
}
public synchronized boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (mMceStateMachine != null && device.equals(mMceStateMachine.getDevice())) {
return mMceStateMachine.sendMapMessage(contacts, message, sentIntent, deliveredIntent);
} else {
return false;
}
}
@Override
protected IProfileServiceBinder initBinder() {
return new Binder(this);
}
@Override
protected boolean start() {
if (DBG) Log.d(TAG, "start()");
setService(this);
if (mMnsServer == null) {
mMnsServer = new MnsService(this);
}
if (mMceStateMachine == null) {
mMceStateMachine = new MceStateMachine(this);
}
mAdapter = BluetoothAdapter.getDefaultAdapter();
mStartError = false;
return !mStartError;
}
@Override
protected synchronized boolean stop() {
if (DBG) Log.d(TAG, "stop()");
if (mMnsServer != null) {
mMnsServer.stop();
}
if (mMceStateMachine.getState() == BluetoothAdapter.STATE_CONNECTED) {
mMceStateMachine.disconnect(mMceStateMachine.getDevice());
}
mMceStateMachine.doQuit();
return true;
}
public boolean cleanup() {
if (DBG) Log.d(TAG, "cleanup()");
return true;
}
public synchronized boolean getUnreadMessages(BluetoothDevice device) {
if (mMceStateMachine != null && device.equals(mMceStateMachine.getDevice())) {
return mMceStateMachine.getUnreadMessages();
} else {
return false;
}
}
@Override
public synchronized void dump(StringBuilder sb) {
super.dump(sb);
println(sb, "StateMachine: " + mMceStateMachine.toString());
}
//Binder object: Must be static class or memory leak may occur
/**
* This class implements the IClient interface - or actually it validates the
* preconditions for calling the actual functionality in the MapClientService, and calls it.
*/
private static class Binder extends IBluetoothMapClient.Stub
implements IProfileServiceBinder {
private MapClientService mService;
Binder(MapClientService service) {
if (VDBG) Log.v(TAG, "Binder()");
mService = service;
}
private MapClientService getService() {
if (!Utils.checkCaller()) {
Log.w(TAG, "MAP call not allowed for non-active user");
return null;
}
if (mService != null && mService.isAvailable()) {
mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
return mService;
}
return null;
}
public boolean cleanup() {
mService = null;
return true;
}
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.v(TAG, "isConnected()");
MapClientService service = getService();
if (service == null) return false;
return service.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED;
}
public boolean connect(BluetoothDevice device) {
if (VDBG) Log.v(TAG, "connect()");
MapClientService service = getService();
if (service == null) return false;
return service.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
if (VDBG) Log.v(TAG, "disconnect()");
MapClientService service = getService();
if (service == null) return false;
return service.disconnect(device);
}
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) Log.v(TAG, "getConnectedDevices()");
MapClientService service = getService();
if (service == null) return new ArrayList<BluetoothDevice>(0);
return service.getConnectedDevices();
}
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) Log.v(TAG, "getDevicesMatchingConnectionStates()");
MapClientService service = getService();
if (service == null) return new ArrayList<BluetoothDevice>(0);
return service.getDevicesMatchingConnectionStates(states);
}
public int getConnectionState(BluetoothDevice device) {
if (VDBG) Log.v(TAG, "getConnectionState()");
MapClientService service = getService();
if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
return service.getConnectionState(device);
}
public boolean setPriority(BluetoothDevice device, int priority) {
MapClientService service = getService();
if (service == null) return false;
return service.setPriority(device, priority);
}
public int getPriority(BluetoothDevice device) {
MapClientService service = getService();
if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
return service.getPriority(device);
}
public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent) {
MapClientService service = getService();
if (service == null) return false;
Log.d(TAG, "Checking Permission of sendMessage");
mService.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS,
"Need SEND_SMS permission");
return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
}
public boolean getUnreadMessages(BluetoothDevice device) {
MapClientService service = getService();
if (service == null) return false;
mService.enforceCallingOrSelfPermission(Manifest.permission.READ_SMS,
"Need READ_SMS permission");
return service.getUnreadMessages(device);
}
}
}