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

Commit 38df49c7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Extract native methods and add unit tests for HID Device"

parents 12d89a2e 4271b705
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -474,8 +474,8 @@ static JNINativeMethod sMethods[] = {
};

int register_com_android_bluetooth_hid_device(JNIEnv* env) {
  return jniRegisterNativeMethods(env,
                                  "com/android/bluetooth/hid/HidDeviceService",
                                  sMethods, NELEM(sMethods));
  return jniRegisterNativeMethods(
      env, "com/android/bluetooth/hid/HidDeviceNativeInterface", sMethods,
      NELEM(sMethods));
}
}  // namespace android
+5 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.bluetooth.btservice;

import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.hid.HidDeviceService;
import com.android.bluetooth.hid.HidHostService;
import com.android.bluetooth.pan.PanService;

@@ -35,6 +36,10 @@ public class ServiceFactory {
        return HidHostService.getHidHostService();
    }

    public HidDeviceService getHidDeviceService() {
        return HidDeviceService.getHidDeviceService();
    }

    public PanService getPanService() {
        return PanService.getPanService();
    }
+281 −0
Original line number Diff line number Diff line
/*
 * Copyright 2017 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.
 */

/*
 * Defines the native inteface that is used by HID Device service to
 * send or receive messages from the native stack. This file is registered
 * for the native methods in the corresponding JNI C++ file.
 */

package com.android.bluetooth.hid;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.support.annotation.VisibleForTesting;
import android.util.Log;

import com.android.bluetooth.Utils;
import com.android.internal.annotations.GuardedBy;

/**
 * HID Device Native Interface to/from JNI.
 */
public class HidDeviceNativeInterface {
    private static final String TAG = "HidDeviceNativeInterface";
    private BluetoothAdapter mAdapter;

    @GuardedBy("INSTANCE_LOCK")
    private static HidDeviceNativeInterface sInstance;
    private static final Object INSTANCE_LOCK = new Object();

    static {
        classInitNative();
    }

