Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b65c8bc authored by Hemant Gupta's avatar Hemant Gupta Committed by Mike Lockwood
Browse files

Bluetooth: Support MAP Client role on Bluedroid.

Implementation changes to support Message Access Profile
client role in Bluetooth Apk.

Change-Id: I209909a2b18e43c7931571526b983453795f6c6b
parent bc8bd843
Loading
Loading
Loading
Loading
+109 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#define LOG_TAG "BluetoothServiceJni"
#include "com_android_bluetooth.h"
#include "hardware/bt_sock.h"
#include "hardware/bt_mce.h"
#include "utils/Log.h"
#include "utils/misc.h"
#include "cutils/properties.h"
@@ -44,9 +45,11 @@ static jmethodID method_discoveryStateChangeCallback;
static jmethodID method_setWakeAlarm;
static jmethodID method_acquireWakeLock;
static jmethodID method_releaseWakeLock;
static jmethodID method_deviceMasInstancesFoundCallback;

static const bt_interface_t *sBluetoothInterface = NULL;
static const btsock_interface_t *sBluetoothSocketInterface = NULL;
static const btmce_interface_t *sBluetoothMceInterface = NULL;
static JNIEnv *callbackEnv = NULL;

static jobject sJniAdapterServiceObj;
@@ -563,6 +566,73 @@ static void alarmFiredNative(JNIEnv *env, jobject obj) {
    sAlarmCallback(sAlarmCallbackData);
}

static void remote_mas_instances_callback(bt_status_t status, bt_bdaddr_t *bd_addr,
                                          int num_instances, btmce_mas_instance_t *instances)
{
    if (!checkCallbackThread()) {
       ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
       return;
    }

    ALOGV("%s: Status is: %d, Instances: %d", __FUNCTION__, status, num_instances);

    if (status != BT_STATUS_SUCCESS) {
        ALOGE("%s: Status %d is incorrect", __FUNCTION__, status);
        return;
    }

    callbackEnv->PushLocalFrame(ADDITIONAL_NREFS);

    jbyteArray addr = NULL;
    jobjectArray a_name = NULL;
    jintArray a_scn = NULL;
    jintArray a_masid = NULL;
    jintArray a_msgtype = NULL;
    jclass mclass;

    mclass = callbackEnv->FindClass("java/lang/String");

    addr = callbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (addr == NULL) goto clean;

    callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr);

    a_name = callbackEnv->NewObjectArray(num_instances, mclass, NULL);
    if (a_name == NULL) goto clean;

    a_scn = callbackEnv->NewIntArray(num_instances);
    if (a_scn == NULL) goto clean;

    a_masid = callbackEnv->NewIntArray(num_instances);
    if (a_masid == NULL) goto clean;

    a_msgtype = callbackEnv->NewIntArray(num_instances);
    if (a_msgtype == NULL) goto clean;

    for (int i = 0; i < num_instances; i++) {
        jstring name = callbackEnv->NewStringUTF(instances[i].p_name);

        callbackEnv->SetObjectArrayElement(a_name, i, name);
        callbackEnv->SetIntArrayRegion(a_scn, i, 1, &instances[i].scn);
        callbackEnv->SetIntArrayRegion(a_masid, i, 1, &instances[i].id);
        callbackEnv->SetIntArrayRegion(a_msgtype, i, 1, &instances[i].msg_types);

        callbackEnv->DeleteLocalRef(name);
    }

    callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceMasInstancesFoundCallback,
            (jint) status, addr, a_name, a_scn, a_masid, a_msgtype);
    checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);

clean:
    if (addr != NULL) callbackEnv->DeleteLocalRef(addr);
    if (a_name != NULL) callbackEnv->DeleteLocalRef(a_name);
    if (a_scn != NULL) callbackEnv->DeleteLocalRef(a_scn);
    if (a_masid != NULL) callbackEnv->DeleteLocalRef(a_masid);
    if (a_msgtype != NULL) callbackEnv->DeleteLocalRef(a_msgtype);
    callbackEnv->PopLocalFrame(NULL);
}

