blob: 1da7da7858c7676e9691c59b0455c406440b675f [file] [log] [blame]
/*
* Copyright 2021 HIMSA II K/S - www.himsa.com.
* Represented by EHIMA - www.ehima.com
*
* 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.mcp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.IBluetoothMcpServiceManager;
import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import java.util.HashMap;
import java.util.Map;
/**
* Provides Media Control Profile, as a service in the Bluetooth application.
* @hide
*/
public class McpService extends ProfileService {
private static final boolean DBG = true;
private static final boolean VDBG = false;
private static final String TAG = "BluetoothMcpService";
private static McpService sMcpService;
private static MediaControlProfile mGmcs;
private Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>();
private Handler mHandler = new Handler(Looper.getMainLooper());
private static synchronized void setMcpService(McpService instance) {
if (VDBG) {
Log.d(TAG, "setMcpService(): set to: " + instance);
}
sMcpService = instance;
}
public static synchronized McpService getMcpService() {
if (sMcpService == null) {
Log.w(TAG, "getMcpService(): service is NULL");
return null;
}
if (!sMcpService.isAvailable()) {
Log.w(TAG, "getMcpService(): service is not available");
return null;
}
return sMcpService;
}
public static void setMediaControlProfileForTesting(MediaControlProfile mediaControlProfile) {
mGmcs = mediaControlProfile;
}
@Override
protected IProfileServiceBinder initBinder() {
return new BluetoothMcpServiceBinder(this);
}
@Override
protected void create() {
if (DBG) {
Log.d(TAG, "create()");
}
}
@Override
protected boolean start() {
if (DBG) {
Log.d(TAG, "start()");
}
if (sMcpService != null) {
throw new IllegalStateException("start() called twice");
}
// Mark service as started
setMcpService(this);
if (mGmcs == null) {
// Initialize the Media Control Service Server
mGmcs = new MediaControlProfile(this);
// Requires this service to be already started thus we have to make it an async call
mHandler.post(() -> mGmcs.init());
}
return true;
}
@Override
protected boolean stop() {
if (DBG) {
Log.d(TAG, "stop()");
}
if (sMcpService == null) {
Log.w(TAG, "stop() called before start()");
return true;
}
if (mGmcs != null) {
mGmcs.cleanup();
}
// Mark service as stopped
setMcpService(null);
return true;
}
@Override
protected void cleanup() {
if (DBG) {
Log.d(TAG, "cleanup()");
}
}
public void onDeviceUnauthorized(BluetoothDevice device) {
Log.w(TAG, "onDeviceUnauthorized - authorization notification not implemented yet ");
}
public void setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized) {
Log.i(TAG, "setDeviceAuthorized(): device: " + device + ", isAuthorized: " + isAuthorized);
int authorization = isAuthorized ? BluetoothDevice.ACCESS_ALLOWED
: BluetoothDevice.ACCESS_REJECTED;
mDeviceAuthorizations.put(device, authorization);
mGmcs.onDeviceAuthorizationSet(device);
}
public int getDeviceAuthorization(BluetoothDevice device) {
// TODO: For now just reject authorization for other than LeAudio device already authorized.
// Consider intent based authorization mechanism for non-LeAudio devices.
return mDeviceAuthorizations.getOrDefault(device, BluetoothDevice.ACCESS_UNKNOWN);
}
/**
* Binder object: must be a static class or memory leak may occur
*/
static class BluetoothMcpServiceBinder
extends IBluetoothMcpServiceManager.Stub implements IProfileServiceBinder {
private McpService mService;
BluetoothMcpServiceBinder(McpService svc) {
mService = svc;
}
private McpService getService(AttributionSource source) {
if (mService != null && mService.isAvailable()) {
return mService;
}
Log.e(TAG, "getService() - Service requested, but not available!");
return null;
}
@Override
public void setDeviceAuthorized(BluetoothDevice device, boolean isAuthorized,
AttributionSource source) {
McpService service = getService(source);
if (service == null) {
return;
}
Utils.enforceBluetoothPrivilegedPermission(service);
service.setDeviceAuthorized(device, isAuthorized);
}
@Override
public void cleanup() {
if (mService != null) {
mService.cleanup();
}
mService = null;
}
}
@Override
public void dump(StringBuilder sb) {
super.dump(sb);
mGmcs.dump(sb);
}
}