Skip to content

Commit

Permalink
feat: create SOME/IP provider service
Browse files Browse the repository at this point in the history
- Create a service based on https://github.com/COVESA/test-someip-service
and respective config files;
- Also, add a .md with simple instructions on how to generate CommonAPI
files and run the service inside the emulator;
  • Loading branch information
MaironLucas committed Jan 28, 2025
1 parent e28b360 commit 7519854
Show file tree
Hide file tree
Showing 21 changed files with 1,205 additions and 6 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ This repository contains the code to build an emulator image containing some ser
* `target`: contains files related to the emulator that will be created. Instead of creating a new device, we just modified the `sdk_car_x86_64.mk` and `car_generic_system.mk` to include our add-on.


We supply a shell script `add_to_aosp.sh` to automatically copy all code to the correct place in the AOSP source tree. Pass to the script the path to where the AOSP repo is:
We supply two different shell scripts. The first one, `fetch_someip_libs.sh`, is responsible for cloning all the necessary libraries to work with SOME/IP and CommonAPI. You only need to run this script once. It's also necessary to generate the files of some_ip_hal example to compile the project. So clone the libraries with the script below and run the commands described at [Generating CommonAPI Files](doc/SOME-IP-HAL.md#generating-commonapi-files) (Don't worry about understanding the whole context of SOME/IP right now, just run the commands in this topic for now).

```bash
./fetch_someip_libs.sh
```

The second one called `add_to_aosp.sh` will automatically copy all code to the correct place in the AOSP source tree. Run these two scripts one after the other, changing `/pathTo/aosp` for the path to your AOSP source tree:

```bash
./add_to_aosp.sh /pathTo/aosp
Expand All @@ -36,7 +42,7 @@ If you use VSCode, you can use [Run on Save](https://marketplace.visualstudio.co

#### Sample HAL implementation

This repository also contains an example of how to develop a HAL and use it through the SDK-Addon. First, focus on how the **hello-world-service** example is described here. Then, take a look at the [HAL Documentation](doc/HAL.md).
This repository also contains an example of how to develop a HAL and use it through the SDK-Addon. First, focus on how the **hello-world-service** example is described here. Then, take a look at the [HAL Documentation](doc/HAL.md).And then, if you are comfortable going further, take a look at the [HAL using SOME/IP](doc/SOME-IP-HAL.md), which is a more complex example involving a HAL that uses SOME/IP to communicate with a provider service.

## Hello World System Service

Expand Down
Binary file added assets/playground_service_log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions device/profusion/profusion_sdk_addon/profusion_sdk_addon.mk
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ PRODUCT_SDK_ADDON_NAME := profusion_sdk_addon
INTERNAL_SDK_HOST_OS_NAME := $(HOST_OS)

PRODUCT_PACKAGES := \
libvsomeip \
libvsomeip_cfg \
libvsomeip_sd \
libvsomeip_e2e \
libvsomeip3 \
libCommonAPI \
libCommonAPI-SomeIP \
helloworld \
profusion.hardware.dummy_car_info_hal-service \
some_ip_playground-service \
DummyCarInfoManager

# Copy the manifest and hardware files for the SDK add-on.
Expand Down
1 change: 1 addition & 0 deletions device/profusion/sepolicy/daemon/file_contexts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/vendor/bin/hw/profusion.hardware.dummy_car_info_hal-service u:object_r:dummy_car_info_hal_exec:s0
/vendor/bin/some_ip_playground-service u:object_r:some_ip_playground-service_exec:s0
12 changes: 12 additions & 0 deletions device/profusion/sepolicy/daemon/someip.te
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type some_ip_playground-service, domain;
type some_ip_playground-service_exec, exec_type, file_type, vendor_file_type;

init_daemon_domain(some_ip_playground-service)

allow some_ip_playground-service sysfs:file { open read };
allow some_ip_playground-service vendor_data_file:dir { add_name remove_name write };
allow some_ip_playground-service vendor_data_file:file { create open read write lock };
allow some_ip_playground-service vendor_data_file:sock_file { create setattr read write unlink};
allow some_ip_playground-service self:netlink_route_socket { create bind shutdown nlmsg_read nlmsg_readpriv read write };
allow some_ip_playground-service self:tcp_socket create;
allow some_ip_playground-service self:udp_socket { create ioctl };
105 changes: 105 additions & 0 deletions doc/SOME-IP-HAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# HAL with SOME/IP and CommonAPI
The idea is that at some moment this repository also contains an implementation of a HAL that uses SOME/IP (through CommonAPI). The `hardware/implementations/some_ip_hal` directory already has some files related to the PlaygroundService, which will provide some mocked data through SOME/IP.
This PlaygroundService module is based on the implementation that can be found at [test-someip-service](https://github.com/COVESA/test-someip-service).

## About version

In this implementation, we use `capicxx-someip-runtime`, `capicxx-core-runtime`, and respective generators at version `3.2.0`. This version was chosen because we faced some problems when trying to compile the most recent versions, due to the vsomeip that comes with AOSP on API 34 don't have the rule to make `libsomeip3_dlt` and `libboost_log`, which are dependencies required by versions `<3.2.3`.

## CommonAPI Files
At this documentation, I will not go deeper into CommonAPI framework concepts. If you want to know more about it, please refer to the [CommonAPI documentation](https://covesa.github.io/capicxx-core-tools/).

#### `hardware/implementation/some_ip_hal/franca`
This directory contains the Franca files that will be used to generate the CommonAPI files. The `.fdepl` and `.fidl` files here are strictly the same as the ones used in [test-someip-service](https://github.com/COVESA/test-someip-service). These files define how CommonAPI should generate files to provide information about a vehicle, such as speed, gear, fuel level, etc.

#### `hardware/implementation/some_ip_hal/default/playground_service/mock`
This directory contains the mock values that will be used by the PlaygroundService to provide information about the vehicle.

#### `hardware/implementation/some_ip_hal/default/include`
This directory will contain the generated CommonAPI files. Commands to generate these files are shown below.

#### `hardware/implementation/some_ip_hal/default/playground_service/PlaygroundStubImpl.cpp` and `hardware/implementation/some_ip_hal/default/playground_service/PlaygroundStubImpl.hpp`
These files contain the implementation to `PlaygroundStubDefault`, which is basically the class that will provide the information about the vehicle to the PlaygroundService. Note that in `PlaygroundStubImpl.hpp` all the attributes come from the `Mock` class.

#### `hardware/implementation/some_ip_hal/default/playground_service/PlaygroundService.cpp`
```cpp
...
using namespace std;

