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

Commit 44b8e252 authored by Hansong Zhang's avatar Hansong Zhang Committed by android-build-merger
Browse files

Merge "Extract native methods and add unit tests for HID Device" am: 38df49c7

am: 9469d04f

Change-Id: I4d999a411d2e4f11d3377bef6811ec4e879aeb1a
parents 38b83a3d 9469d04f
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.