    @VisibleForTesting
    private HidDeviceNativeInterface() {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mAdapter == null) {
            Log.wtfStack(TAG, "No Bluetooth Adapter Available");
        }
    }

    /**
     * Get the singleton instance.
     */
    public static HidDeviceNativeInterface getInstance() {
        synchronized (INSTANCE_LOCK) {
            if (sInstance == null) {
                setInstance(new HidDeviceNativeInterface());
            }
            return sInstance;
        }
    }

    /**
     * Set the singleton instance.
     *
     * @param nativeInterface native interface
     */
    private static void setInstance(HidDeviceNativeInterface nativeInterface) {
        sInstance = nativeInterface;
    }

    /**
     * Initializes the native interface.
     */
    public void init() {
        initNative();
    }

    /**
     * Cleanup the native interface.
     */
    public void cleanup() {
        cleanupNative();
    }

    /**
     * Registers the application
     *
     * @param name name of the HID Device application
     * @param description description of the HID Device application
     * @param provider provider of the HID Device application
     * @param subclass subclass of the HID Device application
     * @param descriptors HID descriptors
     * @param inQos incoming QoS settings
     * @param outQos outgoing QoS settings
     * @return the result of the native call
     */
    public boolean registerApp(String name, String description, String provider,
            byte subclass, byte[] descriptors, int[] inQos, int[] outQos) {
        return registerAppNative(name, description, provider, subclass, descriptors, inQos, outQos);
    }

    /**
     * Unregisters the application
     *
     * @return the result of the native call
     */
    public boolean unregisterApp() {
        return unregisterAppNative();
    }

    /**
     * Send report to the remote host
     *
     * @param id report ID
     * @param data report data array
     * @return the result of the native call
     */
    public boolean sendReport(int id, byte[] data) {
        return sendReportNative(id, data);
    }

    /**
     * Reply report to the remote host
     *
     * @param type report type
     * @param id report ID
     * @param data report data array
     * @return the result of the native call
     */
    public boolean replyReport(byte type, byte id, byte[] data) {
        return replyReportNative(type, id, data);
    }

    /**
     * Send virtual unplug to the remote host
     *
     * @return the result of the native call
     */
    public boolean unplug() {
        return unplugNative();
    }

    /**
     * Connect to the remote host
     *
     * @param device remote host device
     * @return the result of the native call
     */
    public boolean connect(BluetoothDevice device) {
        return connectNative(getByteAddress(device));
    }

    /**
     * Disconnect from the remote host
     *
     * @return the result of the native call
     */
    public boolean disconnect() {
        return disconnectNative();
    }

    /**
     * Report error to the remote host
     *
     * @param error error byte
     * @return the result of the native call
     */
    public boolean reportError(byte error) {
        return reportErrorNative(error);
    }

    private synchronized void onApplicationStateChanged(byte[] address, boolean registered) {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onApplicationStateChangedFromNative(getDevice(address), registered);
        } else {
            Log.wtfStack(TAG, "FATAL: onApplicationStateChanged() "
                    + "is called from the stack while service is not available.");
        }
    }

    private synchronized void onConnectStateChanged(byte[] address, int state) {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onConnectStateChangedFromNative(getDevice(address), state);
        } else {
            Log.wtfStack(TAG, "FATAL: onConnectStateChanged() "
                    + "is called from the stack while service is not available.");
        }
    }

    private synchronized void onGetReport(byte type, byte id, short bufferSize) {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onGetReportFromNative(type, id, bufferSize);
        } else {
            Log.wtfStack(TAG, "FATAL: onGetReport() "
                    + "is called from the stack while service is not available.");
        }
    }

    private synchronized void onSetReport(byte reportType, byte reportId, byte[] data) {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onSetReportFromNative(reportType, reportId, data);
        } else {
            Log.wtfStack(TAG, "FATAL: onSetReport() "
                    + "is called from the stack while service is not available.");
        }
    }

    private synchronized void onSetProtocol(byte protocol) {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onSetProtocolFromNative(protocol);
        } else {
            Log.wtfStack(TAG, "FATAL: onSetProtocol() "
                    + "is called from the stack while service is not available.");
        }
    }

    private synchronized void onIntrData(byte reportId, byte[] data) {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onIntrDataFromNative(reportId, data);
        } else {
            Log.wtfStack(TAG, "FATAL: onIntrData() "
                    + "is called from the stack while service is not available.");
        }
    }

    private synchronized void onVirtualCableUnplug() {
        HidDeviceService service = HidDeviceService.getHidDeviceService();
        if (service != null) {
            service.onVirtualCableUnplugFromNative();
        } else {
            Log.wtfStack(TAG, "FATAL: onVirtualCableUnplug() "
                    + "is called from the stack while service is not available.");
        }
    }

    private BluetoothDevice getDevice(byte[] address) {
        if (address == null) {
            return null;
        }
        return mAdapter.getRemoteDevice(address);
    }

    private byte[] getByteAddress(BluetoothDevice device) {
        return Utils.getBytesFromAddress(device.getAddress());
    }

    private static native void classInitNative();

    private native void initNative();

    private native void cleanupNative();

    private native boolean registerAppNative(String name, String description, String provider,
            byte subclass, byte[] descriptors, int[] inQos, int[] outQos);

    private native boolean unregisterAppNative();

    private native boolean sendReportNative(int id, byte[] data);

    private native boolean replyReportNative(byte type, byte id, byte[] data);

    private native boolean unplugNative();

    private native boolean connectNative(byte[] btAddress);

    private native boolean disconnectNative();

    private native boolean reportErrorNative(byte error);
}
+76 −63
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.util.Log;

import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import com.android.internal.annotations.VisibleForTesting;

