Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
.vscode
libs/*
47 changes: 36 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Whether you are an OEM company or a developer customizing the Android Open Sourc

The Android Software Development Kit (SDK) is a collection of libraries and tools that enables and makes it easier to develop Android applications. SDK Add-ons allow third-party actors to extend the Android SDK to add interfaces to their features without changing the Android SDK. According to the Android Compatibility Definition Document (CDD), OEMs are not allowed to change the Android public APIs - namely those in the protected namespaces: `java.*`, `javax.*`, `sun.*`, `android.*`, `com.android.*`. Therefore, by using add-ons, OEMs can add libraries in their namespaces, providing functionalities that can be exported without infringing the CDD. Having to build only the add-on is also a great advantage that might save a lot of development time.

In this post, we will build an SDK Add-on containing an example service - for which we will cover all the necessary config files. We will also learn how to connect an application to our example service using the add-on. Here, we are assuming you already have access to the Android source code and that you can build and deploy these changes. The code and configuration files were made with Android 10 (API level 30) in mind but they can be easily modifiable to work with other versions.
In this project, we will build an SDK Add-on containing example services - for which we will cover all the necessary config files. We will also learn how to connect an application to our example services using the add-on. Here, we are assuming you already have access to the Android source code and that you can build and deploy these changes. The code and configuration files were made with Android 14 (API level 34) in mind but they can be easily modifiable to work with other versions.

## Setting up the AOSP source tree

Expand All @@ -19,22 +19,30 @@ repo sync

### The content of this repository

This repository contains the code to build an emulator image containing the **hello-world-service**, the SDK add-on containing the **hello-world-service** service libraries that apps that use the **hello-world-service** will use on theirs build process, and a sample app that will use the SDK add-on. The summary of the code follows:
This repository contains the code to build an emulator image containing some services, the SDK add-on with stubs that allow apps to communicate with these services, and a sample app that will use the SDK add-on. First, let's see how the **hello-world-service** example is structured:

* `device/profusion/profusion_sdk_addon`: contains the configuration and manifest files for the SDK add-on. The `profusion_sdk_addon.mk` includes the **helloworld** product as part of this add-on;
* `pacakges/services/profusion/hello-world-service`: contains the service itself, that will be run in the emulator and serve the applications. The service will be part of the framework;
* `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
```

If you use VSCode, you can use [Run on Save](https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave) to automatically update the files with this script on save.

#### 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).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 Expand Up @@ -144,6 +152,8 @@ Should contain commands that the blueprint cannot support. Since Android 11, the
If you already have a service, then this is the interesting part:
You need a `.mk` file that defines the name and properties of your SDK Add-on, and several other files that define miscellaneous properties such as the API level being used, the revision version, and the libraries contained in the add-on. During the Android build, these files are bundled into a `.zip` containing the add-on to be distributed. In the next sections, I will go into further detail into each of the included files.

**PS**: Here, we will only show the files with the HelloWorld service. But if you look at these files in the source itself, you will see how to add more Services/Managers to the add-on.

##### `profusion_sdk_addon.mk`

```make
Expand Down Expand Up @@ -264,10 +274,11 @@ The SDK Add-on is all set. Now we only have to build it. Notice, again, that I'm
. build/envsetup.sh
export TARGET=profusion_sdk_addon
lunch sdk_car_x86_64-trunk_staging-eng
m profusion.hardware.dummy_car_info_hal-update-api # to freeze AIDL interfaces from HAL example
make sdk_addon
```

When the compilation succeeds, you will see two `.zip` files at `out/host/linux-x86/sdk_addon`. `profusion_sdk_addon-linux.zip` will contain the addon itself, which will be used to develop and compile our app. `profusion_sdk_addon-linux-img.zip` will contain a set of files to run the emulator outside the AOSP file tree.
When the compilation succeeds, you will see two `.zip` files at `out/host/linux-x86/sdk_addon`. `profusion_sdk_addon-linux.zip` will contain the addon itself, which will be used to develop and compile our app. `profusion_sdk_addon-linux-img.zip` will supposedly contain the files to run an emulator. We will see how to use it later.

### Building Android

Expand All @@ -279,6 +290,8 @@ lunch sdk_car_x86_64-trunk_staging-eng
m
```

**Tip**: Sometimes, you may want to build only one specific module (the hello-world-service, for example) to test small changes. To do this, run `mmm packages/hello-world-service`.

## Adding the SDK Add-on to Android Studio

Check the environment variable that points to Android's Sdk root.
Expand Down Expand Up @@ -415,20 +428,27 @@ emulator

### Outside the AOSP tree

If you use a remote server to build your addon (It could be a good idea. Building the AOSP right now could be a really really hard job for some hardware) you will probably want to extract the emulator to your local machine. Actually, that's very easy and you already have all you need.
If you use a remote server to build your addon (It could be a good idea. Building the AOSP right now could be a really really hard job for some hardware) you will probably want to extract the emulator to your local machine. The `profusion_sdk_addon-linux-img.zip` generated by the build process should contain the emulator image, but I had trouble running it. So, we will use another make command to generate the image:

Go to your Android Studio and create an emulator using the *Device Manager* tool. Since we builded to a Automotive TARGET, select an hardware under the _Automotive_ category as well. For the _system image_, select (or download first if you don't have it yet) the API 34 image.
```bash
make emu_img_zip
```

Before launching the emulator, extract the content of `profusion_sdk_addon-linux-img.zip` to the API 34 image folder:
This command will pack all the necessary files to create an emulator in a file called `sdk-repo-linux-system-images.zip` under the `out/target/product/emulator_car64_x86_64` folder. Copy this file to your local machine. This usually can be done with a `scp` command, like:

```bash
unzip out/host/linux-x86/sdk_addon/profusion_sdk_addon-linux-img.zip -d $ANDROID_SDK_ROOT/system-images/android-34-ext9/android-automotive
scp <remote-user>@<server-ip>:<aosp-path-on-remote>/out/target/product/emulator_car64_x86_64/sdk-repo-linux-system-images.zip <local-path-were-you-want-to-save>
```

---
Note: If you choose to build a different TARGET like `aosp_x86_64` or another, you will need to extract the content to a different path and the `profusion_sdk_addon-linux-img.zip` could not work as expected. In that case, you could use `make emu_img_zip` to generate a similar `sdk-repo-linux-system-images.zip` under `$PRODUCT_OUT` folder.
Go to your Android Studio and create an emulator using the *Device Manager* tool. Since we builded to a Automotive TARGET, select an hardware under the _Automotive_ category as well. For the _system image_, select (or download first if you don't have it yet) the API 34 image.

Before launching the emulator, extract the content of `sdk-repo-linux-system-images.zip` to the API 34 image folder:

```bash
unzip pathTo/sdk-repo-linux-system-images.zip -d $ANDROID_SDK_ROOT/system-images/android-34-ext9/android-automotive
```

---
And that's it! You can now run the emulator and test the features.

### Build and install the App

Expand Down Expand Up @@ -458,3 +478,8 @@ adb shell
```

![Android emulator running the helloworld app and helloword service](assets/running_app.png "Helloworld App")

### Next steps

Now you know how to build an SDK Add-on with a simple hello world service example. You can use this to develop your service!
Also, I **highly recommend** you read the [HAL Documentation](doc/HAL.md). There, you will find and robust example of how to develop a Hardware Abstraction Layer service and use it through the SDK Add-on using Managers, removing the Service binding responsibility from the application.
8 changes: 7 additions & 1 deletion add_to_aosp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ rsync -a --inplace "$GIT_ROOT"/target/userdata.img "$AOSP"/device/generic/goldfi
echo "Copying profusion sdk addon to device"
rsync -a --inplace "$GIT_ROOT"/device "$AOSP"

echo "Copying helloworld service to the framework"
echo "Copying services to the framework"
rsync -a --inplace "$GIT_ROOT"/packages "$AOSP"

echo "Copying HALs to the hardware"
rsync -a --inplace "$GIT_ROOT"/hardware "$AOSP"

mkdir -p "$AOSP"/out/target/product/emulator_car64_x86_64/data

echo "Copying COMMONAPI libs"
rsync -a --exclude '.git' "$GIT_ROOT"/libs/ "$AOSP"/external

date '+%Y/%m/%d %H:%M:%S'
2 changes: 2 additions & 0 deletions app/HelloWorldApp/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
android:theme="@style/Theme.HelloWorldApp">
<uses-library android:name="helloworld"
android:required="true" />
<uses-library android:name="DummyCarInfoManager"
android:required="true" />
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.helloworldapp.ui.theme.HelloWorldAppTheme
import com.profusion.helloworld.IHelloWorldService
import com.profusion.dummyCarInfo.DummyCarInfoManager

class MainActivity : ComponentActivity() {
companion object {
Expand All @@ -33,31 +39,48 @@ class MainActivity : ComponentActivity() {
}

private var helloWorldService: IHelloWorldService? = null
private var dummyCarInfoManager: DummyCarInfoManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
var carInfoState by remember { mutableStateOf("") }

HelloWorldAppTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
MainContent(
modifier = Modifier.padding(innerPadding),
onButtonClick = {
onHelloWorldButtonClick = {
helloWorldService?.printHelloWorld()
}
},
onCarInfoButtonClick = {
carInfoState = try {
dummyCarInfoManager!!.carInfo
} catch (e: Exception) {
"Error: ${e.message}"
}
},
carInfoState = carInfoState,
)
}
}
}
}

override fun onDestroy() {
super.onDestroy()
dummyCarInfoManager?.unbindService();
}

override fun onStart() {
super.onStart()
dummyCarInfoManager = DummyCarInfoManager(this)
val intent = Intent()
intent.component = ComponentName(HELLO_WORLD_SERVICE_PACKAGE, HELLO_WORLD_SERVICE)
try {
this.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
Log.e(TAG, "Unable to bind HelloWorldService");
Log.e(TAG, "Unable to bind");
e.printStackTrace()
}
}
Expand All @@ -74,19 +97,37 @@ class MainActivity : ComponentActivity() {
}

@Composable
fun MainContent(modifier: Modifier = Modifier, onButtonClick: () -> Unit) {
fun MainContent(
modifier: Modifier = Modifier,
onHelloWorldButtonClick: () -> Unit,
onCarInfoButtonClick: () -> Unit,
carInfoState: String,
) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(
onClick = onButtonClick,
onClick = onHelloWorldButtonClick,
) {
Text(
stringResource(R.string.helloWorldButtonText),
fontSize = 32.sp,
)
}
Button(
modifier = Modifier.padding(vertical = 16.dp),
onClick = onCarInfoButtonClick,
) {
Text(
stringResource(R.string.carInfoButtonText),
fontSize = 32.sp,
)
}
Text(
text = carInfoState,
fontSize = 32.sp,
)
}
}
}
1 change: 1 addition & 0 deletions app/HelloWorldApp/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<resources>
<string name="app_name">HelloWorldApp</string>
<string name="helloWorldButtonText">Tap to call HelloWorldService!</string>
<string name="carInfoButtonText">Tap to call DummyCarInfoManager!</string>
</resources>
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.
Binary file added assets/running_app_hal.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions device/profusion/common/profusion-packages.mk
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# helloworld
include packages/services/profusion/hello-world-service/helloworld-service.mk
include packages/services/profusion/dummy-car-info-service/dummyCarInfo-service.mk
3 changes: 2 additions & 1 deletion device/profusion/profusion_sdk_addon/manifest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ api=34

revision=1

libraries=helloworld
libraries=helloworld; DummyCarInfoManager

helloworld=helloworld.jar
DummyCarInfoManager=DummyCarInfoManager.jar
9 changes: 6 additions & 3 deletions device/profusion/profusion_sdk_addon/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
<display>ProfusionAddOn</display>
</tag>
<libraries>
<library localJarPath="helloworld.jar" name="helloworld">
<description>Hello World Library</description>
</library>
<library localJarPath="helloworld.jar" name="helloworld">
<description>Hello World Library</description>
</library>
<library localJarPath="DummyCarInfoManager.jar" name="DummyCarInfoManager">
<description>Dummy Car Info Library</description>
</library>
</libraries>
</type-details>
<revision>
Expand Down
17 changes: 15 additions & 2 deletions device/profusion/profusion_sdk_addon/profusion_sdk_addon.mk
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ PRODUCT_SDK_ADDON_NAME := profusion_sdk_addon
INTERNAL_SDK_HOST_OS_NAME := $(HOST_OS)

PRODUCT_PACKAGES := \
helloworld
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.
PRODUCT_SDK_ADDON_COPY_FILES := \
Expand All @@ -22,10 +28,17 @@ PRODUCT_SDK_ADDON_COPY_FILES := \
# Define the IMAGE PROPERTY (emulator related, but needed to build)
PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP := $(LOCAL_PATH)/source.properties

BOARD_SEPOLICY_DIRS += \
device/profusion/sepolicy/daemon \
device/profusion/sepolicy/interface

DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE += \
hardware/implementations/dummy_car_info_hal/default/compatibility_matrix.xml

# Copy the jar files for the optional libraries that are exposed as APIs.
PRODUCT_SDK_ADDON_COPY_MODULES := \
helloworld:libs/helloworld.jar
helloworld:libs/helloworld.jar \
DummyCarInfoManager:libs/DummyCarInfoManager.jar

# Rules for public APIs
PRODUCT_SDK_ADDON_STUB_DEFS := $(LOCAL_PATH)/sdk_addon_stub_defs.txt
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
+com.profusion.helloworld.*
+com.profusion.dummyCarInfo.*
13 changes: 13 additions & 0 deletions device/profusion/sepolicy/daemon/dummy.te
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
type dummy_car_info_hal, domain;
type dummy_car_info_hal_exec, exec_type, file_type, vendor_file_type;

init_daemon_domain(dummy_car_info_hal)

binder_call(dummy_car_info_hal, hwservicemanager)

allow dummy_car_info_hal servicemanager:binder { call transfer };

allow system_app dummy_car_info_hal:binder { call };
allow platform_app dummy_car_info_hal:binder { call };

domain_auto_trans(init, dummy_car_info_hal_exec, dummy_car_info_hal)
2 changes: 2 additions & 0 deletions device/profusion/sepolicy/daemon/file_contexts
Original file line number Diff line number Diff line change
@@ -0,0 +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 };
4 changes: 4 additions & 0 deletions device/profusion/sepolicy/interface/service.te
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type dummy_server_service, hal_service_type, protected_service, service_manager_type;

allow dummy_car_info_hal dummy_server_service:service_manager add;
allow platform_app dummy_server_service:service_manager find;
1 change: 1 addition & 0 deletions device/profusion/sepolicy/interface/service_contexts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
profusion.hardware.dummy_car_info_hal.IDummyCarInfoHAL/default u:object_r:dummy_server_service:s0
Loading