int main() {
std::cout << "SOMEIPPlayground: Successfully Registered Service!" << std::endl;
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::shared_ptr<PlaygroundStubImpl> playgroundService =
std::make_shared<PlaygroundStubImpl>();
runtime->registerService("local", "1", playgroundService);
std::cout << "SOMEIPPlayground: Successfully Registered Service!" << std::endl;

while (true) {
playgroundService->updateTankVolume();
playgroundService->monitorTankLevel();
std::cout << "SOMEIPPlayground: Waiting for calls..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return 0;
}
```
This file contains the main function of the PlaygroundService. The principal responsibility of this file is to register a service using the `PlaygroundStubImpl`. Also, this main function will loop indefinitely simulating the vehicle use, and updating the tank volume.
### Generating CommonAPI Files
Since you already cloned the necessary libs using `fetch_someip_libs.sh`, generate CommonAPI is very simple. To generate core files run:
```bash
./libs/someip-generators/commonapi_core_generator/commonapi-core-generator-linux-x86 -nv -sk \
--dest "hardware/implementations/some_ip_hal/default/include" \
hardware/implementations/some_ip_hal/franca/instances/org.genivi.vehicle.playground.fdepl
```

And to generate SOME/IP files run:

```bash
./libs/someip-generators/commonapi_someip_generator/commonapi-someip-generator-linux-x86 -nv \
--dest "hardware/implementations/some_ip_hal/default/include" \
hardware/implementations/some_ip_hal/franca/instances/org.genivi.vehicle.playground.fdepl
```

### Its necessary to start the service manualy?
No, the service will be started automatically through the `some_ip_playground-init.rc` file at Android boot:

```
service some_ip_playground-service /vendor/bin/some_ip_playground-service
class main
user none
group none
disabled
setenv VSOMEIP_APPLICATION_NAME playground-service
setenv VSOMEIP_CONFIGURATION /vendor/etc/some_ip_playground/vsomeip.json
setenv VSOMEIP_BASE_PATH /data/vendor/vsomeip/
on property:sys.boot_completed=1
mkdir /data/vendor/vsomeip 0771 none none
start some_ip_playground-service
```

This file defines the necessary environment variables to start the service and also starts the service when the property `sys.boot_completed` is set to 1. Another important part is to create the directory `/data/vendor/vsomeip` where the vsomeip files will be located. This path will also be important when starting our HAL.

## Verify if Playground Service is running

To verify if the Playground Service is running, you can check the logcat output. With the emulator running, open a terminal and run this command:

```bash
adb shell logcat | grep some_ip
```

If the output is something like that, everything is working fine:

![Logcat of some_ip](../assets/playground_service_log.png "Logcat of some_ip")

## Next steps

The next step is to implement the HAL that will communicate with the Playground Service. Probably, the first step is to create a class responsible for instantiating and handling the communication with `PlaygroundSomeIPProxy`, as it will be more sophisticated than handling everything in the HAL.

About the HAL, I would say that this implementation could be a good opportunity to understand how to communicate between application and the HAL using callbacks since it's a common pattern in Android development. Maybe register a callback to receive the car information updated by a loop in the Playground Service or something like that.

Another thing that is not exactly clear to me is if we should expose the AIDL interface from HAL in the sdk-addon to get a return of types and not only strings from the Manager or if we should create a new AIDL interface under the `packages` directory and map between the two interfaces. I think that this is an important point to discuss and understand the best approach.
1 change: 1 addition & 0 deletions hardware/implementations/some_ip_hal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default/include/*
55 changes: 55 additions & 0 deletions hardware/implementations/some_ip_hal/default/Android.bp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
cc_defaults {
name: "some_ip_playground-defaults",
vendor: true,
rtti: true,
cppflags: [
"-fexceptions",
"-Wall",
"-Werror",
"-Wno-ignored-attributes",
"-Wno-unused-parameter",
"-Wno-overloaded-virtual",
"-DCOMMONAPI_INTERNAL_COMPILATION",
],

shared_libs: [
"libvsomeip3-cfg",
"libvsomeip3",
"libCommonAPI",
"libCommonAPI-SomeIP",
"libbase",
],
}

runtime_required = [
"some_ip_playground-vsomeip.json",
]

prebuilt_etc {
name: "some_ip_playground-vsomeip.json",
vendor: true,
sub_dir: "some_ip_playground",
src: "vsomeip.json",
filename:"vsomeip.json"
}

cc_binary {
name: "some_ip_playground-service",
defaults: [
"some_ip_playground-defaults",
],
init_rc: ["playground_service/some_ip_playground-init.rc"],
local_include_dirs: [
"playground_service/mock",
"include",
],
srcs: [
"playground_service/PlaygroundService.cpp",
"playground_service/PlaygroundStubImpl.cpp",
"include/v1/org/genivi/vehicle/playground/PlaygroundSomeIPDeployment.cpp",
"include/v1/org/genivi/vehicle/playground/PlaygroundSomeIPStubAdapter.cpp",
"playground_service/mock/MockedAttributes.cpp",
],
required: runtime_required,
vendor: true,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2021, Bayerische Motoren Werke Aktiengesellschaft (BMW AG),
* Author: Alexander Domin ([email protected])
* Copyright (C) 2021, ProFUSION Sistemas e Soluções LTDA,
* Author: Leandro Ferlin ([email protected])
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the
* Mozilla Public License, v. 2.0. If a copy of the MPL was
* not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*/

#include <iostream>
#include <thread>
#include <CommonAPI/CommonAPI.hpp>
#include "PlaygroundStubImpl.hpp"

using namespace std;

int main() {
std::cout << "SOMEIPPlayground: Successfully Registered Service!" << std::endl;
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::shared_ptr<PlaygroundStubImpl> playgroundService =
std::make_shared<PlaygroundStubImpl>();
runtime->registerService("local", "1", playgroundService);
std::cout << "SOMEIPPlayground: Successfully Registered Service!" << std::endl;

while (true) {
playgroundService->updateTankVolume();
playgroundService->monitorTankLevel();
std::cout << "SOMEIPPlayground: Waiting for calls..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return 0;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2021, Bayerische Motoren Werke Aktiengesellschaft (BMW AG),
* Author: Alexander Domin ([email protected])
* Copyright (C) 2021, ProFUSION Sistemas e Soluções LTDA,
* Author: Leandro Ferlin ([email protected])
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the
* Mozilla Public License, v. 2.0. If a copy of the MPL was
* not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*/

#include "PlaygroundStubImpl.hpp"
#include "mock/MockedAttributes.hpp"
#include <cstdint>
#include <csignal>
#include <iostream>
#include <iterator>
#include <stdexcept>
#include <sys/types.h>

typedef v1_0::org::genivi::vehicle::playground::PlaygroundStubDefault
PlaygroundStubDefault;

PlaygroundStubImpl::PlaygroundStubImpl()
: consumption{}, capacity{}, volume{}, engineSpeed{}, currentGear{},
isReverseGearOn{}, drivePowerTransmission{}, doorsOpeningStatus{},
seatHeatingStatus{}, seatHeatingLevel{} {
initializeAttributes();
}
PlaygroundStubImpl::~PlaygroundStubImpl() {}

void PlaygroundStubImpl::initializeAttributes() {
PlaygroundStubDefault::setConsumptionAttribute(consumption.getValue());
PlaygroundStubDefault::setCapacityAttribute(capacity.getValue());
PlaygroundStubDefault::setVolumeAttribute(volume.getValue());
PlaygroundStubDefault::setEngineSpeedAttribute(engineSpeed.getValue());
PlaygroundStubDefault::setCurrentGearAttribute(currentGear.getValue());
PlaygroundStubDefault::setIsReverseGearOnAttribute(
isReverseGearOn.getValue());
PlaygroundStubDefault::setDrivePowerTransmissionAttribute(
drivePowerTransmission.getValue());
PlaygroundStubDefault::setDoorsOpeningStatusAttribute(
doorsOpeningStatus.getValue());
PlaygroundStubDefault::setSeatHeatingStatusAttribute(
seatHeatingStatus.getValue());
PlaygroundStubDefault::setSeatHeatingLevelAttribute(
seatHeatingLevel.getValue());
}

void PlaygroundStubImpl::updateTankVolume() {
const float currentVolume = PlaygroundStubDefault::getVolumeAttribute();
float updatedVolume;
if (currentVolume > 0.0) {
updatedVolume = currentVolume - 0.1;
} else {
updatedVolume = capacity.getCapacityInLiters();
}
PlaygroundStubDefault::setVolumeAttribute(updatedVolume);
}

void PlaygroundStubImpl::monitorTankLevel() {
const double capacityInLiters = capacity.getCapacityInLiters();
const int currentVolume = PlaygroundStubDefault::getVolumeAttribute();

const uint8_t &level = (uint8_t)(100 * currentVolume / capacityInLiters);
fireCurrentTankVolumeEvent(level);
}

void PlaygroundStubImpl::changeDoorsState(
const std::shared_ptr<CommonAPI::ClientId> _client,
CarDoorsCommand _commands, changeDoorsStateReply_t _reply) {

DoorsStatus lockedDoorsStatus =
PlaygroundStubDefault::getDoorsOpeningStatusAttribute();

const bool &currentFrontLeftState = lockedDoorsStatus.getFrontLeft();
const bool &currentFrontRightState = lockedDoorsStatus.getFrontRight();
const bool &currentRearLeftState = lockedDoorsStatus.getRearLeft();
const bool &currentRearRightState = lockedDoorsStatus.getRearRight();

const DoorCommand &frontLeftCommand = _commands.getFrontLeftDoor();
const DoorCommand &frontRightCommand = _commands.getFrontRightDoor();
const DoorCommand &rearLeftCommand = _commands.getRearLeftDoor();
const DoorCommand &rearRightCommand = _commands.getRearRightDoor();

const bool &nextFrontLeftState = doorsOpeningStatus.getNextStateFromCommand(
currentFrontLeftState, frontLeftCommand);
const bool &nextFrontRightState = doorsOpeningStatus.getNextStateFromCommand(
currentFrontRightState, frontRightCommand);
const bool &nextRearLeftState = doorsOpeningStatus.getNextStateFromCommand(
currentRearLeftState, rearLeftCommand);
const bool &nextRearRightState = doorsOpeningStatus.getNextStateFromCommand(
currentRearRightState, rearRightCommand);

const DoorsStatus &doorsStatus =
DoorsStatus(nextFrontLeftState, nextFrontRightState, nextRearLeftState,
nextRearRightState);

PlaygroundStubDefault::setDoorsOpeningStatusAttribute(doorsStatus);
_reply();
};
Loading

0 comments on commit 7519854

Please sign in to comment.