static bt_os_callouts_t sBluetoothOsCallouts = {
    sizeof(sBluetoothOsCallouts),
    set_wake_alarm_callout,
@@ -570,6 +640,11 @@ static bt_os_callouts_t sBluetoothOsCallouts = {
    release_wake_lock_callout,
};

static btmce_callbacks_t sBluetoothMceCallbacks = {
    sizeof(sBluetoothMceCallbacks),
    remote_mas_instances_callback,
};

static void classInitNative(JNIEnv* env, jclass clazz) {
    int err;
    hw_module_t* module;
@@ -605,6 +680,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
    method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z");
    method_acquireWakeLock = env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z");
    method_releaseWakeLock = env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z");
    method_deviceMasInstancesFoundCallback = env->GetMethodID(jniCallbackClass,
                                                    "deviceMasInstancesFoundCallback",
                                                    "(I[B[Ljava/lang/String;[I[I[I)V");

    char value[PROPERTY_VALUE_MAX];
    property_get("bluetooth.mock_stack", value, "");
@@ -652,6 +730,17 @@ static bool initNative(JNIEnv* env, jobject obj) {
                  sBluetoothInterface->get_profile_interface(BT_PROFILE_SOCKETS_ID)) == NULL) {
                ALOGE("Error getting socket interface");
        }

        if ( (sBluetoothMceInterface = (btmce_interface_t *)
                  sBluetoothInterface->get_profile_interface(BT_PROFILE_MAP_CLIENT_ID)) == NULL) {
                ALOGE("Error getting mapclient interface");
        } else {
            if ( (sBluetoothMceInterface->init(&sBluetoothMceCallbacks)) != BT_STATUS_SUCCESS) {
                ALOGE("Failed to initialize Bluetooth MCE");
                sBluetoothMceInterface = NULL;
            }
        }

        return JNI_TRUE;
    }
    return JNI_FALSE;
@@ -970,6 +1059,25 @@ static jboolean getRemoteServicesNative(JNIEnv *env, jobject obj, jbyteArray add
    return result;
}

static jboolean getRemoteMasInstancesNative(JNIEnv *env, jobject obj, jbyteArray address) {
    ALOGV("%s:",__FUNCTION__);

    jbyte *addr = NULL;
    jboolean result = JNI_FALSE;
    if (!sBluetoothMceInterface) return result;

    addr = env->GetByteArrayElements(address, NULL);
    if (addr == NULL) {
        jniThrowIOException(env, EINVAL);
        return result;
    }

    int ret = sBluetoothMceInterface->get_remote_mas_instances((bt_bdaddr_t *)addr);
    env->ReleaseByteArrayElements(address, addr, NULL);
    result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    return result;
}