import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -54,24 +55,29 @@ public class HidDeviceService extends ProfileService {
    private static final int MESSAGE_INTR_DATA = 6;
    private static final int MESSAGE_VC_UNPLUG = 7;

    private static HidDeviceService sHidDeviceService;

    private HidDeviceNativeInterface mHidDeviceNativeInterface;

    private boolean mNativeAvailable = false;

    private BluetoothDevice mHidDevice = null;
    private BluetoothDevice mHidDevice;

    private int mHidDeviceState = BluetoothHidDevice.STATE_DISCONNECTED;

    private BluetoothHidDeviceAppConfiguration mAppConfig = null;
    private BluetoothHidDeviceAppConfiguration mAppConfig;

    private IBluetoothHidDeviceCallback mCallback = null;
    private IBluetoothHidDeviceCallback mCallback;

    private BluetoothHidDeviceDeathRecipient mDeathRcpt;

    static {
        classInitNative();
    }
    private HidDeviceServiceHandler mHandler;

    private final Handler mHandler = new Handler() {
    public HidDeviceService() {
        mHidDeviceNativeInterface = HidDeviceNativeInterface.getInstance();
    }

    private class HidDeviceServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (DBG) {
@@ -80,10 +86,11 @@ public class HidDeviceService extends ProfileService {

            switch (msg.what) {
                case MESSAGE_APPLICATION_STATE_CHANGED: {
                    BluetoothDevice device = msg.obj != null ? getDevice((byte[]) msg.obj) : null;
                    BluetoothDevice device = msg.obj != null ? (BluetoothDevice) msg.obj : null;
                    boolean success = (msg.arg1 != 0);

                    if (success) {
                        Log.d(TAG, "App registered, set device to: " + device);
                        mHidDevice = device;
                    } else {
                        mHidDevice = null;
@@ -135,7 +142,7 @@ public class HidDeviceService extends ProfileService {
                }

                case MESSAGE_CONNECT_STATE_CHANGED: {
                    BluetoothDevice device = getDevice((byte[]) msg.obj);
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    int halState = msg.arg1;
                    int state = convertHalState(halState);

@@ -143,7 +150,7 @@ public class HidDeviceService extends ProfileService {
                        mHidDevice = device;
                    }

                    broadcastConnectionState(device, state);
                    setAndBroadcastConnectionState(device, state);

                    try {
                        if (mCallback != null) {
@@ -245,7 +252,8 @@ public class HidDeviceService extends ProfileService {
        }
    }

    private static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub
    @VisibleForTesting
    static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub
            implements IProfileServiceBinder {

        private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName();
@@ -256,6 +264,14 @@ public class HidDeviceService extends ProfileService {
            mService = service;
        }

        @VisibleForTesting
        HidDeviceService getServiceForTesting() {
            if (mService != null && mService.isAvailable()) {
                return mService;
            }
            return null;
        }

        @Override
        public boolean cleanup() {
            mService = null;
@@ -456,8 +472,8 @@ public class HidDeviceService extends ProfileService {
        mAppConfig = config;
        mCallback = callback;

        return registerAppNative(sdp.name, sdp.description, sdp.provider, sdp.subclass,
                sdp.descriptors, inQos == null ? null : inQos.toArray(),
        return mHidDeviceNativeInterface.registerApp(sdp.name, sdp.description, sdp.provider,
                sdp.subclass, sdp.descriptors, inQos == null ? null : inQos.toArray(),
                outQos == null ? null : outQos.toArray());
    }

@@ -475,7 +491,7 @@ public class HidDeviceService extends ProfileService {
            return false;
        }

        return unregisterAppNative();
        return mHidDeviceNativeInterface.unregisterApp();
    }

    synchronized boolean sendReport(BluetoothDevice device, int id, byte[] data) {
@@ -487,7 +503,7 @@ public class HidDeviceService extends ProfileService {
            return false;
        }

        return sendReportNative(id, data);
        return mHidDeviceNativeInterface.sendReport(id, data);
    }

    synchronized boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
@@ -499,7 +515,7 @@ public class HidDeviceService extends ProfileService {
            return false;
        }

        return replyReportNative(type, id, data);
        return mHidDeviceNativeInterface.replyReport(type, id, data);
    }

    synchronized boolean unplug(BluetoothDevice device) {
@@ -511,7 +527,7 @@ public class HidDeviceService extends ProfileService {
            return false;
        }

        return unplugNative();
        return mHidDeviceNativeInterface.unplug();
    }

    synchronized boolean connect(BluetoothDevice device) {
@@ -519,7 +535,7 @@ public class HidDeviceService extends ProfileService {
            Log.d(TAG, "connect(): device=" + device);
        }

        return connectNative(Utils.getByteAddress(device));
        return mHidDeviceNativeInterface.connect(device);
    }

    synchronized boolean disconnect(BluetoothDevice device) {
@@ -531,7 +547,7 @@ public class HidDeviceService extends ProfileService {
            return false;
        }

        return disconnectNative();
        return mHidDeviceNativeInterface.disconnect();
    }

    synchronized boolean reportError(BluetoothDevice device, byte error) {
@@ -543,7 +559,7 @@ public class HidDeviceService extends ProfileService {
            return false;
        }

        return reportErrorNative(error);
        return mHidDeviceNativeInterface.reportError(error);
    }

    @Override
@@ -552,9 +568,10 @@ public class HidDeviceService extends ProfileService {
            Log.d(TAG, "start()");
        }

        initNative();
        mHandler = new HidDeviceServiceHandler();
        setHidDeviceService(this);
        mHidDeviceNativeInterface.init();
        mNativeAvailable = true;

        return true;
    }

@@ -574,7 +591,7 @@ public class HidDeviceService extends ProfileService {
        }

        if (mNativeAvailable) {
            cleanupNative();
            mHidDeviceNativeInterface.cleanup();
            mNativeAvailable = false;
        }

@@ -588,6 +605,26 @@ public class HidDeviceService extends ProfileService {
        return super.onUnbind(intent);
    }

    /**
     * Get the HID Device Service instance
     * @return HID Device Service instance
     */
    public static synchronized HidDeviceService getHidDeviceService() {
        if (sHidDeviceService == null) {
            Log.d(TAG, "getHidDeviceService(): service is NULL");
            return null;
        }
        if (!sHidDeviceService.isAvailable()) {
            Log.d(TAG, "getHidDeviceService(): service is not available");
            return null;
        }
        return sHidDeviceService;
    }

    private static synchronized void setHidDeviceService(HidDeviceService instance) {
        sHidDeviceService = instance;
    }

    int getConnectionState(BluetoothDevice device) {
        if (mHidDevice != null && mHidDevice.equals(device)) {
            return mHidDeviceState;
@@ -610,30 +647,31 @@ public class HidDeviceService extends ProfileService {
        return inputDevices;
    }

    private synchronized void onApplicationStateChanged(byte[] address, boolean registered) {
    synchronized void onApplicationStateChangedFromNative(BluetoothDevice device,
            boolean registered) {
        if (DBG) {
            Log.d(TAG, "onApplicationStateChanged(): registered=" + registered);
        }

        Message msg = mHandler.obtainMessage(MESSAGE_APPLICATION_STATE_CHANGED);
        msg.obj = address;
        msg.obj = device;
        msg.arg1 = registered ? 1 : 0;
        mHandler.sendMessage(msg);
    }

    private synchronized void onConnectStateChanged(byte[] address, int state) {
    synchronized void onConnectStateChangedFromNative(BluetoothDevice device, int state) {
        if (DBG) {
            Log.d(TAG, "onConnectStateChanged(): address=" + Arrays.toString(address) + " state="
                    + state);
            Log.d(TAG, "onConnectStateChanged(): device="
                    + device + " state=" + state);
        }

        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
        msg.obj = address;
        msg.obj = device;
        msg.arg1 = state;
        mHandler.sendMessage(msg);
    }

    private synchronized void onGetReport(byte type, byte id, short bufferSize) {
    synchronized void onGetReportFromNative(byte type, byte id, short bufferSize) {
        if (DBG) {
            Log.d(TAG, "onGetReport(): type=" + type + " id=" + id + " bufferSize=" + bufferSize);
        }
@@ -645,7 +683,7 @@ public class HidDeviceService extends ProfileService {
        mHandler.sendMessage(msg);
    }

    private synchronized void onSetReport(byte reportType, byte reportId, byte[] data) {
    synchronized void onSetReportFromNative(byte reportType, byte reportId, byte[] data) {
        if (DBG) {
            Log.d(TAG, "onSetReport(): reportType=" + reportType + " reportId=" + reportId);
        }
@@ -659,7 +697,7 @@ public class HidDeviceService extends ProfileService {
        mHandler.sendMessage(msg);
    }

    private synchronized void onSetProtocol(byte protocol) {
    synchronized void onSetProtocolFromNative(byte protocol) {
        if (DBG) {
            Log.d(TAG, "onSetProtocol(): protocol=" + protocol);
        }
@@ -669,7 +707,7 @@ public class HidDeviceService extends ProfileService {
        mHandler.sendMessage(msg);
    }

    private synchronized void onIntrData(byte reportId, byte[] data) {
    synchronized void onIntrDataFromNative(byte reportId, byte[] data) {
        if (DBG) {
            Log.d(TAG, "onIntrData(): reportId=" + reportId);
        }
@@ -682,7 +720,7 @@ public class HidDeviceService extends ProfileService {
        mHandler.sendMessage(msg);
    }

    private synchronized void onVirtualCableUnplug() {
    synchronized void onVirtualCableUnplugFromNative() {
        if (DBG) {
            Log.d(TAG, "onVirtualCableUnplug()");
        }
@@ -691,10 +729,10 @@ public class HidDeviceService extends ProfileService {
        mHandler.sendMessage(msg);
    }

    private void broadcastConnectionState(BluetoothDevice device, int newState) {
    private void setAndBroadcastConnectionState(BluetoothDevice device, int newState) {
        if (DBG) {
            Log.d(TAG, "broadcastConnectionState(): device=" + device.getAddress() + " newState="
                    + newState);
            Log.d(TAG, "setAndBroadcastConnectionState(): device=" + device.getAddress()
                    + " oldState=" + mHidDeviceState + " newState=" + newState);
        }

        if (mHidDevice != null && !mHidDevice.equals(device)) {
@@ -705,10 +743,8 @@ public class HidDeviceService extends ProfileService {
        int prevState = mHidDeviceState;
        mHidDeviceState = newState;

        Log.i(TAG, "connection state for " + device.getAddress() + ": " + prevState + " -> "
                + newState);

        if (prevState == newState) {
            Log.w(TAG, "Connection state is unchanged, ignoring");
            return;
        }

@@ -739,27 +775,4 @@ public class HidDeviceService extends ProfileService {
    private static final int CONN_STATE_CONNECTING = 1;
    private static final int CONN_STATE_DISCONNECTED = 2;
    private static final int CONN_STATE_DISCONNECTING = 3;

    private static native void classInitNative();

    private native void initNative();

    private native void cleanupNative();

    private native boolean registerAppNative(String name, String description, String provider,
            byte subclass, byte[] descriptors, int[] inQos, int[] outQos);

    private native boolean unregisterAppNative();

    private native boolean sendReportNative(int id, byte[] data);

    private native boolean replyReportNative(byte type, byte id, byte[] data);

    private native boolean unplugNative();

    private native boolean connectNative(byte[] btAddress);

    private native boolean disconnectNative();

    private native boolean reportErrorNative(byte error);
}
+183 −0

File added.

Preview size limit exceeded, changes collapsed.