AIDL CAN HAL

CAN HAL: now HIDL-free with 100% of your daily value of AIDL.

Bug: 170405615
Test: atest CanControllerAidlTest
Test: manually test slcan and socketcan hardware
Change-Id: I06bbb1379f4385e89757722c0276009b54cc7255
This commit is contained in:
Chris Weir
2022-07-08 09:52:52 -07:00
parent e9d0b47df2
commit d76e07e604
42 changed files with 2457 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
//
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
aidl_interface {
name: "android.hardware.automotive.can",
vendor_available: true,
srcs: ["android/hardware/automotive/can/*.aidl"],
stability: "vintf",
host_supported: true,
backend: {
java: {
enabled: false,
},
rust: {
enabled: true,
},
},
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@VintfStability
parcelable BusConfig {
String name;
android.hardware.automotive.can.BusConfig.InterfaceId interfaceId;
int bitrate;
union InterfaceId {
android.hardware.automotive.can.VirtualInterface virtualif;
android.hardware.automotive.can.NativeInterface nativeif;
android.hardware.automotive.can.SlcanInterface slcan;
android.hardware.automotive.can.IndexedInterface indexed;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@VintfStability
interface ICanController {
android.hardware.automotive.can.InterfaceType[] getSupportedInterfaceTypes();
String getInterfaceName(in String busName);
String upBus(in android.hardware.automotive.can.BusConfig config);
void downBus(in String name);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@VintfStability
parcelable IndexedInterface {
byte index;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@Backing(type="byte") @VintfStability
enum InterfaceType {
VIRTUAL = 0,
NATIVE = 1,
SLCAN = 2,
INDEXED = 3,
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@VintfStability
parcelable NativeInterface {
android.hardware.automotive.can.NativeInterface.InterfaceId interfaceId;
union InterfaceId {
String ifname;
String[] serialno;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@Backing(type="int") @VintfStability
enum Result {
OK = 0,
UNKNOWN_ERROR = 1,
INVALID_STATE = 2,
NOT_SUPPORTED = 3,
BAD_INTERFACE_ID = 4,
BAD_BITRATE = 5,
BAD_BUS_NAME = 6,
INTERFACE_DOWN = 7,
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@VintfStability
parcelable SlcanInterface {
android.hardware.automotive.can.SlcanInterface.InterfaceId interfaceId;
union InterfaceId {
String ttyname;
String[] serialno;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.can;
@VintfStability
parcelable VirtualInterface {
String ifname;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
import android.hardware.automotive.can.IndexedInterface;
import android.hardware.automotive.can.NativeInterface;
import android.hardware.automotive.can.SlcanInterface;
import android.hardware.automotive.can.VirtualInterface;
/**
* Configuration of the (physical or virtual) CAN bus.
*
* ISO TP and CAN FD support is dependent upon the hardware.
*/
@VintfStability
parcelable BusConfig {
/**
* Name by which a given bus may be referenced.
*
* It must consist of only alphanumeric characters and underscore
* (a-z, A-Z, 0-9, '_'), at least 1 and at most 32 characters long.
*
* This field is *not* meant to distinguish between hardware interfaces
* nor preselect parameters like bitrate.
*
* This field represents a more human-friendly name for a CAN bus:
* e.x. rather than /some/dev/can1234, "name" might be "BodyCAN" or "CCAN"
*/
String name;
/**
* Hardware interface configuration.
*
* This union's discriminator has an equivalent enum {@see InterfaceType} to
* express compatibility via getSupportedInterfaceTypes().
*/
union InterfaceId {
/** Virtual SocketCAN interface. */
VirtualInterface virtualif;
/** Native SocketCAN interface. */
NativeInterface nativeif;
/** Serial line CAN interface. */
SlcanInterface slcan;
/**
* Proprietary, device-specific interface.
*
* Non-SocketCAN interfaces should use this variant.
*/
IndexedInterface indexed;
}
InterfaceId interfaceId;
/**
* Bit rate for CAN communication.
*
* Typical bit rates are: 100000, 125000, 250000, 500000.
*
* For {@see interfaceId#virtual} interfaces, this value is ignored.
*/
int bitrate;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
import android.hardware.automotive.can.BusConfig;
import android.hardware.automotive.can.InterfaceType;
import android.hardware.automotive.can.Result;
/**
* Represents a CAN controller that's capable of configuring CAN bus interfaces.
*
* The goal of this service is to configure and manage CAN interfaces.
*
* Providing an ICanController interface to configure CAN buses is optional.
* A system can elect to configure CAN buses manually if the hardware is
* dedicated to a specific application.
*/
@VintfStability
interface ICanController {
/**
* Fetches the list of interface types supported by this HAL server.
*
* @return iftypes The list of supported interface types.
*/
InterfaceType[] getSupportedInterfaceTypes();
/**
* Gets the interface name given the name of the bus. This will
*
* @param busName Name of the CAN bus who's interface name we would like
* (e.x. BCAN, CCAN, HS3, BodyCAN, ...)
* @return name of the socketcan network interface corresponding to busName
* (e.x. can0, vcan5, ...)
*/
String getInterfaceName(in String busName);
/**
* Bring up a CAN bus.
*
* @param config Configuration for the CAN bus.
* @return name of iface if successful
*/
String upBus(in BusConfig config);
/**
* Bring down a CAN bus.
*
* @param name Name of the bus (@see BusConfig#name} to bring down.
*/
void downBus(in String name);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
@VintfStability
parcelable IndexedInterface {
/** Interface number, 0-based. */
byte index;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
/**
* Type of an interface, an equivalent to BusConfig::InterfaceId
* union discriminator. Defines a number of specific standard hardware
* families and a generic catch-all type of {@see INDEXED}.
*/
@VintfStability
@Backing(type="byte")
enum InterfaceType {
/** Virtual SocketCAN interface. */
VIRTUAL,
/** Native SocketCAN interface. */
NATIVE,
/** Serial line CAN interface. */
SLCAN,
/** Proprietary, device-specific interface. */
INDEXED,
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
@VintfStability
parcelable NativeInterface {
union InterfaceId {
/** Interface name, such as can0. */
String ifname;
/**
* Alternatively to providing {@see ifname}, one may provide a list of
* interface serial number suffixes. If there happens to be a device
* (like USB2CAN) with a matching serial number suffix, the HAL service
* will locate it.
*
* Client may utilize this in two ways: by matching against the
* entire serial number, or the last few characters (usually
* one). The former is better for small-scale test deployments
* (with just a handful of vehicles), the latter is good for
* larger scale (where a small suffix list may support large
* test fleet).
*/
String[] serialno;
}
InterfaceId interfaceId;
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
/**
* Possible error codes (or OK) for ICanController.
*/
@VintfStability
@Backing(type="int")
enum Result {
OK,
/**
* General error class, if others are not applicable.
*/
UNKNOWN_ERROR,
/**
* Up request was called out of order (i.e. trying to up the interface
* twice).
*/
INVALID_STATE,
/** Interface type is not supported. */
NOT_SUPPORTED,
/**
* Provided interface ID (index, name, device path) doesn't exist or there
* is no device with a given serial number.
*/
BAD_INTERFACE_ID,
/** Provided bit rate is not supported by the hardware. */
BAD_BITRATE,
/**
* Provided bus name ({@see BusConfig#name}) has invalid format or doesn't exist.
*/
BAD_BUS_NAME,
/**
* The interface for the bus you are trying to interact with is currently
* down. As opposed to INVALID_STATE, this serves to warn the caller
* _before_ they attempt an invalid operation.
*/
INTERFACE_DOWN,
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
@VintfStability
parcelable SlcanInterface {
union InterfaceId {
/** Path to a device, such as /dev/ttyUSB0. */
String ttyname;
/**
* List of interface serial number suffixes.
* {@see Socketcan::serialno}
*/
String[] serialno;
}
InterfaceId interfaceId;
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2022 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 android.hardware.automotive.can;
@VintfStability
parcelable VirtualInterface {
/**
* Interface name, such as vcan0. If the interface doesn't
* exist, HAL server must create it.
*/
String ifname;
}

View File

@@ -0,0 +1,51 @@
//
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_binary {
name: "android.hardware.automotive.can-service",
init_rc: ["android.hardware.automotive.can.rc"],
defaults: ["android.hardware.automotive.can@defaults"],
vendor: true,
relative_install_path: "hw",
srcs: [
"CanBus.cpp",
"CanBusSlcan.cpp",
"CanBusNative.cpp",
"CanBusVirtual.cpp",
"CanController.cpp",
"service.cpp",
],
shared_libs: [
"android.hardware.automotive.can-V1-ndk",
"libbase",
"libbinder_ndk",
],
static_libs: [
"android.hardware.automotive.can@libnetdevice",
"android.hardware.automotive@libc++fs",
"libnl++",
],
vintf_fragments: ["android.hardware.automotive.can.xml"],
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2022, 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.
*/
#include "CanBus.h"
#include <android-base/logging.h>
#include <libnetdevice/libnetdevice.h>
namespace aidl::android::hardware::automotive::can {
CanBus::CanBus(std::string_view ifname) : mIfname(ifname) {}
CanBus::~CanBus() {
std::lock_guard<std::mutex> lck(mIsUpGuard);
CHECK(!mIsUp) << "Interface is still up while being destroyed";
}
Result CanBus::preUp() {
return Result::OK;
}
bool CanBus::postDown() {
return true;
}
std::string CanBus::getIfaceName() {
return mIfname;
}
Result CanBus::up() {
std::lock_guard<std::mutex> lck(mIsUpGuard);
if (mIsUp) {
LOG(WARNING) << "Interface is already up";
return Result::INVALID_STATE;
}
const auto preResult = preUp();
if (preResult != Result::OK) return preResult;
const auto isUp = ::android::netdevice::isUp(mIfname);
if (!isUp.has_value()) {
// preUp() should prepare the interface (either create or make sure it's there)
LOG(ERROR) << "Interface " << mIfname << " didn't get prepared";
return Result::BAD_INTERFACE_ID;
}
if (!*isUp && !::android::netdevice::up(mIfname)) {
LOG(ERROR) << "Can't bring " << mIfname << " up";
return Result::UNKNOWN_ERROR;
}
mDownAfterUse = !*isUp;
mIsUp = true;
return Result::OK;
}
Result CanBus::down() {
std::lock_guard<std::mutex> lck(mIsUpGuard);
if (!mIsUp) {
LOG(WARNING) << "Interface is already down";
return Result::INVALID_STATE;
}
mIsUp = false;
Result success = Result::OK;
if (mDownAfterUse && !::android::netdevice::down(mIfname)) {
LOG(ERROR) << "Can't bring " << mIfname << " down";
// don't return yet, let's try to do best-effort cleanup
success = Result::UNKNOWN_ERROR;
}
if (!postDown()) success = Result::UNKNOWN_ERROR;
return success;
}
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2022, 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.
*/
#pragma once
#include <aidl/android/hardware/automotive/can/Result.h>
#include <android-base/macros.h>
#include <utils/Mutex.h>
#include <atomic>
#include <mutex>
namespace aidl::android::hardware::automotive::can {
class CanBus {
public:
/**
* Some interface types (such as SLCAN) don't get an interface name until after being
* initialized, hence ifname is optional.
*
* You MUST ensure mIfname is initialized prior to the completion of preUp().
*/
CanBus(std::string_view ifname = std::string_view{""});
virtual ~CanBus();
Result up();
Result down();
std::string getIfaceName();
protected:
/**
* Prepare the SocketCAN interface.
*
* After calling this method, mIfname network interface is available and ready to be brought up.
*
* \return true upon success and false upon failure
*/
virtual Result preUp();
/**
* Cleanup after bringing the interface down.
*
* This is a counterpart to preUp().
*
* \return true upon success and false upon failure
*/
virtual bool postDown();
/** Network interface name. */
std::string mIfname;
private:
/**
* Guard for up flag is required to be held for entire time when the interface is being used
* because we don't want the interface to be torn down while executing that operation.
*/
std::mutex mIsUpGuard;
bool mIsUp GUARDED_BY(mIsUpGuard) = false;
bool mDownAfterUse;
};
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2022, 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.
*/
#include "CanBusNative.h"
#include <android-base/logging.h>
#include <libnetdevice/can.h>
#include <libnetdevice/libnetdevice.h>
namespace aidl::android::hardware::automotive::can {
using namespace ::android;
CanBusNative::CanBusNative(const std::string& ifname, uint32_t bitrate)
: CanBus(ifname), mBitrate(bitrate) {}
Result CanBusNative::preUp() {
if (!netdevice::exists(mIfname)) {
LOG(ERROR) << "Interface " << mIfname << " doesn't exist";
return Result::BAD_INTERFACE_ID;
}
if (mBitrate == 0) {
// interface is already up and we just want to register it
return Result::OK;
}
if (!netdevice::down(mIfname)) {
LOG(ERROR) << "Can't bring " << mIfname << " down (to configure it)";
return Result::UNKNOWN_ERROR;
}
if (!netdevice::can::setBitrate(mIfname, mBitrate)) {
LOG(ERROR) << "Can't set bitrate " << mBitrate << " for " << mIfname;
return Result::BAD_BITRATE;
}
return Result::OK;
}
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2022, 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.
*/
#pragma once
#include "CanBus.h"
namespace aidl::android::hardware::automotive::can {
class CanBusNative : public CanBus {
public:
CanBusNative(const std::string& ifname, uint32_t bitrate);
protected:
virtual Result preUp() override;
private:
const uint32_t mBitrate;
};
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2022, 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.
*/
#include "CanBusSlcan.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <libnetdevice/libnetdevice.h>
#include <linux/serial.h>
#include <linux/tty.h>
#include <net/if.h>
#include <termios.h>
#include <map>
namespace aidl::android::hardware::automotive::can {
using namespace std::string_view_literals;
using namespace ::android::base;
namespace slcanprotocol {
static constexpr std::string_view kOpenCommand = "O\r"sv;
static constexpr std::string_view kCloseCommand = "C\r"sv;
static constexpr int kSlcanDiscipline = N_SLCAN;
static constexpr int kDefaultDiscipline = N_TTY;
static const std::map<uint32_t, std::string_view> kBitrateCommands = {
{10000, "C\rS0\r"sv}, {20000, "C\rS1\r"sv}, {50000, "C\rS2\r"sv},
{100000, "C\rS3\r"sv}, {125000, "C\rS4\r"sv}, {250000, "C\rS5\r"sv},
{500000, "C\rS6\r"sv}, {800000, "C\rS7\r"sv}, {1000000, "C\rS8\r"sv}};
} // namespace slcanprotocol
/**
* Serial Line CAN constructor
* \param string uartName - name of slcan device (e.x. /dev/ttyUSB0)
* \param uint32_t bitrate - speed of the CAN bus (125k = MSCAN, 500k = HSCAN)
*/
CanBusSlcan::CanBusSlcan(const std::string& uartName, uint32_t bitrate)
: CanBus(), mTtyPath(uartName), kBitrate(bitrate) {}
/** helper function to update CanBusSlcan object's iface name */
Result CanBusSlcan::updateIfaceName(unique_fd& uartFd) {
struct ifreq ifrequest = {};
/*
* Fetching the iface name with an ioctl won't interfere with an open socketCAN iface attached
* to this tty. This is important in the event we are trying to register a SLCAN based iface
* that has already been configured and brought up.
*/
if (ioctl(uartFd.get(), SIOCGIFNAME, ifrequest.ifr_name) < 0) {
PLOG(ERROR) << "Failed to get the name of the created device";
return Result::UNKNOWN_ERROR;
}
// Update the CanBus object with name that was assigned to it
mIfname = ifrequest.ifr_name;
return Result::OK;
}
Result CanBusSlcan::preUp() {
// verify valid bitrate and translate to serial command format
std::optional<std::string_view> canBitrateCommand = std::nullopt;
if (kBitrate != 0) {
const auto lookupIt = slcanprotocol::kBitrateCommands.find(kBitrate);
if (lookupIt == slcanprotocol::kBitrateCommands.end()) {
return Result::BAD_BITRATE;
}
canBitrateCommand = lookupIt->second;
}
/* Attempt to open the uart in r/w without blocking or becoming the
* controlling terminal */
mFd = unique_fd(open(mTtyPath.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY | O_CLOEXEC));
if (!mFd.ok()) {
PLOG(ERROR) << "SLCAN Failed to open " << mTtyPath;
return Result::BAD_INTERFACE_ID;
}
// If the device is already up, update the iface name in our CanBusSlcan object
if (kBitrate == 0) {
return updateIfaceName(mFd);
}
// blank terminal settings and pull them from the device
struct termios terminalSettings = {};
if (tcgetattr(mFd.get(), &terminalSettings) < 0) {
PLOG(ERROR) << "Failed to read attrs of" << mTtyPath;
return Result::UNKNOWN_ERROR;
}
// change settings to raw mode
cfmakeraw(&terminalSettings);
// disable software flow control
terminalSettings.c_iflag &= ~IXOFF;
// enable hardware flow control
terminalSettings.c_cflag |= CRTSCTS;
struct serial_struct serialSettings;
// get serial settings
if (ioctl(mFd.get(), TIOCGSERIAL, &serialSettings) < 0) {
PLOG(ERROR) << "Failed to read serial settings from " << mTtyPath;
return Result::UNKNOWN_ERROR;
}
// set low latency mode
serialSettings.flags |= ASYNC_LOW_LATENCY;
// apply serial settings
if (ioctl(mFd.get(), TIOCSSERIAL, &serialSettings) < 0) {
PLOG(ERROR) << "Failed to set low latency mode on " << mTtyPath;
return Result::UNKNOWN_ERROR;
}
/* TCSADRAIN applies settings after we finish writing the rest of our
* changes (as opposed to TCSANOW, which changes immediately) */
if (tcsetattr(mFd.get(), TCSADRAIN, &terminalSettings) < 0) {
PLOG(ERROR) << "Failed to apply terminal settings to " << mTtyPath;
return Result::UNKNOWN_ERROR;
}
// apply speed setting for CAN
if (!WriteStringToFd(*canBitrateCommand, mFd)) {
PLOG(ERROR) << "Failed to apply CAN bitrate";
return Result::UNKNOWN_ERROR;
}
// TODO(b/144775286): set open flag & support listen only
if (!WriteStringToFd(slcanprotocol::kOpenCommand, mFd)) {
PLOG(ERROR) << "Failed to set open flag";
return Result::UNKNOWN_ERROR;
}
// set line discipline to slcan
if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kSlcanDiscipline) < 0) {
PLOG(ERROR) << "Failed to set line discipline to slcan";
return Result::UNKNOWN_ERROR;
}
// Update the CanBus object with name that was assigned to it
return updateIfaceName(mFd);
}
bool CanBusSlcan::postDown() {
// reset the line discipline to TTY mode
if (ioctl(mFd.get(), TIOCSETD, &slcanprotocol::kDefaultDiscipline) < 0) {
LOG(ERROR) << "Failed to reset line discipline!";
return false;
}
// issue the close command
if (!WriteStringToFd(slcanprotocol::kCloseCommand, mFd)) {
LOG(ERROR) << "Failed to close tty!";
return false;
}
// close our unique_fd
mFd.reset();
return true;
}
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2022, 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.
*/
#pragma once
#include "CanBus.h"
#include <android-base/unique_fd.h>
namespace aidl::android::hardware::automotive::can {
class CanBusSlcan : public CanBus {
public:
CanBusSlcan(const std::string& uartName, uint32_t bitrate);
protected:
virtual Result preUp() override;
virtual bool postDown() override;
private:
Result updateIfaceName(::android::base::unique_fd& uartFd);
const std::string mTtyPath;
const uint32_t kBitrate;
::android::base::unique_fd mFd;
};
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2022, 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.
*/
#include "CanBusVirtual.h"
#include <android-base/logging.h>
#include <libnetdevice/libnetdevice.h>
namespace aidl::android::hardware::automotive::can {
using namespace ::android;
CanBusVirtual::CanBusVirtual(const std::string& ifname) : CanBus(ifname) {}
Result CanBusVirtual::preUp() {
if (netdevice::exists(mIfname)) return Result::OK;
LOG(DEBUG) << "Virtual interface " << mIfname << " doesn't exist, creating...";
mWasCreated = true;
if (!netdevice::add(mIfname, "vcan")) {
LOG(ERROR) << "Can't create vcan interface " << mIfname;
return Result::UNKNOWN_ERROR;
}
return Result::OK;
}
bool CanBusVirtual::postDown() {
if (mWasCreated) {
mWasCreated = false;
if (!netdevice::del(mIfname)) {
LOG(ERROR) << "Couldn't remove vcan interface " << mIfname;
return false;
}
}
return true;
}
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2022, 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.
*/
#pragma once
#include "CanBus.h"
namespace aidl::android::hardware::automotive::can {
class CanBusVirtual : public CanBus {
public:
CanBusVirtual(const std::string& ifname);
protected:
virtual Result preUp() override;
virtual bool postDown() override;
private:
bool mWasCreated = false;
};
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,333 @@
/*
* Copyright 2022, 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.
*/
#include "CanController.h"
#include "CanBusNative.h"
#include "CanBusSlcan.h"
#include "CanBusVirtual.h"
#include <android-base/format.h>
#include <android-base/logging.h>
#include <automotive/filesystem>
#include <fstream>
#include <regex>
namespace aidl::android::hardware::automotive::can {
namespace fs = ::android::hardware::automotive::filesystem;
namespace fsErrors {
static const std::error_code ok;
static const std::error_code eperm(EPERM, std::generic_category());
static const std::error_code enoent(ENOENT, std::generic_category());
static const std::error_code eacces(EACCES, std::generic_category());
} // namespace fsErrors
/* In the /sys/devices tree, there are files called "serial", which contain the serial numbers
* for various devices. The exact location inside of this directory is dependent upon the
* hardware we are running on, so we have to start from /sys/devices and work our way down. */
static const fs::path kDevPath("/sys/devices/");
static const std::regex kTtyRe("^tty[A-Z]+[0-9]+$");
static constexpr auto kOpts = ~(fs::directory_options::follow_directory_symlink |
fs::directory_options::skip_permission_denied);
constexpr auto ok = &ndk::ScopedAStatus::ok;
/**
* A helper object to associate the interface name and type of a USB to CAN adapter.
*/
struct UsbCanIface {
InterfaceType iftype;
std::string ifaceName;
};
static bool isValidName(const std::string& name) {
static const std::regex nameRE("^[a-zA-Z0-9_]{1,32}$");
return std::regex_match(name, nameRE);
}
/**
* Given a path, get the last element from it.
*
* \param itrPath - the path we want the last element of
* \return - the last element in the path (in string form).
*/
static std::string getLeaf(const fs::path& itrPath) {
/* end() returns an iterator one past the leaf of the path, so we've overshot
decrement (--) to go back one to the leaf
dereference and now we have our leaf. */
return *(--(itrPath.end()));
}
static ndk::ScopedAStatus resultToStatus(Result res, const std::string& msg = "") {
if (msg.empty()) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(static_cast<int>(res)));
}
return ndk::ScopedAStatus(
AStatus_fromServiceSpecificErrorWithMessage(static_cast<int>(res), msg.c_str()));
}
/**
* Given a UsbCanIface object, get the ifaceName given the serialPath.
*
* \param serialPath - Absolute path to a "serial" file for a given device in /sys.
* \return A populated UsbCanIface. On failure, nullopt is returned.
*/
static std::optional<UsbCanIface> getIfaceName(const fs::path& serialPath) {
std::error_code fsStatus;
// Since the path is to a file called "serial", we need to search its parent directory.
fs::recursive_directory_iterator fsItr(serialPath.parent_path(), kOpts, fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed to open " << serialPath.parent_path();
return std::nullopt;
}
for (; fsStatus == fsErrors::ok && fsItr != fs::recursive_directory_iterator();
fsItr.increment(fsStatus)) {
/* We want either a directory called "net" or a directory that looks like tty<something>, so
* skip files. */
bool isDir = fsItr->is_directory(fsStatus);
if (fsStatus != fsErrors::ok || !isDir) continue;
std::string currentDir = getLeaf(fsItr->path());
if (currentDir == "net") {
/* This device is a SocketCAN device. The iface name is the only directory under
* net/. Multiple directories under net/ is an error.*/
fs::directory_iterator netItr(fsItr->path(), kOpts, fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed to open " << fsItr->path() << " to get net name!";
return std::nullopt;
}
// The leaf of our path should be the interface name.
std::string netName = getLeaf(netItr->path());
// Check if there is more than one item in net/
netItr.increment(fsStatus);
if (fsStatus != fsErrors::ok) {
// It's possible we have a valid net name, but this is most likely an error.
LOG(ERROR) << "Failed to verify " << fsItr->path() << " has valid net name!";
return std::nullopt;
}
if (netItr != fs::directory_iterator()) {
// There should never be more than one name under net/
LOG(ERROR) << "Found more than one net name in " << fsItr->path() << "!";
return std::nullopt;
}
return {{InterfaceType::NATIVE, netName}};
} else if (std::regex_match(currentDir, kTtyRe)) {
// This device is a USB serial device, and currentDir is the tty name.
return {{InterfaceType::SLCAN, "/dev/" + currentDir}};
}
}
// check if the loop above exited due to a c++fs error.
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed search filesystem: " << fsStatus;
}
return std::nullopt;
}
/**
* A helper function to read the serial number from a "serial" file in /sys/devices/
*
* \param serialnoPath - path to the file to read.
* \return the serial number, or nullopt on failure.
*/
static std::optional<std::string> readSerialNo(const std::string& serialnoPath) {
std::ifstream serialnoStream(serialnoPath);
std::string serialno;
if (!serialnoStream.good()) {
LOG(ERROR) << "Failed to read serial number from " << serialnoPath;
return std::nullopt;
}
std::getline(serialnoStream, serialno);
return serialno;
}
/**
* Searches for USB devices found in /sys/devices/, and attempts to find a device matching the
* provided list of serial numbers.
*
* \param configSerialnos - a list of serial number (suffixes) from the HAL config.
* \param iftype - the type of the interface to be located.
* \return a matching USB device. On failure, std::nullopt is returned.
*/
static std::optional<UsbCanIface> findUsbDevice(const std::vector<std::string>& configSerialnos) {
std::error_code fsStatus;
fs::recursive_directory_iterator fsItr(kDevPath, kOpts, fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed to open " << kDevPath;
return std::nullopt;
}
for (; fsStatus == fsErrors::ok && fsItr != fs::recursive_directory_iterator();
fsItr.increment(fsStatus)) {
// We want to find a file called "serial", which is in a directory somewhere. Skip files.
bool isDir = fsItr->is_directory(fsStatus);
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Failed check if " << fsStatus;
return std::nullopt;
}
if (!isDir) continue;
auto serialnoPath = fsItr->path() / "serial";
bool isReg = fs::is_regular_file(serialnoPath, fsStatus);
/* Make sure we have permissions to this directory, ignore enoent, since the file
* "serial" may not exist, which is ok. */
if (fsStatus == fsErrors::eperm || fsStatus == fsErrors::eacces) {
/* This means we don't have access to this directory. If we recurse into it, this
* will cause the iterator to loose its state and we'll crash. */
fsItr.disable_recursion_pending();
continue;
}
if (fsStatus == fsErrors::enoent) continue;
if (fsStatus != fsErrors::ok) {
LOG(WARNING) << "An unexpected error occurred while checking for serialno: "
<< fsStatus;
continue;
}
if (!isReg) continue;
// we found a serial number
auto serialno = readSerialNo(serialnoPath);
if (!serialno.has_value()) continue;
// see if the serial number exists in the config
for (auto&& cfgSn : configSerialnos) {
if (serialno->ends_with(std::string(cfgSn))) {
auto ifaceInfo = getIfaceName(serialnoPath);
if (!ifaceInfo.has_value()) break;
return ifaceInfo;
}
}
}
if (fsStatus != fsErrors::ok) {
LOG(ERROR) << "Error searching filesystem: " << fsStatus;
return std::nullopt;
}
return std::nullopt;
}
ndk::ScopedAStatus CanController::getSupportedInterfaceTypes(
std::vector<InterfaceType>* supportedTypes) {
*supportedTypes = {InterfaceType::VIRTUAL, InterfaceType::NATIVE, InterfaceType::SLCAN};
return ok();
}
ndk::ScopedAStatus CanController::getInterfaceName(const std::string& busName,
std::string* ifaceName) {
*ifaceName = {};
if (mBusesByName.find(busName) == mBusesByName.end()) {
return resultToStatus(Result::BAD_BUS_NAME, fmt::format("{} doesn't exist", busName));
}
*ifaceName = std::string(mBusesByName[busName]->getIfaceName());
return ok();
}
ndk::ScopedAStatus CanController::upBus(const BusConfig& config, std::string* ifaceName) {
if (!isValidName(config.name)) {
LOG(ERROR) << "Bus name " << config.name << " is invalid";
return resultToStatus(Result::BAD_BUS_NAME,
fmt::format("{} is not a valid bus name", config.name));
} else if (mBusesByName.find(config.name) != mBusesByName.end()) {
LOG(ERROR) << "A bus named " << config.name << " already exists!";
return resultToStatus(Result::INVALID_STATE,
fmt::format("A bus named {} already exists", config.name));
}
if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::virtualif) {
auto& virtualif = config.interfaceId.get<BusConfig::InterfaceId::Tag::virtualif>();
mBusesByName[config.name] = std::make_unique<CanBusVirtual>(virtualif.ifname);
}
else if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::nativeif) {
auto& nativeif = config.interfaceId.get<BusConfig::InterfaceId::Tag::nativeif>();
std::string ifaceName;
if (nativeif.interfaceId.getTag() == NativeInterface::InterfaceId::Tag::serialno) {
// Configure by serial number.
auto selectedDevice = findUsbDevice(
nativeif.interfaceId.get<NativeInterface::InterfaceId::Tag::serialno>());
// verify the returned device is the correct one
if (!selectedDevice.has_value() || selectedDevice->iftype != InterfaceType::NATIVE) {
return resultToStatus(
Result::BAD_INTERFACE_ID,
"Couldn't find a native socketcan device with the given serial number(s)");
}
ifaceName = selectedDevice->ifaceName;
} else {
// configure by iface name.
ifaceName = nativeif.interfaceId.get<NativeInterface::InterfaceId::Tag::ifname>();
}
mBusesByName[config.name] = std::make_unique<CanBusNative>(ifaceName, config.bitrate);
}
else if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::slcan) {
auto& slcanif = config.interfaceId.get<BusConfig::InterfaceId::Tag::slcan>();
std::string ttyName;
if (slcanif.interfaceId.getTag() == SlcanInterface::InterfaceId::Tag::serialno) {
// Configure by serial number.
auto selectedDevice = findUsbDevice(
slcanif.interfaceId.get<SlcanInterface::InterfaceId::Tag::serialno>());
if (!selectedDevice.has_value() || selectedDevice->iftype != InterfaceType::SLCAN) {
return resultToStatus(
Result::BAD_INTERFACE_ID,
"Couldn't find a slcan device with the given serial number(s)");
}
ttyName = selectedDevice->ifaceName;
} else {
// Configure by tty name.
ttyName = slcanif.interfaceId.get<SlcanInterface::InterfaceId::Tag::ttyname>();
}
mBusesByName[config.name] = std::make_unique<CanBusSlcan>(ttyName, config.bitrate);
}
else if (config.interfaceId.getTag() == BusConfig::InterfaceId::Tag::indexed) {
return resultToStatus(Result::NOT_SUPPORTED,
"Indexed devices are not supported in this implementation");
} else {
// this shouldn't happen.
return resultToStatus(Result::UNKNOWN_ERROR, "Unknown interface id type");
}
Result result = mBusesByName[config.name]->up();
if (result != Result::OK) {
// the bus failed to come up, don't leave a broken entry in the map.
mBusesByName.erase(config.name);
return resultToStatus(result, fmt::format("CanBus::up failed for {}", config.name));
}
*ifaceName = mBusesByName[config.name]->getIfaceName();
return ok();
}
ndk::ScopedAStatus CanController::downBus(const std::string& busName) {
if (mBusesByName.find(busName) == mBusesByName.end()) {
return resultToStatus(
Result::UNKNOWN_ERROR,
fmt::format("Couldn't bring down {}, because it doesn't exist", busName));
}
Result result = mBusesByName[busName]->down();
if (result != Result::OK) {
return resultToStatus(result, fmt::format("Couldn't bring down {}!", busName));
}
mBusesByName.erase(busName);
return ok();
}
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2022, 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.
*/
#pragma once
#include <aidl/android/hardware/automotive/can/BnCanController.h>
#include "CanBus.h"
#include <aidl/android/hardware/automotive/can/Result.h>
#include <map>
#include <string>
namespace aidl::android::hardware::automotive::can {
class CanController : public BnCanController {
public:
ndk::ScopedAStatus getSupportedInterfaceTypes(
std::vector<InterfaceType>* supportedTypes) override;
ndk::ScopedAStatus getInterfaceName(const std::string& busName,
std::string* ifaceName) override;
ndk::ScopedAStatus upBus(const BusConfig& config, std::string* ifaceName) override;
ndk::ScopedAStatus downBus(const std::string& busName) override;
private:
std::map<std::string, std::unique_ptr<CanBus>> mBusesByName = {};
};
} // namespace aidl::android::hardware::automotive::can

View File

@@ -0,0 +1,5 @@
service android.hardware.automotive.can /vendor/bin/hw/android.hardware.automotive.can-service
class hal
capabilities NET_ADMIN
user vehicle_network
group system inet

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2022 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.
-->
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.automotive.can</name>
<version>1</version>
<interface>
<name>ICanController</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2022, 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.
*/
#include "CanController.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
namespace android::hardware::automotive::can {
using namespace std::string_literals;
using ::aidl::android::hardware::automotive::can::CanController;
extern "C" int main() {
base::SetDefaultTag("CanController");
base::SetMinimumLogSeverity(base::VERBOSE);
LOG(VERBOSE) << "Starting up...";
auto service = ndk::SharedRefBase::make<CanController>();
const auto instance = CanController::descriptor + "/default"s;
const auto status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
CHECK_EQ(status, STATUS_OK) << "Failed to add service " << instance;
LOG(VERBOSE) << "Started successfully!";
ABinderProcess_joinThreadPool();
LOG(FATAL) << "CanController exited unexpectedly!";
return EXIT_FAILURE;
}
} // namespace android::hardware::automotive::can

View File

@@ -0,0 +1,43 @@
//
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_binary {
name: "canhalconfigurator-aidl",
init_rc: ["canhalconfigurator-aidl.rc"],
defaults: ["android.hardware.automotive.can@defaults"],
srcs: [
"canhalconfigurator.cpp",
"canprototools.cpp",
],
shared_libs: [
"libbase",
"libbinder_ndk",
"libprotobuf-cpp-full",
],
static_libs: [
"android.hardware.automotive.can-V1-ndk",
"android.hardware.automotive.can-aidl-config-format",
],
}

View File

@@ -0,0 +1,3 @@
service canhalconfigurator /system/bin/canhalconfigurator-aidl
class core
oneshot

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2022 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.
*/
#include "canbus_config.pb.h"
#include "canprototools.h"
#include <aidl/android/hardware/automotive/can/ICanController.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <chrono>
#include <thread>
namespace android::hardware::automotive::can {
using namespace std::string_literals;
using ::aidl::android::hardware::automotive::can::ICanController;
static constexpr std::string_view kDefaultConfigPath = "/etc/canbus_config.pb";
/**
* Takes output from parsed protobuf config and uses it to configure the CAN HAL.
*
* \param pb_cfg is an instance of the autogenerated protobuf object for our configuration.
* \return boolean status, true on success, false on failure.
*/
static bool processPbCfg(const config::CanBusConfig& pb_cfg) {
for (auto const& bus : pb_cfg.buses()) {
if (bus.name().empty()) {
LOG(ERROR) << "Invalid config: Bus config must have a valid name field";
return false;
}
auto busCfgMaybe = config::fromPbBus(bus);
if (!busCfgMaybe.has_value()) {
return false;
}
auto busCfg = *busCfgMaybe;
const auto instance = ICanController::descriptor + "/default"s;
const auto service = ICanController::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str())));
if (service == nullptr) {
LOG(FATAL) << "Can't find CAN HAL! (has it started yet?)";
return false;
}
LOG(VERBOSE) << "Bringing up a " << busCfg.name << " @ " << busCfg.bitrate;
std::string ifaceName;
const auto status = service->upBus(busCfg, &ifaceName);
if (!status.isOk() && status.getExceptionCode() != EX_SERVICE_SPECIFIC) {
LOG(FATAL) << "Binder transaction failed!" << status.getStatus();
return false;
} else if (!status.isOk()) {
LOG(ERROR) << "upBus failed: " << config::resultStringFromStatus(status) << ": "
<< status.getMessage();
continue;
}
LOG(INFO) << bus.name() << " has been successfully configured on " << ifaceName;
}
return true;
}
/**
* This kicks off the CAN HAL configuration process. This starts the following:
* 1. Reading the config file
* 2. Setting up CAN buses
* 3. Handling services
* \param filepath is a string specifying the absolute path of the config file
* \return boolean status, true on success, false on failure
*/
static bool configuratorStart(const std::string& filepath) {
base::SetDefaultTag("CanConfigurator");
auto pbCfg = config::parseConfigFile(filepath);
if (!pbCfg.has_value()) {
return false;
}
// process the rest of the config file data and configure the CAN buses.
if (!processPbCfg(*pbCfg)) {
return false;
}
LOG(INFO) << "CAN HAL has been configured!";
return true;
}
extern "C" int main(int argc, char* argv[]) {
std::string configFilepath = static_cast<std::string>(kDefaultConfigPath);
// allow for CLI specification of a config file.
if (argc == 2) {
configFilepath = argv[1];
} else if (argc > 2) {
std::cerr << "usage: " << argv[0] << " [optional config filepath]";
return 1;
}
if (!configuratorStart(configFilepath)) {
return 1;
}
return 0;
}
} // namespace android::hardware::automotive::can

View File

@@ -0,0 +1,200 @@
/*
* Copyright (C) 2022 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.
*/
#include "canprototools.h"
#include <aidl/android/hardware/automotive/can/IndexedInterface.h>
#include <aidl/android/hardware/automotive/can/NativeInterface.h>
#include <aidl/android/hardware/automotive/can/SlcanInterface.h>
#include <aidl/android/hardware/automotive/can/VirtualInterface.h>
#include <android-base/logging.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <fstream>
namespace android::hardware::automotive::can::config {
using ::aidl::android::hardware::automotive::can::BusConfig;
using ::aidl::android::hardware::automotive::can::IndexedInterface;
using ::aidl::android::hardware::automotive::can::InterfaceType;
using ::aidl::android::hardware::automotive::can::NativeInterface;
using ::aidl::android::hardware::automotive::can::Result;
using ::aidl::android::hardware::automotive::can::SlcanInterface;
using ::aidl::android::hardware::automotive::can::VirtualInterface;
/**
* Helper function for parseConfigFile. readString is used to get the fist n characters (n) from an
* istream object (s) and return it as a string object.
*
* \param s istream of the file you intend to read.
* \param n streamsize object of the number of characters you'd like.
* \return optional string containing up to n characters from the stream(s) you provided.
*/
static std::optional<std::string> readString(std::istream& s, std::streamsize n) {
char buff[n];
auto got = s.read(buff, n).gcount();
if (!s.good() && !s.eof()) return std::nullopt;
return std::string(buff, 0, std::min(n, got));
}
std::optional<CanBusConfig> parseConfigFile(const std::string& filepath) {
std::ifstream cfg_stream(filepath);
// text headers that would be present in a plaintext proto config file.
static const std::array<std::string, 3> text_headers = {"buses", "#", "controller"};
auto cfg_file_snippet = readString(cfg_stream, 10);
if (!cfg_file_snippet.has_value()) {
LOG(ERROR) << "Can't open " << filepath << " for reading";
return std::nullopt;
}
cfg_stream.seekg(0);
// check if any of the textHeaders are at the start of the config file.
bool text_format = false;
for (auto const& header : text_headers) {
if (cfg_file_snippet->compare(0, header.length(), header) == 0) {
text_format = true;
break;
}
}
CanBusConfig config;
if (text_format) {
google::protobuf::io::IstreamInputStream pb_stream(&cfg_stream);
if (!google::protobuf::TextFormat::Parse(&pb_stream, &config)) {
LOG(ERROR) << "Failed to parse (text format) " << filepath;
return std::nullopt;
}
} else if (!config.ParseFromIstream(&cfg_stream)) {
LOG(ERROR) << "Failed to parse (binary format) " << filepath;
return std::nullopt;
}
return config;
}
std::optional<BusConfig> fromPbBus(const Bus& pb_bus) {
BusConfig bus_cfg = {};
bus_cfg.name = pb_bus.name();
switch (pb_bus.iface_type_case()) {
case Bus::kNative: {
const std::string ifname = pb_bus.native().ifname();
const std::vector<std::string> serials = {pb_bus.native().serialno().begin(),
pb_bus.native().serialno().end()};
if (ifname.empty() == serials.empty()) {
LOG(ERROR) << "Invalid config: native type bus must have an iface name xor a "
<< "serial number";
return std::nullopt;
}
bus_cfg.bitrate = pb_bus.bitrate();
NativeInterface nativeif = {};
if (!ifname.empty())
nativeif.interfaceId.set<NativeInterface::InterfaceId::Tag::ifname>(ifname);
if (!serials.empty())
nativeif.interfaceId.set<NativeInterface::InterfaceId::Tag::serialno>(serials);
bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::nativeif>(nativeif);
break;
}
case Bus::kSlcan: {
const std::string ttyname = pb_bus.slcan().ttyname();
const std::vector<std::string> serials = {pb_bus.slcan().serialno().begin(),
pb_bus.slcan().serialno().end()};
if (ttyname.empty() == serials.empty()) {
LOG(ERROR) << "Invalid config: slcan type bus must have a tty name xor a serial "
<< "number";
return std::nullopt;
}
bus_cfg.bitrate = pb_bus.bitrate();
SlcanInterface slcan = {};
if (!ttyname.empty())
slcan.interfaceId.set<SlcanInterface::InterfaceId::Tag::ttyname>(ttyname);
if (!serials.empty())
slcan.interfaceId.set<SlcanInterface::InterfaceId::Tag::serialno>(serials);
bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::slcan>(slcan);
break;
}
case Bus::kVirtual: {
// Theoretically, we could just create the next available vcan iface.
const std::string ifname = pb_bus.virtual_().ifname();
if (ifname.empty()) {
LOG(ERROR) << "Invalid config: native type bus must have an iface name";
return std::nullopt;
}
VirtualInterface virtualif = {};
virtualif.ifname = ifname;
bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::virtualif>(virtualif);
break;
}
case Bus::kIndexed: {
const uint8_t index = pb_bus.indexed().index();
if (index > UINT8_MAX) {
LOG(ERROR) << "Interface index out of range: " << index;
return std::nullopt;
}
IndexedInterface indexedif = {};
indexedif.index = index;
bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::indexed>(indexedif);
break;
}
default:
LOG(ERROR) << "Invalid config: bad interface type for " << bus_cfg.name;
return std::nullopt;
}
return bus_cfg;
}
std::optional<InterfaceType> getHalIftype(const Bus& pb_bus) {
switch (pb_bus.iface_type_case()) {
case Bus::kNative:
return InterfaceType::NATIVE;
case Bus::kSlcan:
return InterfaceType::SLCAN;
case Bus::kVirtual:
return InterfaceType::VIRTUAL;
case Bus::kIndexed:
return InterfaceType::INDEXED;
default:
return std::nullopt;
}
}
std::string resultStringFromStatus(const ndk::ScopedAStatus& status) {
const auto res = static_cast<Result>(status.getServiceSpecificError());
switch (res) {
case Result::OK:
return "OK";
case Result::UNKNOWN_ERROR:
return "UNKNOWN_ERROR";
case Result::INVALID_STATE:
return "INVALID_STATE";
case Result::NOT_SUPPORTED:
return "NOT_SUPPORTED";
case Result::BAD_INTERFACE_ID:
return "BAD_INTERFACE_ID";
case Result::BAD_BITRATE:
return "BAD_BITRATE";
case Result::BAD_BUS_NAME:
return "BAD_BUS_NAME";
case Result::INTERFACE_DOWN:
return "INTERFACE_DOWN";
default:
return "Invalid Result!";
}
}
} // namespace android::hardware::automotive::can::config

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2022 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.
*/
#pragma once
#include "canbus_config.pb.h"
#include <aidl/android/hardware/automotive/can/BusConfig.h>
#include <aidl/android/hardware/automotive/can/InterfaceType.h>
#include <aidl/android/hardware/automotive/can/Result.h>
namespace android::hardware::automotive::can::config {
/**
* This reads the protobuf config file into a protobuf object. Both text based protobuf files as
* well as binary format protobuf files are supported.
*
* \param filepath string containing the name of the config file to read.
* \return a CanBusConfig protobuf object constructed from the config file.
*/
std::optional<CanBusConfig> parseConfigFile(const std::string& filepath);
/**
* Converts protobuf format single-bus config object to a HAL bus config object.
*
* \param pb_bus is the protobuf object representing a the configuration of one CAN bus.
* \return a converted HAL bus config object.
*/
std::optional<::aidl::android::hardware::automotive::can::BusConfig> fromPbBus(const Bus& pb_bus);
/**
* Get the CAN HAL interface type specified by a given protobuf config object.
*
* \param pb_bus is the protobuf object representing a the configuration of one CAN bus.
* \return the CAN HAL interface type.
*/
std::optional<::aidl::android::hardware::automotive::can::InterfaceType> getHalIftype(
const Bus& pb_bus);
std::string resultStringFromStatus(const ndk::ScopedAStatus& status);
} // namespace android::hardware::automotive::can::config

View File

@@ -0,0 +1,37 @@
//
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_library_static {
name: "android.hardware.automotive.can-aidl-config-format",
defaults: ["android.hardware.automotive.can@defaults"],
proto: {
export_proto_headers: true,
type: "full",
},
strip: {
keep_symbols: true,
},
srcs: ["canbus_config.proto"],
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2022 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.
*/
syntax = "proto3";
package android.hardware.automotive.can.config;
message IfaceNative {
string ifname = 1;
repeated string serialno = 2;
};
message IfaceSlcan {
string ttyname = 1;
repeated string serialno = 2;
};
message IfaceVirtual {
string ifname = 1;
};
message IfaceIndexed {
uint32 index = 1;
};
message Bus {
string name = 1; // this is the name presented in the HAL
oneof iface_type {
IfaceNative native = 2;
IfaceSlcan slcan = 3;
IfaceVirtual virtual = 4;
IfaceIndexed indexed = 5;
}
uint32 bitrate = 6;
};
message CanBusConfig {
repeated Bus buses = 1;
};

View File

@@ -0,0 +1,51 @@
//
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "hardware_interfaces_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_test {
name: "VtsHalCanControllerV1_0Test",
defaults: [
"VtsHalTargetTestDefaults",
"android.hardware.automotive.can@defaults",
"use_libaidlvintf_gtest_helper_static",
],
cpp_std: "experimental",
srcs: [
"CanControllerAidlTest.cpp",
],
shared_libs: [
"libbase",
"libbinder_ndk",
],
static_libs: [
"android.hardware.automotive.can-V1-ndk",
"android.hardware.automotive.can@libnetdevice",
"libnl++",
"libgmock",
],
test_suites: [
"general-tests",
"vts",
],
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2022 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.
*/
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <aidl/android/hardware/automotive/can/BusConfig.h>
#include <aidl/android/hardware/automotive/can/ICanController.h>
#include <aidl/android/hardware/automotive/can/Result.h>
#include <aidl/android/hardware/automotive/can/VirtualInterface.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <gtest/gtest.h>
#include <libnetdevice/libnetdevice.h>
#include <libnl++/MessageFactory.h>
#include <libnl++/Socket.h>
#include <libnl++/printer.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <chrono>
#include <thread>
using aidl::android::hardware::automotive::can::BusConfig;
using aidl::android::hardware::automotive::can::ICanController;
using aidl::android::hardware::automotive::can::VirtualInterface;
using namespace std::chrono_literals;
using namespace std::string_literals;
class CanControllerAidlTest : public ::testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
android::base::SetDefaultTag("CAN_HAL_VTS");
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
const auto instance = ICanController::descriptor + "/default"s;
mCanControllerService = ICanController::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str())));
ASSERT_NE(mCanControllerService, nullptr);
}
virtual void TearDown() override {}
static bool mTestCaseInitialized;
std::shared_ptr<ICanController> mCanControllerService;
};
// we can't test a real bus, since we can't make any assumptions about hardware
// this checks upBus, getInterfaceName, and downBus
TEST_P(CanControllerAidlTest, ToggleBus) {
const std::string_view canIface = "vcan50";
const std::string busName = "VTS_CAN";
std::string upBusReturn; // should be vcan50
BusConfig config = {};
VirtualInterface iface = {};
iface.ifname = canIface;
config.interfaceId.set<BusConfig::InterfaceId::Tag::virtualif>(iface);
config.name = busName;
auto aidlStatus = mCanControllerService->upBus(config, &upBusReturn);
ASSERT_TRUE(aidlStatus.isOk());
EXPECT_EQ(upBusReturn, canIface);
std::string ifaceName;
aidlStatus = mCanControllerService->getInterfaceName(busName, &ifaceName);
ASSERT_TRUE(aidlStatus.isOk());
EXPECT_EQ(ifaceName, canIface);
aidlStatus = mCanControllerService->downBus(busName);
ASSERT_TRUE(aidlStatus.isOk());
}
TEST_P(CanControllerAidlTest, GetSupported) {
LOG(VERBOSE) << "Get the supported iface types";
std::vector<::aidl::android::hardware::automotive::can::InterfaceType> supportedTypes;
auto aidlStatus = mCanControllerService->getSupportedInterfaceTypes(&supportedTypes);
ASSERT_TRUE(aidlStatus.isOk());
EXPECT_FALSE(supportedTypes.empty());
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CanControllerAidlTest);
INSTANTIATE_TEST_SUITE_P(
PerInstance, CanControllerAidlTest,
testing::ValuesIn(android::getAidlHalInstanceNames(ICanController::descriptor)),
android::PrintInstanceNameToString);

View File

@@ -0,0 +1,3 @@
# Bug component: 533426
twasilczyk@google.com
chrisweir@google.com

View File

@@ -68,6 +68,14 @@
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.automotive.can</name>
<version>1</version>
<interface>
<name>ICanController</name>
<instance>default</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.automotive.can</name>
<version>1.0</version>