static int connectSocketNative(JNIEnv *env, jobject object, jbyteArray address, jint type,
                                   jbyteArray uuidObj, jint channel, jint flag) {
    jbyte *addr = NULL, *uuid = NULL;
@@ -1086,6 +1194,7 @@ static JNINativeMethod sMethods[] = {
    {"pinReplyNative", "([BZI[B)Z", (void*) pinReplyNative},
    {"sspReplyNative", "([BIZI)Z", (void*) sspReplyNative},
    {"getRemoteServicesNative", "([B)Z", (void*) getRemoteServicesNative},
    {"getRemoteMasInstancesNative", "([B)Z", (void*) getRemoteMasInstancesNative},
    {"connectSocketNative", "([BI[BII)I", (void*) connectSocketNative},
    {"createSocketChannelNative", "(ILjava/lang/String;[BII)I",
     (void*) createSocketChannelNative},
+19 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ public class AdapterService extends Service {
    static final String BLUETOOTH_ADMIN_PERM =
        android.Manifest.permission.BLUETOOTH_ADMIN;
    static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
    static final String RECEIVE_MAP_PERM = android.Manifest.permission.RECEIVE_BLUETOOTH_MAP;

    private static final int ADAPTER_SERVICE_TYPE=Service.START_STICKY;

@@ -900,6 +901,17 @@ public class AdapterService extends Service {
            return service.fetchRemoteUuids(device);
        }

        public boolean fetchRemoteMasInstances(BluetoothDevice device) {
            if (!Utils.checkCaller()) {
                Log.w(TAG,"fetchMasInstances(): not allowed for non-active user");
                return false;
            }

            AdapterService service = getService();
            if (service == null) return false;
            return service.fetchRemoteMasInstances(device);
        }

        public boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) {
            if (!Utils.checkCaller()) {
                Log.w(TAG, "setPin() - Not allowed for non-active user");
@@ -1367,6 +1379,12 @@ public class AdapterService extends Service {
        return true;
    }

      boolean fetchRemoteMasInstances(BluetoothDevice device) {
         enforceCallingOrSelfPermission(RECEIVE_MAP_PERM, "Need RECEIVE BLUETOOTH MAP permission");
         mRemoteDevices.fetchMasInstances(device);
         return true;
     }

     boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                       "Need BLUETOOTH ADMIN permission");
@@ -1607,6 +1625,7 @@ public class AdapterService extends Service {
            accept, int passkey);

    /*package*/ native boolean getRemoteServicesNative(byte[] address);
    /*package*/ native boolean getRemoteMasInstancesNative(byte[] address);

    // TODO(BT) move this to ../btsock dir
    private native int connectSocketNative(byte[] address, int type,
+5 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 * Copyright (C) 2012-2014 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.
@@ -81,4 +81,8 @@ final class JniCallbacks {
        mAdapterProperties.adapterPropertyChangedCallback(types, val);
    }

    void deviceMasInstancesFoundCallback(int status, byte[] address, String[] name, int[] scn,
            int[] id, int[] msgtype) {
        mRemoteDevices.deviceMasInstancesFoundCallback(status, address, name, scn, id, msgtype);
    }
}
+64 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 * Copyright (C) 2012-2014 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.
@@ -19,6 +19,7 @@ package com.android.bluetooth.btservice;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothMasInstance;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
@@ -44,18 +45,23 @@ final class RemoteDevices {
    private static BluetoothAdapter mAdapter;
    private static AdapterService mAdapterService;
    private static ArrayList<BluetoothDevice> mSdpTracker;
    private static ArrayList<BluetoothDevice> mSdpMasTracker;

    private Object mObject = new Object();

    private static final int UUID_INTENT_DELAY = 6000;
    private static final int MESSAGE_UUID_INTENT = 1;

    private static final int MAS_INSTANCE_INTENT_DELAY = 6000;
    private static final int MESSAGE_MAS_INSTANCE_INTENT = 2;

    private HashMap<BluetoothDevice, DeviceProperties> mDevices;

    RemoteDevices(AdapterService service) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mAdapterService = service;
        mSdpTracker = new ArrayList<BluetoothDevice>();
        mSdpMasTracker = new ArrayList<BluetoothDevice>();
        mDevices = new HashMap<BluetoothDevice, DeviceProperties>();
    }

@@ -64,6 +70,9 @@ final class RemoteDevices {
        if (mSdpTracker !=null)
            mSdpTracker.clear();

        if (mSdpMasTracker != null)
            mSdpMasTracker.clear();

        if (mDevices != null)
            mDevices.clear();
    }
@@ -224,6 +233,18 @@ final class RemoteDevices {
        mSdpTracker.remove(device);
    }

    private void sendMasInstanceIntent(BluetoothDevice device,
            ArrayList<BluetoothMasInstance> instances) {
        Intent intent = new Intent(BluetoothDevice.ACTION_MAS_INSTANCE);

        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
        if (instances != null)  intent.putExtra(BluetoothDevice.EXTRA_MAS_INSTANCE, instances);
        mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);

        //Remove the outstanding UUID request
        mSdpMasTracker.remove(device);
    }

    private void sendDisplayPinIntent(byte[] address, int pin) {
        Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address));
@@ -429,6 +450,31 @@ final class RemoteDevices {
        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    }

    void deviceMasInstancesFoundCallback(int status, byte[] address, String[] name, int[] scn,
            int[] id, int[] msgtype) {
        BluetoothDevice device = getDevice(address);

        if (device == null) {
            errorLog("deviceMasInstancesFoundCallback: Device is NULL");
            return;
        }

        debugLog("deviceMasInstancesFoundCallback: found " + name.length + " instances");

        ArrayList<BluetoothMasInstance> instances = new ArrayList<BluetoothMasInstance>();

        for (int i = 0; i < name.length; i++) {
            BluetoothMasInstance inst = new BluetoothMasInstance(id[i], name[i],
                    scn[i], msgtype[i]);

            debugLog(inst.toString());

            instances.add(inst);
        }

        sendMasInstanceIntent(device, instances);
    }

    void fetchUuids(BluetoothDevice device) {
        if (mSdpTracker.contains(device)) return;
        mSdpTracker.add(device);
@@ -441,6 +487,17 @@ final class RemoteDevices {
        mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
    }

    void fetchMasInstances(BluetoothDevice device) {
        if (mSdpMasTracker.contains(device)) return;
        mSdpMasTracker.add(device);

        Message message = mHandler.obtainMessage(MESSAGE_MAS_INSTANCE_INTENT);
        message.obj = device;
        mHandler.sendMessageDelayed(message, MAS_INSTANCE_INTENT_DELAY);

        mAdapterService.getRemoteMasInstancesNative(Utils.getBytesFromAddress(device.getAddress()));
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
@@ -451,6 +508,12 @@ final class RemoteDevices {
                    sendUuidIntent(device);
                }
                break;
            case MESSAGE_MAS_INSTANCE_INTENT:
                BluetoothDevice dev = (BluetoothDevice)msg.obj;
                if (dev != null) {
                    sendMasInstanceIntent(dev, null);
                }
                break;
            }
        }
    };