diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..e654877 --- /dev/null +++ b/Android.bp @@ -0,0 +1,6 @@ +// Copyright (C) 2021-2022 KonstaKANG +// +// SPDX-License-Identifier: Apache-2.0 + +soong_namespace { +} diff --git a/AndroidProducts.mk b/AndroidProducts.mk new file mode 100644 index 0000000..829137b --- /dev/null +++ b/AndroidProducts.mk @@ -0,0 +1,13 @@ +# +# Copyright (C) 2021-2023 KonstaKANG +# Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +# Copyright (C) 2026 Brendan Szymanski +# +# SPDX-License-Identifier: Apache-2.0 +# + +PRODUCT_MAKEFILES := \ + $(LOCAL_DIR)/lineage_orangepi5ultra.mk + +COMMON_LUNCH_CHOICES := \ + lineage_orangepi5ultra-userdebug diff --git a/BoardConfig.mk b/BoardConfig.mk new file mode 100644 index 0000000..364a169 --- /dev/null +++ b/BoardConfig.mk @@ -0,0 +1,84 @@ +# +# Copyright (C) 2021-2022 KonstaKANG +# Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +# +# SPDX-License-Identifier: Apache-2.0 +# + +DEVICE_PATH := device/rockchip/orangepi5ultra + + +TARGET_ARCH := arm64 +TARGET_ARCH_VARIANT := armv8-a +TARGET_CPU_ABI := arm64-v8a +TARGET_CPU_ABI2 := +TARGET_CPU_VARIANT := generic +TARGET_CPU_VARIANT_RUNTIME := cortex-a76 + + + +# Bluetooth +BOARD_HAVE_BLUETOOTH := true + +# Bootloader +TARGET_NO_BOOTLOADER := true + + +# Display +TARGET_SCREEN_DENSITY := 240 + + + + +# Kernel +BOARD_CUSTOM_BOOTIMG := true +BOARD_CUSTOM_BOOTIMG_MK := $(DEVICE_PATH)/mkbootimg.mk +BOARD_KERNEL_CMDLINE := console=ttyS2,1500000 no_console_suspend root=/dev/ram0 rootwait androidboot.hardware=opi5ultra + +# Manifest +DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE := $(DEVICE_PATH)/framework_compatibility_matrix.xml +DEVICE_MANIFEST_FILE := $(DEVICE_PATH)/manifest.xml +PRODUCT_MANIFEST_FILES := $(DEVICE_PATH)/product_manifest.xml + + +# Partition sizes +BOARD_FLASH_BLOCK_SIZE := 4096 +BOARD_USES_METADATA_PARTITION := true +BOARD_BOOTIMAGE_PARTITION_SIZE := 134217728 # 128M +BOARD_METADATAIMAGE_PARTITION_SIZE := 16777216 # 16M +BOARD_SYSTEMIMAGE_PARTITION_SIZE := 3221225472 # 3072M +BOARD_USERDATAIMAGE_PARTITION_SIZE := 134217728 # 128M +BOARD_VENDORIMAGE_PARTITION_SIZE := 402653184 # 384M +BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4 +TARGET_USERIMAGES_SPARSE_EXT_DISABLED := true +TARGET_USERIMAGES_USE_EXT4 := true + + + +# Platform +TARGET_BOARD_PLATFORM := rk3588 + +TARGET_BOOTLOADER_BOARD_NAME := orangepi5ultra + +# Properties +TARGET_VENDOR_PROP += $(DEVICE_PATH)/vendor.prop + +# Recovery +TARGET_NO_RECOVERY := true + +# SELinux +BOARD_SEPOLICY_DIRS += device/rockchip/orangepi5ultra/sepolicy +BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive + +# Treble +TARGET_COPY_OUT_VENDOR := vendor + + +# Wifi +BOARD_WLAN_DEVICE := bcmdhd +BOARD_HOSTAPD_DRIVER := NL80211 +BOARD_HOSTAPD_PRIVATE_LIB := lib_driver_cmd_$(BOARD_WLAN_DEVICE) +BOARD_WPA_SUPPLICANT_DRIVER := NL80211 +BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_$(BOARD_WLAN_DEVICE) +#WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY := true +WPA_SUPPLICANT_VERSION := VER_0_8_X diff --git a/README.md b/README.md index daa0130..6daf288 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ -# android_device_lineage-rockchip_orangepi5ultra +AOSP 16 device configuration for Orangepi 5 Pro. + +Ported from KonstaKang's raspberry vanilla aosp 16 project. + +Not working - + +Camera +3.5 mm audio + +Working - + +everything else including vulkan. + +This project can be ported to any device which utilises Rockchip SoC with minor changes. + +any doubts, feel free to mail me at vdarbha0473@gmail.com diff --git a/audio/Android.bp b/audio/Android.bp new file mode 100644 index 0000000..08d8d36 --- /dev/null +++ b/audio/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2015 The Android Open Source Project +// Copyright (C) 2021-2023 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_library_shared { + name: "audio.primary.opi", + relative_install_path: "hw", + vendor: true, + srcs: ["audio_hw.c"], + include_dirs: [ + "external/expat/lib", + "external/tinyalsa/include", + "system/media/audio_effects/include", + "system/media/audio_utils/include", + ], + header_libs: ["libhardware_headers"], + shared_libs: [ + "libcutils", + "liblog", + "libtinyalsa", + ], + cflags: ["-Wno-unused-parameter"], +} + +cc_library_shared { + name: "audio.primary.opi_hdmi", + relative_install_path: "hw", + vendor: true, + srcs: ["audio_hw_hdmi.c"], + include_dirs: [ + "external/expat/lib", + "system/media/audio_effects/include", + "system/media/audio_utils/include", + ], + header_libs: ["libhardware_headers"], + shared_libs: [ + "libcutils", + "liblog", + "libasound", + ], + cflags: ["-Wno-unused-parameter"], +} diff --git a/audio/audio_hw.c b/audio/audio_hw.c new file mode 100644 index 0000000..ee20ddb --- /dev/null +++ b/audio/audio_hw.c @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2021-2022 KonstaKANG + * Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha + * + * + * 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. + */ + +#define LOG_TAG "audio_hw_opi" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +/* Minimum granularity - Arbitrary but small value */ +#define CODEC_BASE_FRAME_COUNT 32 + +/* number of base blocks in a short period (low latency) */ +#define PERIOD_MULTIPLIER 32 /* 21 ms */ +/* number of frames per short period (low latency) */ +#define PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * PERIOD_MULTIPLIER) +/* number of pseudo periods for low latency playback */ +#define PLAYBACK_PERIOD_COUNT 4 +#define PLAYBACK_PERIOD_START_THRESHOLD 2 +#define CODEC_SAMPLING_RATE 48000 +#define CHANNEL_STEREO 2 +#define MIN_WRITE_SLEEP_US 5000 + +int pcm_card; +int pcm_device; + +struct stub_stream_in { + struct audio_stream_in stream; +}; + +struct alsa_audio_device { + struct audio_hw_device hw_device; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + int devices; + struct alsa_stream_in *active_input; + struct alsa_stream_out *active_output; + bool mic_mute; +}; + +struct alsa_stream_out { + struct audio_stream_out stream; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + struct pcm_config config; + struct pcm *pcm; + bool unavailable; + int standby; + struct alsa_audio_device *dev; + int write_threshold; + unsigned int written; +}; + +static int probe_pcm_out_card() { + FILE *fp; + char card_node[32]; + char card_id[16]; + + char card_prop[PROPERTY_VALUE_MAX]; + property_get("persist.vendor.audio.device", card_prop, ""); + + for (int i = 0; i < 5; i++) { + snprintf(card_node, sizeof(card_node), "/proc/asound/card%d/id", i); + if ((fp = fopen(card_node, "r")) != NULL) { + fgets(card_id, sizeof(card_id), fp); + ALOGV("%s: %s", card_node, card_id); + if (!strcmp(card_prop, "jack") && (!strncmp(card_id, "Headphones", 10) || !strncmp(card_id, "es8388", 6))) { + fclose(fp); + ALOGI("Using PCM card %d for 3.5mm audio jack", i); + return i; + } else if (!strcmp(card_prop, "dac") && strncmp(card_id, "Headphones", 10) + && strncmp(card_id, "vc4hdmi", 7)) { + fclose(fp); + ALOGI("Using PCM card %d for audio DAC %s", i, card_id); + return i; + } + fclose(fp); + } + } + + ALOGE("Could not probe PCM card for %s, using PCM card 0", card_prop); + return 0; +} + +static int get_pcm_card() +{ + char card[PROPERTY_VALUE_MAX]; + property_get("persist.vendor.audio.pcm.card.auto", card, "false"); + if (!strcmp(card, "true")) + return probe_pcm_out_card(); + + property_get("persist.vendor.audio.pcm.card", card, "0"); + return atoi(card); +} + +static int get_pcm_device() +{ + char device[PROPERTY_VALUE_MAX]; + property_get("persist.vendor.audio.pcm.device", device, "0"); + return atoi(device); +} + +/* must be called with hw device and output stream mutexes locked */ +static int start_output_stream(struct alsa_stream_out *out) +{ + struct alsa_audio_device *adev = out->dev; + + if (out->unavailable) + return -ENODEV; + + /* default to low power: will be corrected in out_write if necessary before first write to + * tinyalsa. + */ + out->write_threshold = PLAYBACK_PERIOD_COUNT * PERIOD_SIZE; + out->config.start_threshold = PLAYBACK_PERIOD_START_THRESHOLD * PERIOD_SIZE; + out->config.avail_min = PERIOD_SIZE; + + //out->pcm = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC, &out->config); + out->pcm = pcm_open(get_pcm_card(), get_pcm_device(), PCM_OUT | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC, &out->config); + + if (!pcm_is_ready(out->pcm)) { + ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm)); + pcm_close(out->pcm); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + + adev->active_output = out; + return 0; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + return out->config.rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + ALOGV("out_set_sample_rate: %d", 0); + return -ENOSYS; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + ALOGV("out_get_buffer_size: %d", 4096); + + /* return the closest majoring multiple of 16 frames, as + * audioflinger expects audio buffers to be a multiple of 16 frames */ + size_t size = PERIOD_SIZE; + size = ((size + 15) / 16) * 16; + return size * audio_stream_out_frame_size((struct audio_stream_out *)stream); +} + +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) +{ + ALOGV("out_get_channels"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + return audio_channel_out_mask_from_count(out->config.channels); +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + ALOGV("out_get_format"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + return audio_format_from_pcm_format(out->config.format); +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + ALOGV("out_set_format: %d",format); + return -ENOSYS; +} + +static int do_output_standby(struct alsa_stream_out *out) +{ + struct alsa_audio_device *adev = out->dev; + + if (!out->standby) { + pcm_close(out->pcm); + out->pcm = NULL; + adev->active_output = NULL; + out->standby = 1; + } + return 0; +} + +static int out_standby(struct audio_stream *stream) +{ + ALOGV("out_standby"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + int status; + + pthread_mutex_lock(&out->dev->lock); + pthread_mutex_lock(&out->lock); + status = do_output_standby(out); + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&out->dev->lock); + return status; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + ALOGV("out_dump"); + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + ALOGV("out_set_parameters"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + struct alsa_audio_device *adev = out->dev; + struct str_parms *parms; + char value[32]; + int val = 0; + int ret = -EINVAL; + + if (kvpairs == NULL || kvpairs[0] == 0) { + return 0; + } + + parms = str_parms_create_str(kvpairs); + + if (str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)) >= 0) { + val = atoi(value); + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + if (((adev->devices & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0)) { + adev->devices &= ~AUDIO_DEVICE_OUT_ALL; + adev->devices |= val; + } + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&adev->lock); + ret = 0; + } + + str_parms_destroy(parms); + return ret; +} + +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + ALOGV("out_get_parameters"); + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + ALOGV("out_get_latency"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + return (PERIOD_SIZE * PLAYBACK_PERIOD_COUNT * 1000) / out->config.rate; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + ALOGV("out_set_volume: Left:%f Right:%f", left, right); + return 0; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, + size_t bytes) +{ + int ret; + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + struct alsa_audio_device *adev = out->dev; + size_t frame_size = audio_stream_out_frame_size(stream); + size_t out_frames = bytes / frame_size; + + /* acquiring hw device mutex systematically is useful if a low priority thread is waiting + * on the output stream mutex - e.g. executing select_mode() while holding the hw device + * mutex + */ + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + if (out->standby) { + ret = start_output_stream(out); + if (ret != 0) { + pthread_mutex_unlock(&adev->lock); + goto exit; + } + out->standby = 0; + } + + pthread_mutex_unlock(&adev->lock); + + ret = pcm_mmap_write(out->pcm, buffer, out_frames * frame_size); + if (ret == 0) { + out->written += out_frames; + } +exit: + pthread_mutex_unlock(&out->lock); + + if (ret != 0) { + usleep((int64_t)bytes * 1000000 / audio_stream_out_frame_size(stream) / + out_get_sample_rate(&stream->common)); + } + + return bytes; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + *dsp_frames = 0; + ALOGV("out_get_render_position: dsp_frames: %p", dsp_frames); + return -EINVAL; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) +{ + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + int ret = -1; + + if (out->pcm) { + unsigned int avail; + if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { + size_t kernel_buffer_size = out->config.period_size * out->config.period_count; + int64_t signed_frames = out->written - kernel_buffer_size + avail; + if (signed_frames >= 0) { + *frames = signed_frames; + ret = 0; + } + } + } + + return ret; +} + + +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + ALOGV("out_add_audio_effect: %p", effect); + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + ALOGV("out_remove_audio_effect: %p", effect); + return 0; +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, + int64_t *timestamp) +{ + *timestamp = 0; + ALOGV("out_get_next_write_timestamp: %ld", (long int)(*timestamp)); + return -EINVAL; +} + +/** audio_stream_in implementation **/ +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + ALOGV("in_get_sample_rate"); + return 8000; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + ALOGV("in_set_sample_rate: %d", rate); + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + ALOGV("in_get_buffer_size: %d", 320); + return 320; +} + +static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) +{ + ALOGV("in_get_channels: %d", AUDIO_CHANNEL_IN_MONO); + return AUDIO_CHANNEL_IN_MONO; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + return AUDIO_FORMAT_PCM_16_BIT; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + return 0; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + return 0; +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + return strdup(""); +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + return 0; +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + ALOGV("in_read: bytes %zu", bytes); + /* XXX: fake timing for audio input */ + usleep((int64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) / + in_get_sample_rate(&stream->common)); + memset(buffer, 0, bytes); + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + return 0; +} + +static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + return 0; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + return 0; +} + +static int adev_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out, + const char *address __unused) +{ + ALOGV("adev_open_output_stream..."); + + struct alsa_audio_device *ladev = (struct alsa_audio_device *)dev; + struct alsa_stream_out *out; + struct pcm_params *params; + int ret = 0; + + //params = pcm_params_get(pcm_card, pcm_device, PCM_OUT); + params = pcm_params_get(get_pcm_card(), get_pcm_device(), PCM_OUT); + + if (!params) + return -ENOSYS; + + out = (struct alsa_stream_out *)calloc(1, sizeof(struct alsa_stream_out)); + if (!out) + return -ENOMEM; + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; + out->stream.get_presentation_position = out_get_presentation_position; + + out->config.channels = CHANNEL_STEREO; + out->config.rate = CODEC_SAMPLING_RATE; + out->config.format = PCM_FORMAT_S16_LE; + out->config.period_size = PERIOD_SIZE; + out->config.period_count = PLAYBACK_PERIOD_COUNT; + + if (out->config.rate != config->sample_rate || + audio_channel_count_from_out_mask(config->channel_mask) != CHANNEL_STEREO || + out->config.format != pcm_format_from_audio_format(config->format) ) { + config->sample_rate = out->config.rate; + config->format = audio_format_from_pcm_format(out->config.format); + config->channel_mask = audio_channel_out_mask_from_count(CHANNEL_STEREO); + ret = -EINVAL; + } + + ALOGI("adev_open_output_stream selects channels=%d rate=%d format=%d", + out->config.channels, out->config.rate, out->config.format); + + out->dev = ladev; + out->standby = 1; + out->unavailable = false; + + config->format = out_get_format(&out->stream.common); + config->channel_mask = out_get_channels(&out->stream.common); + config->sample_rate = out_get_sample_rate(&out->stream.common); + + *stream_out = &out->stream; + + /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger. */ + ret = 0; + + return ret; +} + +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + ALOGV("adev_close_output_stream..."); + free(stream); +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + ALOGV("adev_set_parameters"); + return -ENOSYS; +} + +static char * adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + ALOGV("adev_get_parameters"); + return strdup(""); +} + +static int adev_init_check(const struct audio_hw_device *dev) +{ + ALOGV("adev_init_check"); + return 0; +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + ALOGV("adev_set_voice_volume: %f", volume); + return -ENOSYS; +} + +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) +{ + ALOGV("adev_set_master_volume: %f", volume); + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) +{ + ALOGV("adev_get_master_volume: %f", *volume); + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) +{ + ALOGV("adev_set_master_mute: %d", muted); + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) +{ + ALOGV("adev_get_master_mute: %d", *muted); + return -ENOSYS; +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + ALOGV("adev_set_mode: %d", mode); + return 0; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + ALOGV("adev_set_mic_mute: %d",state); + return -ENOSYS; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + ALOGV("adev_get_mic_mute"); + return -ENOSYS; +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + ALOGV("adev_get_input_buffer_size: %d", 320); + return 320; +} + +static int adev_open_input_stream(struct audio_hw_device __unused *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags __unused, + const char *address __unused, + audio_source_t source __unused) +{ + struct stub_stream_in *in; + + ALOGV("adev_open_input_stream..."); + + in = (struct stub_stream_in *)calloc(1, sizeof(struct stub_stream_in)); + if (!in) + return -ENOMEM; + + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.get_buffer_size = in_get_buffer_size; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.standby = in_standby; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect; + in->stream.common.remove_audio_effect = in_remove_audio_effect; + in->stream.set_gain = in_set_gain; + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + *stream_in = &in->stream; + return 0; +} + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *in) +{ + ALOGV("adev_close_input_stream..."); + return; +} + +static int adev_dump(const audio_hw_device_t *device, int fd) +{ + ALOGV("adev_dump"); + return 0; +} + +static int adev_close(hw_device_t *device) +{ + ALOGV("adev_close"); + free(device); + return 0; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct alsa_audio_device *adev; + + ALOGV("adev_open: %s", name); + + // pcm_card = get_pcm_card(); + // pcm_device = get_pcm_device(); + // ALOGI("adev_open: pcm_card %d, pcm_device %d", pcm_card, pcm_device); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + adev = calloc(1, sizeof(struct alsa_audio_device)); + if (!adev) + return -ENOMEM; + + adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; + adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->hw_device.common.module = (struct hw_module_t *) module; + adev->hw_device.common.close = adev_close; + adev->hw_device.init_check = adev_init_check; + adev->hw_device.set_voice_volume = adev_set_voice_volume; + adev->hw_device.set_master_volume = adev_set_master_volume; + adev->hw_device.get_master_volume = adev_get_master_volume; + adev->hw_device.set_master_mute = adev_set_master_mute; + adev->hw_device.get_master_mute = adev_get_master_mute; + adev->hw_device.set_mode = adev_set_mode; + adev->hw_device.set_mic_mute = adev_set_mic_mute; + adev->hw_device.get_mic_mute = adev_get_mic_mute; + adev->hw_device.set_parameters = adev_set_parameters; + adev->hw_device.get_parameters = adev_get_parameters; + adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; + adev->hw_device.open_output_stream = adev_open_output_stream; + adev->hw_device.close_output_stream = adev_close_output_stream; + adev->hw_device.open_input_stream = adev_open_input_stream; + adev->hw_device.close_input_stream = adev_close_input_stream; + adev->hw_device.dump = adev_dump; + + adev->devices = AUDIO_DEVICE_NONE; + + *device = &adev->hw_device.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = adev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = AUDIO_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "Orangepi audio HW HAL", + .author = "The Android Open Source Project", + .methods = &hal_module_methods, + }, +}; diff --git a/audio/audio_hw_hdmi.c b/audio/audio_hw_hdmi.c new file mode 100644 index 0000000..28efa67 --- /dev/null +++ b/audio/audio_hw_hdmi.c @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2021-2023 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "audio_hw_opi_hdmi" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* Minimum granularity - Arbitrary but small value */ +#define CODEC_BASE_FRAME_COUNT 32 + +/* number of base blocks in a short period (low latency) */ +#define PERIOD_MULTIPLIER 32 /* 21 ms */ +/* number of frames per short period (low latency) */ +#define PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * PERIOD_MULTIPLIER) +/* number of pseudo periods for low latency playback */ +#define PLAYBACK_PERIOD_COUNT 4 +#define PLAYBACK_PERIOD_START_THRESHOLD 2 +#define CODEC_SAMPLING_RATE 48000 +#define CHANNEL_STEREO 2 +#define MIN_WRITE_SLEEP_US 5000 + +char device_name[PROPERTY_VALUE_MAX]; + +struct stub_stream_in { + struct audio_stream_in stream; +}; + +struct alsa_audio_device { + struct audio_hw_device hw_device; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + int devices; + struct alsa_stream_in *active_input; + struct alsa_stream_out *active_output; + bool mic_mute; +}; + +struct alsa_stream_out { + struct audio_stream_out stream; + struct alsa_audio_device *dev; + + pthread_mutex_t lock; /* see note below on mutex acquisition order */ + snd_pcm_t *pcm; + + snd_pcm_uframes_t period_size; + unsigned int periods; + snd_pcm_uframes_t buffer_size; + + bool unavailable; + int standby; + snd_pcm_uframes_t written; +}; + +static void get_alsa_device_name(char *name) { + char hdmi_device[PROPERTY_VALUE_MAX]; + property_get("persist.vendor.audio.hdmi.device", hdmi_device, "vc4hdmi0"); + + // use card configured in vc4-hdmi.conf to get IEC958 subframe conversion + sprintf(name, "default:CARD=%s", hdmi_device); +} + +/* must be called with hw device and output stream mutexes locked */ +static int start_output_stream(struct alsa_stream_out *out) +{ + struct alsa_audio_device *adev = out->dev; + + if (out->unavailable) + return -ENODEV; + + ALOGI("start_output_stream: %s", device_name); + + int r; + snd_pcm_t *pcm; + + if ((r = snd_pcm_open(&pcm, device_name, SND_PCM_STREAM_PLAYBACK, 0) < 0)) { + ALOGE("cannot open pcm_out driver: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + out->pcm = pcm; + + snd_pcm_hw_params_t *hwp; + snd_pcm_hw_params_alloca(&hwp); + snd_pcm_hw_params_any(pcm, hwp); + snd_pcm_hw_params_set_access(pcm, hwp, SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(pcm, hwp, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_rate(pcm, hwp, CODEC_SAMPLING_RATE, 0); + snd_pcm_hw_params_set_channels(pcm, hwp, CHANNEL_STEREO); + + // Configurue period_size, periods and buffer_size + int dir = 0; + out->period_size = PERIOD_SIZE; + if ((r = snd_pcm_hw_params_set_period_size_near(pcm, hwp, &out->period_size, &dir)) < 0) { + ALOGE("cannot snd_pcm_hw_params_set_period_size_near: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + dir = 0; + out->periods = PLAYBACK_PERIOD_COUNT; + if ((r = snd_pcm_hw_params_set_periods_near(pcm, hwp, &out->periods, &dir)) < 0) { + ALOGE("cannot snd_pcm_hw_params_set_periods_near: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + out->buffer_size = out->period_size * out->periods; + if ((r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwp, &out->buffer_size)) < 0) { + ALOGE("cannot snd_pcm_hw_params_set_buffer_size_near: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + + //write the hw params + if ((r = snd_pcm_hw_params(pcm, hwp)) < 0) { + ALOGE("cannot snd_pcm_hw_params: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + + //Software parameters + snd_pcm_sw_params_t *swp; + snd_pcm_sw_params_alloca(&swp); + snd_pcm_sw_params_current(pcm, swp); + + // set avail_min to period_size + if ((r = snd_pcm_sw_params_set_avail_min(pcm, swp, out->period_size)) < 0) { + ALOGE("cannot snd_pcm_sw_params_set_avail_min: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + // set start_threshold to period_size * PLAYBACK_PERIOD_START_THRESHOLD + if ((r = snd_pcm_sw_params_set_start_threshold(pcm, swp, out->period_size * PLAYBACK_PERIOD_START_THRESHOLD)) < 0) { + ALOGE("cannot snd_pcm_sw_params_set_start_threshold: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + //write the sw params + if ((r = snd_pcm_sw_params(pcm, swp)) < 0) { + ALOGE("cannot snd_pcm_sw_params: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + + // prepare + if ((r = snd_pcm_prepare(pcm)) < 0) { + ALOGE("cannot snd_pcm_prepare: %s", snd_strerror(r)); + adev->active_output = NULL; + out->unavailable = true; + return -ENODEV; + } + + adev->active_output = out; + return 0; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + ALOGV("out_get_sample_rate: %d", CODEC_SAMPLING_RATE); + return CODEC_SAMPLING_RATE; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + ALOGV("out_set_sample_rate: %d", 0); + return -ENOSYS; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + + /* return the closest majoring multiple of 16 frames, as + * audioflinger expects audio buffers to be a multiple of 16 frames */ + size_t size = out->period_size; + size = ((size + 15) / 16) * 16; + ALOGV("out_get_buffer_size: %ld", (long int)size); + return size * audio_stream_out_frame_size((struct audio_stream_out *)stream); +} + +static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) +{ + ALOGV("out_get_channels: %d", CHANNEL_STEREO); + return audio_channel_out_mask_from_count(CHANNEL_STEREO); +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + ALOGV("out_get_format: %d", AUDIO_FORMAT_PCM_16_BIT); + return AUDIO_FORMAT_PCM_16_BIT; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + ALOGV("out_set_format: %d",format); + return -ENOSYS; +} + +static int do_output_standby(struct alsa_stream_out *out) +{ + struct alsa_audio_device *adev = out->dev; + + if (!out->standby) { + snd_pcm_close(out->pcm); + out->pcm = NULL; + adev->active_output = NULL; + out->standby = 1; + } + return 0; +} + +static int out_standby(struct audio_stream *stream) +{ + ALOGV("out_standby"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + int status; + + pthread_mutex_lock(&out->dev->lock); + pthread_mutex_lock(&out->lock); + status = do_output_standby(out); + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&out->dev->lock); + return status; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + ALOGV("out_dump"); + return 0; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + ALOGV("out_set_parameters"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + struct alsa_audio_device *adev = out->dev; + struct str_parms *parms; + char value[32]; + int val = 0; + int ret = -EINVAL; + + if (kvpairs == NULL || kvpairs[0] == 0) { + return 0; + } + + parms = str_parms_create_str(kvpairs); + + if (str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)) >= 0) { + val = atoi(value); + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + if (((adev->devices & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0)) { + adev->devices &= ~AUDIO_DEVICE_OUT_ALL; + adev->devices |= val; + } + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&adev->lock); + ret = 0; + } + + str_parms_destroy(parms); + return ret; +} + +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) +{ + ALOGV("out_get_parameters"); + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + ALOGV("out_get_latency"); + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + // latency = buffer_size / rate + return (out->buffer_size * 1000) / CODEC_SAMPLING_RATE; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + ALOGV("out_set_volume: Left:%f Right:%f", left, right); + return 0; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, + size_t bytes) +{ + int ret; + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + struct alsa_audio_device *adev = out->dev; + size_t frame_size = audio_stream_out_frame_size(stream); + snd_pcm_uframes_t out_frames = bytes / frame_size; + + /* acquiring hw device mutex systematically is useful if a low priority thread is waiting + * on the output stream mutex - e.g. executing select_mode() while holding the hw device + * mutex + */ + pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->lock); + if (out->standby) { + ret = start_output_stream(out); + if (ret != 0) { + pthread_mutex_unlock(&adev->lock); + goto exit; + } + out->standby = 0; + } + + pthread_mutex_unlock(&adev->lock); + + ALOGV("out_write: out_frames:%ld", (long int)out_frames); + + ret = snd_pcm_writei(out->pcm, buffer, out_frames); + if (ret == out_frames) { + out->written += out_frames; + } +exit: + pthread_mutex_unlock(&out->lock); + + if (ret != out_frames) { + if (ret == -EPIPE) { + ALOGE("underrun deteced -> redo snd_pcm_prepare"); + snd_pcm_prepare(out->pcm); + } else { + ALOGE("out_write err: %s", snd_strerror(ret)); + usleep((int64_t)bytes * 1000000 / audio_stream_out_frame_size(stream) / + out_get_sample_rate(&stream->common)); + } + } + + return bytes; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + *dsp_frames = 0; + ALOGV("out_get_render_position: dsp_frames: %p", dsp_frames); + return -EINVAL; +} + +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) +{ + struct alsa_stream_out *out = (struct alsa_stream_out *)stream; + int ret = -1; + + if (out->pcm) { + snd_pcm_uframes_t avail; + int r; + if ((r = snd_pcm_htimestamp(out->pcm, &avail, timestamp)) == 0) { + int64_t signed_frames = (int64_t)(out->written) - out->buffer_size + avail; + if (signed_frames >= 0) { + *frames = signed_frames; + ret = 0; + } + ALOGV("out_get_presentation_position: %ld", (long int)(*frames)); + } else { + ALOGE("out_get_presentation_position: err: %s", snd_strerror(r)); + } + } else { + ALOGV("out_get_presentation_position: stream in standby"); + } + return ret; +} + + +static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + ALOGV("out_add_audio_effect: %p", effect); + return 0; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + ALOGV("out_remove_audio_effect: %p", effect); + return 0; +} + +static int out_get_next_write_timestamp(const struct audio_stream_out *stream, + int64_t *timestamp) +{ + *timestamp = 0; + ALOGV("out_get_next_write_timestamp: %ld", (long int)(*timestamp)); + return -EINVAL; +} + +/** audio_stream_in implementation **/ +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + ALOGV("in_get_sample_rate"); + return 8000; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + ALOGV("in_set_sample_rate: %d", rate); + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + ALOGV("in_get_buffer_size: %d", 320); + return 320; +} + +static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) +{ + ALOGV("in_get_channels: %d", AUDIO_CHANNEL_IN_MONO); + return AUDIO_CHANNEL_IN_MONO; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + return AUDIO_FORMAT_PCM_16_BIT; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + return 0; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + return 0; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + return 0; +} + +static char * in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + return strdup(""); +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + return 0; +} + +static ssize_t in_read(struct audio_stream_in *stream, void* buffer, + size_t bytes) +{ + ALOGV("in_read: bytes %zu", bytes); + /* XXX: fake timing for audio input */ + usleep((int64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) / + in_get_sample_rate(&stream->common)); + memset(buffer, 0, bytes); + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + return 0; +} + +static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + return 0; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) +{ + return 0; +} + +static int adev_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out, + const char *address __unused) +{ + ALOGV("adev_open_output_stream..."); + + struct alsa_audio_device *ladev = (struct alsa_audio_device *)dev; + struct alsa_stream_out *out; + int ret = 0; + + out = (struct alsa_stream_out *)calloc(1, sizeof(struct alsa_stream_out)); + if (!out) + return -ENOMEM; + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; + out->stream.get_presentation_position = out_get_presentation_position; + + out->period_size = PERIOD_SIZE; + out->periods = PLAYBACK_PERIOD_COUNT; + out->buffer_size = out->period_size * out->periods; + + out->dev = ladev; + out->standby = 1; + out->unavailable = false; + + config->format = out_get_format(&out->stream.common); + config->channel_mask = out_get_channels(&out->stream.common); + config->sample_rate = out_get_sample_rate(&out->stream.common); + + *stream_out = &out->stream; + + /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger. */ + ret = 0; + + return ret; +} + +static void adev_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + ALOGV("adev_close_output_stream..."); + free(stream); +} + +static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) +{ + ALOGV("adev_set_parameters"); + return -ENOSYS; +} + +static char * adev_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + ALOGV("adev_get_parameters"); + return strdup(""); +} + +static int adev_init_check(const struct audio_hw_device *dev) +{ + ALOGV("adev_init_check"); + return 0; +} + +static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + ALOGV("adev_set_voice_volume: %f", volume); + return -ENOSYS; +} + +static int adev_set_master_volume(struct audio_hw_device *dev, float volume) +{ + ALOGV("adev_set_master_volume: %f", volume); + return -ENOSYS; +} + +static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) +{ + ALOGV("adev_get_master_volume: %f", *volume); + return -ENOSYS; +} + +static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) +{ + ALOGV("adev_set_master_mute: %d", muted); + return -ENOSYS; +} + +static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) +{ + ALOGV("adev_get_master_mute: %d", *muted); + return -ENOSYS; +} + +static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) +{ + ALOGV("adev_set_mode: %d", mode); + return 0; +} + +static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + ALOGV("adev_set_mic_mute: %d",state); + return -ENOSYS; +} + +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + ALOGV("adev_get_mic_mute"); + return -ENOSYS; +} + +static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + ALOGV("adev_get_input_buffer_size: %d", 320); + return 320; +} + +static int adev_open_input_stream(struct audio_hw_device __unused *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags __unused, + const char *address __unused, + audio_source_t source __unused) +{ + struct stub_stream_in *in; + + ALOGV("adev_open_input_stream..."); + + in = (struct stub_stream_in *)calloc(1, sizeof(struct stub_stream_in)); + if (!in) + return -ENOMEM; + + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.get_buffer_size = in_get_buffer_size; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.standby = in_standby; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect; + in->stream.common.remove_audio_effect = in_remove_audio_effect; + in->stream.set_gain = in_set_gain; + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + *stream_in = &in->stream; + return 0; +} + +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *in) +{ + ALOGV("adev_close_input_stream..."); + return; +} + +static int adev_dump(const audio_hw_device_t *device, int fd) +{ + ALOGV("adev_dump"); + return 0; +} + +static int adev_close(hw_device_t *device) +{ + ALOGV("adev_close"); + free(device); + return 0; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + struct alsa_audio_device *adev; + + ALOGV("adev_open: %s", name); + + get_alsa_device_name(device_name); + ALOGI("adev_open: %s", device_name); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) + return -EINVAL; + + adev = calloc(1, sizeof(struct alsa_audio_device)); + if (!adev) + return -ENOMEM; + + adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; + adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; + adev->hw_device.common.module = (struct hw_module_t *) module; + adev->hw_device.common.close = adev_close; + adev->hw_device.init_check = adev_init_check; + adev->hw_device.set_voice_volume = adev_set_voice_volume; + adev->hw_device.set_master_volume = adev_set_master_volume; + adev->hw_device.get_master_volume = adev_get_master_volume; + adev->hw_device.set_master_mute = adev_set_master_mute; + adev->hw_device.get_master_mute = adev_get_master_mute; + adev->hw_device.set_mode = adev_set_mode; + adev->hw_device.set_mic_mute = adev_set_mic_mute; + adev->hw_device.get_mic_mute = adev_get_mic_mute; + adev->hw_device.set_parameters = adev_set_parameters; + adev->hw_device.get_parameters = adev_get_parameters; + adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; + adev->hw_device.open_output_stream = adev_open_output_stream; + adev->hw_device.close_output_stream = adev_close_output_stream; + adev->hw_device.open_input_stream = adev_open_input_stream; + adev->hw_device.close_input_stream = adev_close_input_stream; + adev->hw_device.dump = adev_dump; + + adev->devices = AUDIO_DEVICE_NONE; + + *device = &adev->hw_device.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = adev_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = AUDIO_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "Orange Pi audio hdmi HW HAL", + .author = "The Android Open Source Project", + .methods = &hal_module_methods, + }, +}; diff --git a/audio/audio_policy_configuration.xml b/audio/audio_policy_configuration.xml new file mode 100644 index 0000000..b2647e8 --- /dev/null +++ b/audio/audio_policy_configuration.xml @@ -0,0 +1,106 @@ + + + + + + + Speaker + Built-In Mic + + Speaker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bluetooth/Android.bp b/bluetooth/Android.bp new file mode 100644 index 0000000..13a4a51 --- /dev/null +++ b/bluetooth/Android.bp @@ -0,0 +1,57 @@ +// Copyright (C) 2020 The Android Open Source Project +// Copyright (C) 2024 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "android.hardware.bluetooth-service.opi", + relative_install_path: "hw", + vendor: true, + srcs: [ + "BluetoothHci.cpp", + "main.cpp", + "net_bluetooth_mgmt.cpp", + ], + static_libs: [ + "android.hardware.bluetooth.async", + "android.hardware.bluetooth.hci", + ], + shared_libs: [ + "android.hardware.bluetooth-V1-ndk", + "libbase", + "libbinder_ndk", + "liblog", + "libutils", + ], + installable: false, +} + +prebuilt_etc { + name: "android.hardware.bluetooth-service.opi.rc", + src: "android.hardware.bluetooth-service.opi.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.bluetooth-service.opi.xml", + src: "android.hardware.bluetooth-service.opi.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.bluetooth.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["android.hardware.bluetooth-service.opi"], + prebuilts: [ + "android.hardware.bluetooth-service.opi.rc", + "android.hardware.bluetooth-service.opi.xml", + ], +} diff --git a/bluetooth/BluetoothHci.cpp b/bluetooth/BluetoothHci.cpp new file mode 100644 index 0000000..191b487 --- /dev/null +++ b/bluetooth/BluetoothHci.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "android.hardware.bluetooth.service.opi" + +#include "BluetoothHci.h" + +#include "log/log.h" + +using namespace ::android::hardware::bluetooth::hci; +using namespace ::android::hardware::bluetooth::async; +using aidl::android::hardware::bluetooth::Status; + +namespace aidl::android::hardware::bluetooth::impl { + +void OnDeath(void* cookie); + +class BluetoothDeathRecipient { + public: + BluetoothDeathRecipient(BluetoothHci* hci) : mHci(hci) {} + + void LinkToDeath(const std::shared_ptr& cb) { + mCb = cb; + clientDeathRecipient_ = AIBinder_DeathRecipient_new(OnDeath); + auto linkToDeathReturnStatus = AIBinder_linkToDeath( + mCb->asBinder().get(), clientDeathRecipient_, this /* cookie */); + LOG_ALWAYS_FATAL_IF(linkToDeathReturnStatus != STATUS_OK, + "Unable to link to death recipient"); + } + + void UnlinkToDeath(const std::shared_ptr& cb) { + LOG_ALWAYS_FATAL_IF(cb != mCb, "Unable to unlink mismatched pointers"); + } + + void serviceDied() { + if (mCb != nullptr && !AIBinder_isAlive(mCb->asBinder().get())) { + ALOGE("Bluetooth remote service has died"); + } else { + ALOGE("BluetoothDeathRecipient::serviceDied called but service not dead"); + return; + } + { + std::lock_guard guard(mHasDiedMutex); + has_died_ = true; + } + mHci->close(); + } + BluetoothHci* mHci; + std::shared_ptr mCb; + AIBinder_DeathRecipient* clientDeathRecipient_; + bool getHasDied() { + std::lock_guard guard(mHasDiedMutex); + return has_died_; + } + + private: + std::mutex mHasDiedMutex; + bool has_died_{false}; +}; + +void OnDeath(void* cookie) { + auto* death_recipient = static_cast(cookie); + death_recipient->serviceDied(); +} + +BluetoothHci::BluetoothHci() { + mDeathRecipient = std::make_shared(this); +} + +ndk::ScopedAStatus BluetoothHci::initialize( + const std::shared_ptr& cb) { + ALOGI(__func__); + + if (cb == nullptr) { + ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)"); + return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE); + } + + HalState old_state = HalState::READY; + { + std::lock_guard guard(mStateMutex); + if (mState != HalState::READY) { + old_state = mState; + } else { + mState = HalState::INITIALIZING; + } + } + + if (old_state != HalState::READY) { + ALOGE("initialize: Unexpected State %d", static_cast(old_state)); + close(); + cb->initializationComplete(Status::ALREADY_INITIALIZED); + return ndk::ScopedAStatus::ok(); + } + + mCb = cb; + management_.reset(new NetBluetoothMgmt); + mFd = management_->openHci(); + if (mFd < 0) { + management_.reset(); + + ALOGI("Unable to open Linux interface."); + mState = HalState::READY; + cb->initializationComplete(Status::UNABLE_TO_OPEN_INTERFACE); + return ndk::ScopedAStatus::ok(); + } + + mDeathRecipient->LinkToDeath(mCb); + + mH4 = std::make_shared( + mFd, + [](const std::vector& /* raw_command */) { + LOG_ALWAYS_FATAL("Unexpected command!"); + }, + [this](const std::vector& raw_acl) { + mCb->aclDataReceived(raw_acl); + }, + [this](const std::vector& raw_sco) { + mCb->scoDataReceived(raw_sco); + }, + [this](const std::vector& raw_event) { + mCb->hciEventReceived(raw_event); + }, + [this](const std::vector& raw_iso) { + mCb->isoDataReceived(raw_iso); + }, + [this]() { + ALOGI("HCI socket device disconnected"); + mFdWatcher.StopWatchingFileDescriptors(); + }); + mFdWatcher.WatchFdForNonBlockingReads(mFd, + [this](int) { mH4->OnDataReady(); }); + + { + std::lock_guard guard(mStateMutex); + mState = HalState::ONE_CLIENT; + } + ALOGI("initialization complete"); + auto status = mCb->initializationComplete(Status::SUCCESS); + if (!status.isOk()) { + if (!mDeathRecipient->getHasDied()) { + ALOGE("Error sending init callback, but no death notification"); + } + close(); + return ndk::ScopedAStatus::fromServiceSpecificError( + STATUS_FAILED_TRANSACTION); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus BluetoothHci::close() { + ALOGI(__func__); + { + std::lock_guard guard(mStateMutex); + if (mState != HalState::ONE_CLIENT) { + LOG_ALWAYS_FATAL_IF(mState == HalState::INITIALIZING, + "mState is INITIALIZING"); + ALOGI("Already closed"); + return ndk::ScopedAStatus::ok(); + } + mState = HalState::CLOSING; + } + + mFdWatcher.StopWatchingFileDescriptors(); + + management_->closeHci(); + + { + std::lock_guard guard(mStateMutex); + mState = HalState::READY; + mH4 = nullptr; + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus BluetoothHci::sendHciCommand( + const std::vector& packet) { + return send(PacketType::COMMAND, packet); +} + +ndk::ScopedAStatus BluetoothHci::sendAclData( + const std::vector& packet) { + return send(PacketType::ACL_DATA, packet); +} + +ndk::ScopedAStatus BluetoothHci::sendScoData( + const std::vector& packet) { + return send(PacketType::SCO_DATA, packet); +} + +ndk::ScopedAStatus BluetoothHci::sendIsoData( + const std::vector& packet) { + return send(PacketType::ISO_DATA, packet); +} + +ndk::ScopedAStatus BluetoothHci::send(PacketType type, + const std::vector& v) { + if (v.empty()) { + ALOGE("Packet is empty, no data was found to be sent"); + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + std::lock_guard guard(mStateMutex); + if (mH4 == nullptr) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + + mH4->Send(type, v); + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::bluetooth::impl diff --git a/bluetooth/BluetoothHci.h b/bluetooth/BluetoothHci.h new file mode 100644 index 0000000..a27316c --- /dev/null +++ b/bluetooth/BluetoothHci.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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 +#include + +#include "async_fd_watcher.h" +#include "h4_protocol.h" +#include "net_bluetooth_mgmt.h" + +namespace aidl::android::hardware::bluetooth::impl { + +class BluetoothDeathRecipient; + +// This Bluetooth HAL implementation connects with a serial port at dev_path_. +class BluetoothHci : public BnBluetoothHci { + public: + BluetoothHci(); + + ndk::ScopedAStatus initialize( + const std::shared_ptr& cb) override; + + ndk::ScopedAStatus sendHciCommand( + const std::vector& packet) override; + + ndk::ScopedAStatus sendAclData(const std::vector& packet) override; + + ndk::ScopedAStatus sendScoData(const std::vector& packet) override; + + ndk::ScopedAStatus sendIsoData(const std::vector& packet) override; + + ndk::ScopedAStatus close() override; + + private: + int mFd{-1}; + std::shared_ptr mCb = nullptr; + + std::shared_ptr<::android::hardware::bluetooth::hci::H4Protocol> mH4; + + std::shared_ptr mDeathRecipient; + + ::android::hardware::bluetooth::async::AsyncFdWatcher mFdWatcher; + + [[nodiscard]] ndk::ScopedAStatus send( + ::android::hardware::bluetooth::hci::PacketType type, + const std::vector& packet); + std::unique_ptr management_{}; + + // Don't close twice or open before close is complete + std::mutex mStateMutex; + enum class HalState { + READY, + INITIALIZING, + ONE_CLIENT, + CLOSING, + } mState{HalState::READY}; +}; + +} // namespace aidl::android::hardware::bluetooth::impl diff --git a/bluetooth/android.hardware.bluetooth-service.opi.rc b/bluetooth/android.hardware.bluetooth-service.opi.rc new file mode 100644 index 0000000..b962009 --- /dev/null +++ b/bluetooth/android.hardware.bluetooth-service.opi.rc @@ -0,0 +1,6 @@ +service vendor.bluetooth-rpi /apex/com.android.hardware.bluetooth.opi5/bin/hw/android.hardware.bluetooth-service.opi + class hal + capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE + user bluetooth + group bluetooth + task_profiles HighPerformance diff --git a/bluetooth/android.hardware.bluetooth-service.opi.xml b/bluetooth/android.hardware.bluetooth-service.opi.xml new file mode 100644 index 0000000..bb05995 --- /dev/null +++ b/bluetooth/android.hardware.bluetooth-service.opi.xml @@ -0,0 +1,6 @@ + + + android.hardware.bluetooth + IBluetoothHci/default + + diff --git a/bluetooth/apex_file_contexts b/bluetooth/apex_file_contexts new file mode 100644 index 0000000..77f5897 --- /dev/null +++ b/bluetooth/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.bluetooth-service\.opi u:object_r:hal_bluetooth_default_exec:s0 diff --git a/bluetooth/apex_manifest.json b/bluetooth/apex_manifest.json new file mode 100644 index 0000000..544ae6f --- /dev/null +++ b/bluetooth/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.bluetooth.opi5", + "version": 1 +} diff --git a/bluetooth/main.cpp b/bluetooth/main.cpp new file mode 100644 index 0000000..74a0576 --- /dev/null +++ b/bluetooth/main.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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 "BluetoothHci.h" + +#include +#include +#include + +using ::aidl::android::hardware::bluetooth::impl::BluetoothHci; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr bluetooth = ndk::SharedRefBase::make(); + + const std::string instance = std::string() + BluetoothHci::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(bluetooth->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/bluetooth/net_bluetooth_mgmt.cpp b/bluetooth/net_bluetooth_mgmt.cpp new file mode 100644 index 0000000..24b85bd --- /dev/null +++ b/bluetooth/net_bluetooth_mgmt.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "android.hardware.bluetooth.service.opi" + +#include "net_bluetooth_mgmt.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// Definitions imported from +#define BTPROTO_HCI 1 + +// Definitions imported from +#define HCI_CHANNEL_USER 1 +#define HCI_CHANNEL_CONTROL 3 +#define HCI_DEV_NONE 0xffff + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; + +// Definitions imported from +#define MGMT_OP_READ_INDEX_LIST 0x0003 +#define MGMT_EV_INDEX_ADDED 0x0004 +#define MGMT_EV_CMD_COMPLETE 0x0001 +#define MGMT_PKT_SIZE_MAX 1024 +#define MGMT_INDEX_NONE 0xFFFF +#define WRITE_NO_INTR(fn) \ + do { \ + } while ((fn) == -1 && errno == EINTR) + +struct mgmt_pkt { + uint16_t opcode; + uint16_t index; + uint16_t len; + uint8_t data[MGMT_PKT_SIZE_MAX]; +} __attribute__((packed)); + +struct mgmt_ev_read_index_list { + uint16_t opcode; + uint8_t status; + uint16_t num_controllers; + uint16_t index[]; +} __attribute__((packed)); + +namespace aidl::android::hardware::bluetooth::impl { + +// Wait indefinitely for the selected HCI interface to be enabled in the +// bluetooth driver. +int NetBluetoothMgmt::waitHciDev(int hci_interface) { + ALOGI("waiting for hci interface %d", hci_interface); + + int ret = -1; + struct mgmt_pkt cmd; + struct pollfd pollfd; + struct sockaddr_hci hci_addr = { + .hci_family = AF_BLUETOOTH, + .hci_dev = HCI_DEV_NONE, + .hci_channel = HCI_CHANNEL_CONTROL, + }; + + // Open and bind a socket to the bluetooth control interface in the + // kernel driver, used to send control commands and receive control + // events. + int fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (fd < 0) { + ALOGE("unable to open raw bluetooth socket: %s", strerror(errno)); + return -1; + } + + if (bind(fd, (struct sockaddr*)&hci_addr, sizeof(hci_addr)) < 0) { + ALOGE("unable to bind bluetooth control channel: %s", strerror(errno)); + goto end; + } + + // Send the control command [Read Index List]. + cmd = { + .opcode = MGMT_OP_READ_INDEX_LIST, + .index = MGMT_INDEX_NONE, + .len = 0, + }; + + if (write(fd, &cmd, 6) != 6) { + ALOGE("error writing mgmt command: %s", strerror(errno)); + goto end; + } + + // Poll the control socket waiting for the command response, + // and subsequent [Index Added] events. The loops continue without + // timeout until the selected hci interface is detected. + pollfd = {.fd = fd, .events = POLLIN}; + + for (;;) { + ret = poll(&pollfd, 1, -1); + + // Poll interrupted, try again. + if (ret == -1 && (errno == EINTR || errno == EAGAIN)) { + continue; + } + + // Poll failure, abandon. + if (ret == -1) { + ALOGE("poll error: %s", strerror(errno)); + break; + } + + // Spurious wakeup, try again. + if (ret == 0 || (pollfd.revents & POLLIN) == 0) { + continue; + } + + // Read the next control event. + struct mgmt_pkt ev {}; + ret = read(fd, &ev, sizeof(ev)); + if (ret < 0) { + ALOGE("error reading mgmt event: %s", strerror(errno)); + goto end; + } + + // Received [Read Index List] command response. + if (ev.opcode == MGMT_EV_CMD_COMPLETE) { + struct mgmt_ev_read_index_list* data = + (struct mgmt_ev_read_index_list*)ev.data; + + // Prefer the exact hci_interface + for (int i = 0; i < data->num_controllers; i++) { + if (data->index[i] == hci_interface) { + ALOGI("hci interface %d found", data->index[i]); + ret = data->index[i]; + goto end; + } + } + + // Accept a larger one if we can't find the exact one + for (int i = 0; i < data->num_controllers; i++) { + if (data->index[i] >= hci_interface) { + ALOGI("hci interface %d found", data->index[i]); + ret = data->index[i]; + goto end; + } + } + } + + // Received [Index Added] event. + if (ev.opcode == MGMT_EV_INDEX_ADDED && ev.index == hci_interface) { + ALOGI("hci interface %d added", hci_interface); + ret = hci_interface; + goto end; + } + } + +end: + ::close(fd); + return ret; +} + +int NetBluetoothMgmt::findRfKill() { + char rfkill_type[64]; + char type[16]; + int fd, size, i; + for(i = 0; rfkill_state_ == NULL; i++) + { + snprintf(rfkill_type, sizeof(rfkill_type), "/sys/class/rfkill/rfkill%d/type", i); + if ((fd = open(rfkill_type, O_RDONLY)) < 0) + { + ALOGE("open(%s) failed: %s (%d)\n", rfkill_type, strerror(errno), errno); + return -1; + } + + size = read(fd, &type, sizeof(type)); + ::close(fd); + + if ((size >= 9) && !memcmp(type, "bluetooth", 9)) + { + ::asprintf(&rfkill_state_, "/sys/class/rfkill/rfkill%d/state", i); + break; + } + } + return 0; +} + +int NetBluetoothMgmt::rfKill(int block) { + int fd; + char on = (block)?'1':'0'; + if (findRfKill() != 0) return 0; + + fd = open(rfkill_state_, O_WRONLY); + if (fd < 0) { + ALOGE( "Unable to open /dev/rfkill"); + return -1; + } + ssize_t len; + WRITE_NO_INTR(len = write(fd, &on, 1)); + if (len < 0) { + ALOGE( "Failed to change rfkill state"); + ::close(fd); + return -1; + } + ::close(fd); + return 0; +} + +int NetBluetoothMgmt::openHci(int hci_interface) { + ALOGI("opening hci interface %d", hci_interface); + + // Block Bluetooth. + rfkill_state_ = NULL; + rfKill(1); + + // Wait for the HCI interface to complete initialization or to come online. + int hci = waitHciDev(hci_interface); + if (hci < 0) { + ALOGE("hci interface %d not found", hci_interface); + return -1; + } + + // Open the raw HCI socket. + int fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (fd < 0) { + ALOGE("unable to open raw bluetooth socket: %s", strerror(errno)); + return -1; + } + + struct sockaddr_hci hci_addr = { + .hci_family = AF_BLUETOOTH, + .hci_dev = static_cast(hci), + .hci_channel = HCI_CHANNEL_USER, + }; + + // Bind the socket to the selected interface. + if (bind(fd, (struct sockaddr*)&hci_addr, sizeof(hci_addr)) < 0) { + ALOGE("unable to bind bluetooth user channel: %s", strerror(errno)); + ::close(fd); + return -1; + } + + ALOGI("hci interface %d ready", hci); + bt_fd_ = fd; + return fd; +} + +void NetBluetoothMgmt::closeHci() { + if (bt_fd_ != -1) { + ::close(bt_fd_); + bt_fd_ = -1; + } + + // Unblock Bluetooth. + rfKill(0); + free(rfkill_state_); +} + +} // namespace aidl::android::hardware::bluetooth::impl diff --git a/bluetooth/net_bluetooth_mgmt.h b/bluetooth/net_bluetooth_mgmt.h new file mode 100644 index 0000000..b67510f --- /dev/null +++ b/bluetooth/net_bluetooth_mgmt.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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 + +namespace aidl::android::hardware::bluetooth::impl { + +class NetBluetoothMgmt { + public: + NetBluetoothMgmt() {} + ~NetBluetoothMgmt() { + ::close(bt_fd_); + } + + int openHci(int hci_interface = 0); + void closeHci(); + + private: + int waitHciDev(int hci_interface); + int findRfKill(); + int rfKill(int block); + char *rfkill_state_; + + // File descriptor opened to the bluetooth user channel. + int bt_fd_{-1}; +}; + +} // namespace aidl::android::hardware::bluetooth::impl diff --git a/bootanimation.zip b/bootanimation.zip new file mode 100644 index 0000000..2d58f59 Binary files /dev/null and b/bootanimation.zip differ diff --git a/compatibility_matrix.xml b/compatibility_matrix.xml new file mode 100644 index 0000000..2bde0cc --- /dev/null +++ b/compatibility_matrix.xml @@ -0,0 +1,36 @@ + + + android.frameworks.sensorservice + 1.0 + + ISensorManager + default + + + + android.hidl.manager + 1.2 + + IServiceManager + default + + + + android.hidl.memory + 1.0 + + IMapper + ashmem + + + + android.hidl.token + 1.0 + + ITokenManager + default + + + + + diff --git a/device.mk b/device.mk new file mode 100644 index 0000000..061da03 --- /dev/null +++ b/device.mk @@ -0,0 +1,303 @@ +# +# Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +# +# SPDX-License-Identifier: Apache-2.0 +# + +DEVICE_PATH := device/rockchip/orangepi5ultra +PRODUCT_SOONG_NAMESPACES += $(DEVICE_PATH) + +$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit_only.mk) +$(call inherit-product, frameworks/native/build/tablet-7in-xhdpi-2048-dalvik-heap.mk) +$(call inherit-product, vendor/lineage-rockchip/opi5ultra-vendor.mk) + +# APEX +$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk) +OVERRIDE_PRODUCT_COMPRESSED_APEX := false + +# API level +PRODUCT_SHIPPING_API_LEVEL := 36 + + +# Audio +PRODUCT_PACKAGES += \ + android.hardware.audio.service \ + android.hardware.audio@7.1-impl \ + android.hardware.audio.effect@7.0-impl \ + audio.r_submix.default \ + audio.usb.default \ + audio.primary.opi \ + audio.primary.opi_hdmi + +PRODUCT_PACKAGES += \ + tinycap \ + tinyhostless \ + tinymix \ + tinypcminfo \ + tinyplay + + +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)/audio/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \ + frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \ + frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \ + frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \ + frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \ + frameworks/av/services/audiopolicy/config/usb_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/usb_audio_policy_configuration.xml + + +# Bluetooth +PRODUCT_PACKAGES += \ + com.android.hardware.bluetooth.opi5 + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.bluetooth.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.bluetooth.xml \ + frameworks/native/data/etc/android.hardware.bluetooth_le.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.bluetooth_le.xml + +PRODUCT_PACKAGES += \ + android.hardware.bluetooth.audio-impl \ + audio.bluetooth.default + +PRODUCT_COPY_FILES += \ + frameworks/av/services/audiopolicy/config/a2dp_in_audio_policy_configuration_7_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/a2dp_in_audio_policy_configuration_7_0.xml \ + frameworks/av/services/audiopolicy/config/bluetooth_audio_policy_configuration_7_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_audio_policy_configuration_7_0.xml + + +# !-----TODO : Camera isn't supported currently and might be implemented in the near future. Kept just in case. +# # Camera +# PRODUCT_PACKAGES += \ +# android.hardware.camera.provider-V1-external-service + +# PRODUCT_COPY_FILES += \ +# $(DEVICE_PATH)/camera/external_camera_config.xml:$(TARGET_COPY_OUT_VENDOR)/etc/external_camera_config.xml + +# PRODUCT_COPY_FILES += \ +# frameworks/native/data/etc/android.hardware.camera.external.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.external.xml + +# PRODUCT_PACKAGES += \ +# android.hardware.camera.provider@2.5-service_64 +# # camera.libcamera \ +# # ipa_rpi_pisp + +# PRODUCT_COPY_FILES += \ +# $(DEVICE_PATH)/camera/android.hardware.camera.provider@2.5-service_64.opi.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/android.hardware.camera.provider@2.5-service_64.opi.rc + +# # PRODUCT_COPY_FILES += \ +# # $(DEVICE_PATH)/camera/camera_hal.yaml:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/camera_hal.yaml \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx219.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx219.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx219_noir.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx219_noir.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx296.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx296.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx296_mono.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx296_mono.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx477.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx477.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx477_noir.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx477_noir.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx477_scientific.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx477_scientific.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx500.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx500.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx519.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx519.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx708.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx708.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx708_noir.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx708_noir.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx708_wide.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx708_wide.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/imx708_wide_noir.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/imx708_wide_noir.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/ov5647.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/ov5647.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/ov5647_noir.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/ov5647_noir.json \ +# # external/libcamera/src/ipa/rpi/pisp/data/ov64a40.json:$(TARGET_COPY_OUT_VENDOR)/etc/libcamera/ipa/rpi/pisp/ov64a40.json + +# PRODUCT_COPY_FILES += \ +# frameworks/native/data/etc/android.hardware.camera.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.xml \ +# frameworks/native/data/etc/android.hardware.camera.concurrent.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.concurrent.xml \ +# frameworks/native/data/etc/android.hardware.camera.flash-autofocus.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.flash-autofocus.xml \ +# frameworks/native/data/etc/android.hardware.camera.front.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.front.xml \ +# frameworks/native/data/etc/android.hardware.camera.full.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.full.xml \ +# frameworks/native/data/etc/android.hardware.camera.raw.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.raw.xml + +# PRODUCT_COPY_FILES += \ +# $(DEVICE_PATH)/camera/media_profiles_V1_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml + +# # CEC +# PRODUCT_PACKAGES += \ +# com.android.hardware.tv.hdmi.cec.opi5 \ +# com.android.hardware.tv.hdmi.connection.opi5 + +# PRODUCT_COPY_FILES += \ +# frameworks/native/data/etc/android.hardware.hdmi.cec.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.hdmi.cec.xml + +#cec +PRODUCT_PACKAGES += \ + com.android.hardware.tv.hdmi.connection.opi5 + +# Debugfs +PRODUCT_SET_DEBUGFS_RESTRICTIONS := false + +# DRM +PRODUCT_PACKAGES += \ + com.android.hardware.drm.clearkey + +# Emergency info +PRODUCT_PACKAGES += \ + EmergencyInfo + +# Ethernet +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.ethernet.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.ethernet.xml + +#FFmpeg +PRODUCT_PACKAGES += \ + com.android.hardware.media.c2.ffmpeg + + +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)/media/media_codecs_ffmpeg_c2.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_ffmpeg_c2.xml \ + $(DEVICE_PATH)/seccomp_policy/android.hardware.media.c2-ffmpeg.policy:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/android.hardware.media.c2-ffmpeg.policy + +# Gatekeeper +PRODUCT_PACKAGES += \ + com.android.hardware.gatekeeper.nonsecure \ + +# Graphics +PRODUCT_SOONG_NAMESPACES += external/mesa3d-panfrost +PRODUCT_PACKAGES += \ + com.android.hardware.graphics.allocator.minigbm_gbm_mesa + +PRODUCT_PACKAGES += \ + libEGL_mesa \ + libGLESv1_CM_mesa \ + libGLESv2_mesa \ + libgallium_dri + +PRODUCT_PACKAGES += \ + com.android.hardware.vulkan.panfrost + + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.software.opengles.deqp.level-2024-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.opengles.deqp.level.xml + + + +PRODUCT_PACKAGES += \ + com.android.hardware.graphics.composer.drm_hwcomposer + + +# Health +PRODUCT_PACKAGES += \ + com.android.hardware.health.opi5 + +# HIDL +PRODUCT_PACKAGES += \ + android.hidl.allocator@1.0-service \ + hwservicemanager + +# Kernel +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)-kernel/Image:$(PRODUCT_OUT)/kernel + +# Keylayout +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)/keylayout/Generic.kl:$(TARGET_COPY_OUT_VENDOR)/usr/keylayout/Generic.kl + +# Keymint +PRODUCT_PACKAGES += \ + com.android.hardware.keymint.rust_nonsecure + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.keystore.app_attest_key.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.keystore.app_attest_key.xml + + # Lights + PRODUCT_PACKAGES += \ + com.android.hardware.light.opi5 + +# Media +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)/media/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \ + frameworks/av/media/libstagefright/data/media_codecs_google_c2_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_c2_audio.xml \ + frameworks/av/media/libstagefright/data/media_codecs_google_c2_tv.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_c2_tv.xml \ + frameworks/av/media/libstagefright/data/media_codecs_google_c2_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_c2_video.xml + +# Power +PRODUCT_PACKAGES += \ + com.android.hardware.power + +# Ramdisk +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)/ramdisk/fstab.opi5ultra:$(TARGET_COPY_OUT_RAMDISK)/fstab.opi5ultra \ + $(DEVICE_PATH)/ramdisk/fstab.opi5ultra:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.opi5ultra \ + $(DEVICE_PATH)/ramdisk/init.opi5ultra.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/init.opi5ultra.rc \ + $(DEVICE_PATH)/ramdisk/init.opi5ultra.usb.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/init.opi5ultra.usb.rc \ + $(DEVICE_PATH)/ramdisk/ueventd.opi5ultra.rc:$(TARGET_COPY_OUT_VENDOR)/etc/ueventd.rc + +# Seccomp +PRODUCT_COPY_FILES += \ + $(DEVICE_PATH)/seccomp_policy/mediacodec.policy:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/mediacodec.policy \ + $(DEVICE_PATH)/seccomp_policy/mediaswcodec.policy:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/mediaswcodec.policy + + +# Soong +PRODUCT_SOONG_NAMESPACES += $(DEVICE_PATH) +# Storage +$(call inherit-product, $(SRC_TARGET_DIR)/product/emulated_storage.mk) + + # Suspend +PRODUCT_PACKAGES += \ + com.android.hardware.suspend_blocker.opi5 + +# Thermal +PRODUCT_PACKAGES += \ + com.android.hardware.thermal + +# Touchscreen +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.touchscreen.multitouch.jazzhand.xml + + +# USB +PRODUCT_PACKAGES += \ + com.android.hardware.usb \ + com.android.hardware.usb.gadget.opi5 + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.usb.accessory.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.accessory.xml \ + frameworks/native/data/etc/android.hardware.usb.host.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.host.xml \ + frameworks/native/data/etc/android.software.midi.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.midi.xml + +# V4L-Utils +PRODUCT_PACKAGES += \ + cec-ctl \ + ir-keytable \ + media-ctl \ + v4l2-ctl \ + + +# Virtualization +$(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk) + + + +# Wifi +# PRODUCT_PACKAGES += \ +# android.hardware.wifi-service \ +# hostapd \ +# hostapd_cli \ +# libwpa_client \ +# wificond \ +# wpa_cli \ +# wpa_supplicant \ +# wpa_supplicant.conf + +# PRODUCT_COPY_FILES += \ +# hardware/broadcom/wlan/bcmdhd/config/wpa_supplicant_overlay.conf:$(TARGET_COPY_OUT_VENDOR)/etc/wifi/wpa_supplicant_overlay.conf + + +# Wifi +PRODUCT_PACKAGES += \ + com.android.hardware.wifi \ + com.android.hardware.wifi.hostapd.opi5 \ + com.android.hardware.wifi.supplicant.opi5 \ + libwpa_client \ + wificond + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.wifi.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.wifi.xml + +# Packages +PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true + +# Window extensions +$(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk) diff --git a/framework_compatibility_matrix.xml b/framework_compatibility_matrix.xml new file mode 100644 index 0000000..74b65ea --- /dev/null +++ b/framework_compatibility_matrix.xml @@ -0,0 +1,12 @@ + + + + android.hardware.media.c2 + 1 + + IComponentStore + ffmpeg + + + + diff --git a/hdmi/cec/Android.bp b/hdmi/cec/Android.bp new file mode 100644 index 0000000..8f1d93e --- /dev/null +++ b/hdmi/cec/Android.bp @@ -0,0 +1,54 @@ +// Copyright (C) 2021 The Android Open Source Project +// Copyright (C) 2025 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "android.hardware.tv.hdmi.cec-service.opi", + relative_install_path: "hw", + vendor: true, + srcs: [ + "HdmiCec.cpp", + "HdmiCecPort.cpp", + "main.cpp", + ], + shared_libs: [ + "android.hardware.tv.hdmi.cec-V1-ndk", + "libbase", + "libbinder_ndk", + "libcutils", + "libhardware", + "liblog", + ], + installable: false, +} + +prebuilt_etc { + name: "android.hardware.tv.hdmi.cec-service.opi.rc", + src: "android.hardware.tv.hdmi.cec-service.opi.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.tv.hdmi.cec-service.opi.xml", + src: "android.hardware.tv.hdmi.cec-service.opi.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.tv.hdmi.cec.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["android.hardware.tv.hdmi.cec-service.opi"], + prebuilts: [ + "android.hardware.tv.hdmi.cec-service.opi.rc", + "android.hardware.tv.hdmi.cec-service.opi.xml", + ], +} diff --git a/hdmi/cec/HdmiCec.cpp b/hdmi/cec/HdmiCec.cpp new file mode 100644 index 0000000..8ca412b --- /dev/null +++ b/hdmi/cec/HdmiCec.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "android.hardware.tv.hdmi.cec-service.opi" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "HdmiCec.h" + +#define PROPERTY_CEC_DEVICE "persist.vendor.hdmi.cec_device" + +using android::base::GetProperty; +using ndk::ScopedAStatus; +using std::string; + +namespace android { +namespace hardware { +namespace tv { +namespace hdmi { +namespace cec { +namespace implementation { + +HdmiCec::HdmiCec() { + mCecEnabled = false; + mWakeupEnabled = false; + mCecControlEnabled = false; + mCallback = nullptr; + + Result result = init(); + if (result != Result::SUCCESS) { + LOG(ERROR) << "Failed to init HDMI-CEC HAL"; + } +} + +HdmiCec::~HdmiCec() { + release(); +} + +ScopedAStatus HdmiCec::addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) { + if (addr < CecLogicalAddress::TV || addr >= CecLogicalAddress::BROADCAST) { + LOG(ERROR) << "Add logical address failed, Invalid address"; + *_aidl_return = Result::FAILURE_INVALID_ARGS; + return ScopedAStatus::ok(); + } + + cec_log_addrs cecLogAddrs; + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs); + if (ret) { + LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno); + *_aidl_return = Result::FAILURE_BUSY; + return ScopedAStatus::ok(); + } + + cecLogAddrs.cec_version = CEC_OP_CEC_VERSION_1_4; + cecLogAddrs.vendor_id = 0x000c03; // HDMI LLC vendor ID + + unsigned int logAddrType = CEC_LOG_ADDR_TYPE_UNREGISTERED; + unsigned int allDevTypes = 0; + unsigned int primDevType = 0xff; + switch (addr) { + case CecLogicalAddress::TV: + primDevType = CEC_OP_PRIM_DEVTYPE_TV; + logAddrType = CEC_LOG_ADDR_TYPE_TV; + allDevTypes = CEC_OP_ALL_DEVTYPE_TV; + break; + case CecLogicalAddress::RECORDER_1: + case CecLogicalAddress::RECORDER_2: + case CecLogicalAddress::RECORDER_3: + primDevType = CEC_OP_PRIM_DEVTYPE_RECORD; + logAddrType = CEC_LOG_ADDR_TYPE_RECORD; + allDevTypes = CEC_OP_ALL_DEVTYPE_RECORD; + break; + case CecLogicalAddress::TUNER_1: + case CecLogicalAddress::TUNER_2: + case CecLogicalAddress::TUNER_3: + case CecLogicalAddress::TUNER_4: + primDevType = CEC_OP_PRIM_DEVTYPE_TUNER; + logAddrType = CEC_LOG_ADDR_TYPE_TUNER; + allDevTypes = CEC_OP_ALL_DEVTYPE_TUNER; + break; + case CecLogicalAddress::PLAYBACK_1: + case CecLogicalAddress::PLAYBACK_2: + case CecLogicalAddress::PLAYBACK_3: + primDevType = CEC_OP_PRIM_DEVTYPE_PLAYBACK; + logAddrType = CEC_LOG_ADDR_TYPE_PLAYBACK; + allDevTypes = CEC_OP_ALL_DEVTYPE_PLAYBACK; + cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU; + break; + case CecLogicalAddress::AUDIO_SYSTEM: + primDevType = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM; + logAddrType = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM; + allDevTypes = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + break; + case CecLogicalAddress::FREE_USE: + primDevType = CEC_OP_PRIM_DEVTYPE_PROCESSOR; + logAddrType = CEC_LOG_ADDR_TYPE_SPECIFIC; + allDevTypes = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + case CecLogicalAddress::UNREGISTERED: + cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK; + break; + case CecLogicalAddress::BACKUP_1: + case CecLogicalAddress::BACKUP_2: + break; + } + + int logAddrIndex = cecLogAddrs.num_log_addrs; + cecLogAddrs.num_log_addrs += 1; + cecLogAddrs.log_addr[logAddrIndex] = static_cast(addr); + cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType; + cecLogAddrs.primary_device_type[logAddrIndex] = primDevType; + cecLogAddrs.all_device_types[logAddrIndex] = allDevTypes; + cecLogAddrs.features[logAddrIndex][0] = 0; + cecLogAddrs.features[logAddrIndex][1] = 0; + + ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs); + if (ret) { + LOG(ERROR) << "Add logical address failed for port " << mHdmiCecPorts[0]->mPortId + << ", Error = " << strerror(errno); + *_aidl_return = Result::FAILURE_BUSY; + return ScopedAStatus::ok(); + } + + *_aidl_return = Result::SUCCESS; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::clearLogicalAddress() { + cec_log_addrs cecLogAddrs; + memset(&cecLogAddrs, 0, sizeof(cecLogAddrs)); + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs); + if (ret) { + LOG(ERROR) << "Clear logical Address failed for port " << mHdmiCecPorts[0]->mPortId + << ", Error = " << strerror(errno); + } + + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) { + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::getCecVersion(int32_t* _aidl_return) { + *_aidl_return = CEC_OP_CEC_VERSION_1_4; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::getPhysicalAddress(int32_t* _aidl_return) { + uint16_t addr; + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr); + if (ret) { + LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno); + return ScopedAStatus::fromServiceSpecificError( + static_cast(Result::FAILURE_INVALID_STATE)); + } + + *_aidl_return = addr; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::getVendorId(int32_t* _aidl_return) { + *_aidl_return = 0x000c03; // HDMI LLC vendor ID + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::sendMessage(const CecMessage& message, SendMessageResult* _aidl_return) { + if (!mCecEnabled) { + *_aidl_return = SendMessageResult::FAIL; + return ScopedAStatus::ok(); + } + + cec_msg cecMsg; + memset(&cecMsg, 0, sizeof(cec_msg)); + + int initiator = static_cast(message.initiator); + int destination = static_cast(message.destination); + + cecMsg.msg[0] = (initiator << 4) | destination; + for (size_t i = 0; i < message.body.size(); ++i) { + cecMsg.msg[i + 1] = message.body[i]; + } + cecMsg.len = message.body.size() + 1; + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_TRANSMIT, &cecMsg); + if (ret) { + LOG(ERROR) << "Send message failed, Error = " << strerror(errno); + *_aidl_return = SendMessageResult::FAIL; + return ScopedAStatus::ok(); + } + + if (cecMsg.tx_status != CEC_TX_STATUS_OK) { + LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status; + } + + *_aidl_return = getSendMessageResult(cecMsg.tx_status); + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::setCallback(const std::shared_ptr& callback) { + if (mCallback != nullptr) { + mCallback = nullptr; + } + + if (callback != nullptr) { + mCallback = callback; + } + + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::setLanguage(const std::string& language __unused) { + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::enableWakeupByOtp(bool value) { + mWakeupEnabled = value; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::enableCec(bool value) { + mCecEnabled = value; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiCec::enableSystemCecControl(bool value) { + mCecControlEnabled = value; + return ScopedAStatus::ok(); +} + +// Initialise the cec file descriptor +Result HdmiCec::init() { + string cecDevice = GetProperty(PROPERTY_CEC_DEVICE, "cec0"); + if (cecDevice != "cec0" && cecDevice != "cec1") { + LOG(ERROR) << "Invalid CEC device " << cecDevice; + return Result::FAILURE_NOT_SUPPORTED; + } + + string devicePath = "/dev/" + cecDevice; + int portId = stoi(cecDevice.substr(3)); + + shared_ptr hdmiCecPort(new HdmiCecPort(portId)); + Result result = hdmiCecPort->init(devicePath.c_str()); + if (result != Result::SUCCESS) { + return Result::FAILURE_NOT_SUPPORTED; + } + + thread eventThread(&HdmiCec::event_thread, this, hdmiCecPort.get()); + mEventThreads.push_back(std::move(eventThread)); + mHdmiCecPorts.push_back(std::move(hdmiCecPort)); + LOG(INFO) << "Using CEC device " << devicePath; + + mCecEnabled = true; + mWakeupEnabled = true; + mCecControlEnabled = true; + + return Result::SUCCESS; +} + +void HdmiCec::release() { + mCecEnabled = false; + mWakeupEnabled = false; + mCecControlEnabled = false; + + for (thread& eventThread : mEventThreads) { + if (eventThread.joinable()) { + eventThread.join(); + } + } + setCallback(nullptr); + mHdmiCecPorts.clear(); + mEventThreads.clear(); +} + +void HdmiCec::event_thread(HdmiCecPort* hdmiCecPort) { + struct pollfd ufds[3] = { + {hdmiCecPort->mCecFd, POLLIN, 0}, + {hdmiCecPort->mCecFd, POLLERR, 0}, + {hdmiCecPort->mExitFd, POLLIN, 0}, + }; + + while (1) { + ufds[0].revents = 0; + ufds[1].revents = 0; + ufds[2].revents = 0; + + int ret = poll(ufds, /* size(ufds) = */ 3, /* timeout = */ -1); + + if (ret <= 0) { + continue; + } + + if (ufds[2].revents == POLLIN) { /* Exit */ + break; + } + + if (ufds[1].revents == POLLERR) { /* CEC Event */ + cec_event ev; + ret = ioctl(hdmiCecPort->mCecFd, CEC_DQEVENT, &ev); + + if (ret) { + LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno); + continue; + } + + if (!mCecEnabled) { + continue; + } + } + + if (ufds[0].revents == POLLIN) { /* CEC Driver */ + cec_msg msg = {}; + ret = ioctl(hdmiCecPort->mCecFd, CEC_RECEIVE, &msg); + + if (ret) { + LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno); + continue; + } + + if (msg.rx_status != CEC_RX_STATUS_OK) { + LOG(ERROR) << "msg rx_status = " << msg.rx_status; + continue; + } + + if (!mCecEnabled) { + continue; + } + + if (!mWakeupEnabled && isWakeupMessage(msg)) { + LOG(DEBUG) << "Filter wakeup message"; + continue; + } + + if (!mCecControlEnabled && !isTransferableInSleep(msg)) { + LOG(DEBUG) << "Filter message in standby mode"; + continue; + } + + if (mCallback != nullptr) { + size_t length = std::min(msg.len - 1, (uint32_t)(CEC_MESSAGE_BODY_MAX_LENGTH - 1)); + CecMessage cecMessage{ + .initiator = static_cast(msg.msg[0] >> 4), + .destination = static_cast(msg.msg[0] & 0xf), + }; + cecMessage.body.resize(length); + for (size_t i = 0; i < length; ++i) { + cecMessage.body[i] = static_cast(msg.msg[i + 1]); + } + mCallback->onCecMessage(cecMessage); + } else { + LOG(ERROR) << "no event callback for message"; + } + } + } +} + +int HdmiCec::getOpcode(cec_msg message) { + return static_cast(message.msg[1]); +} + +bool HdmiCec::isWakeupMessage(cec_msg message) { + int opcode = getOpcode(message); + switch (opcode) { + case CEC_MESSAGE_TEXT_VIEW_ON: + case CEC_MESSAGE_IMAGE_VIEW_ON: + return true; + default: + return false; + } +} + +bool HdmiCec::isTransferableInSleep(cec_msg message) { + int opcode = getOpcode(message); + switch (opcode) { + case CEC_MESSAGE_ABORT: + case CEC_MESSAGE_DEVICE_VENDOR_ID: + case CEC_MESSAGE_GET_CEC_VERSION: + case CEC_MESSAGE_GET_MENU_LANGUAGE: + case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS: + case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID: + case CEC_MESSAGE_GIVE_OSD_NAME: + case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS: + case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS: + case CEC_MESSAGE_REPORT_POWER_STATUS: + case CEC_MESSAGE_SET_OSD_NAME: + case CEC_MESSAGE_DECK_CONTROL: + case CEC_MESSAGE_PLAY: + case CEC_MESSAGE_IMAGE_VIEW_ON: + case CEC_MESSAGE_TEXT_VIEW_ON: + case CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST: + return true; + case CEC_MESSAGE_USER_CONTROL_PRESSED: + return isPowerUICommand(message); + default: + return false; + } +} + +int HdmiCec::getFirstParam(cec_msg message) { + return static_cast(message.msg[2]); +} + +bool HdmiCec::isPowerUICommand(cec_msg message) { + int uiCommand = getFirstParam(message); + switch (uiCommand) { + case CEC_OP_UI_CMD_POWER: + case CEC_OP_UI_CMD_DEVICE_ROOT_MENU: + case CEC_OP_UI_CMD_POWER_ON_FUNCTION: + return true; + default: + return false; + } +} + +SendMessageResult HdmiCec::getSendMessageResult(int tx_status) { + switch (tx_status) { + case CEC_TX_STATUS_OK: + return SendMessageResult::SUCCESS; + case CEC_TX_STATUS_ARB_LOST: + return SendMessageResult::BUSY; + case CEC_TX_STATUS_NACK: + return SendMessageResult::NACK; + default: + return SendMessageResult::FAIL; + } +} + +} // namespace implementation +} // namespace cec +} // namespace hdmi +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/hdmi/cec/HdmiCec.h b/hdmi/cec/HdmiCec.h new file mode 100644 index 0000000..68b62fb --- /dev/null +++ b/hdmi/cec/HdmiCec.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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 +#include +#include +#include +#include +#include "HdmiCecPort.h" + +namespace android { +namespace hardware { +namespace tv { +namespace hdmi { +namespace cec { +namespace implementation { + +using std::shared_ptr; +using std::thread; +using std::vector; + +using ::aidl::android::hardware::tv::hdmi::cec::BnHdmiCec; +using ::aidl::android::hardware::tv::hdmi::cec::CecLogicalAddress; +using ::aidl::android::hardware::tv::hdmi::cec::CecMessage; +using ::aidl::android::hardware::tv::hdmi::cec::IHdmiCec; +using ::aidl::android::hardware::tv::hdmi::cec::IHdmiCecCallback; +using ::aidl::android::hardware::tv::hdmi::cec::Result; +using ::aidl::android::hardware::tv::hdmi::cec::SendMessageResult; + +struct HdmiCec : public BnHdmiCec { + public: + HdmiCec(); + ~HdmiCec(); + ::ndk::ScopedAStatus addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) override; + ::ndk::ScopedAStatus clearLogicalAddress() override; + ::ndk::ScopedAStatus enableAudioReturnChannel(int32_t portId, bool enable) override; + ::ndk::ScopedAStatus getCecVersion(int32_t* _aidl_return) override; + ::ndk::ScopedAStatus getPhysicalAddress(int32_t* _aidl_return) override; + ::ndk::ScopedAStatus getVendorId(int32_t* _aidl_return) override; + ::ndk::ScopedAStatus sendMessage(const CecMessage& message, + SendMessageResult* _aidl_return) override; + ::ndk::ScopedAStatus setCallback(const std::shared_ptr& callback) override; + ::ndk::ScopedAStatus setLanguage(const std::string& language) override; + ::ndk::ScopedAStatus enableWakeupByOtp(bool value) override; + ::ndk::ScopedAStatus enableCec(bool value) override; + ::ndk::ScopedAStatus enableSystemCecControl(bool value) override; + + Result init(); + void release(); + + private: + void event_thread(HdmiCecPort* hdmiCecPort); + static int getOpcode(cec_msg message); + static int getFirstParam(cec_msg message); + static bool isWakeupMessage(cec_msg message); + static bool isTransferableInSleep(cec_msg message); + static bool isPowerUICommand(cec_msg message); + static SendMessageResult getSendMessageResult(int tx_status); + + vector mEventThreads; + vector> mHdmiCecPorts; + + // When set to false, all the CEC commands are discarded. True by default after initialization. + bool mCecEnabled; + /* + * When set to false, HAL does not wake up the system upon receiving or + * . True by default after initialization. + */ + bool mWakeupEnabled; + /* + * Updated when system goes into or comes out of standby mode. + * When set to true, Android system is handling CEC commands. + * When set to false, microprocessor is handling CEC commands. + * True by default after initialization. + */ + bool mCecControlEnabled; + + std::shared_ptr mCallback; +}; + +} // namespace implementation +} // namespace cec +} // namespace hdmi +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/hdmi/cec/HdmiCecPort.cpp b/hdmi/cec/HdmiCecPort.cpp new file mode 100644 index 0000000..c163169 --- /dev/null +++ b/hdmi/cec/HdmiCecPort.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "android.hardware.tv.hdmi.cec-service.opi" + +#include +#include +#include +#include +#include +#include + +#include "HdmiCecPort.h" + +namespace android { +namespace hardware { +namespace tv { +namespace hdmi { +namespace cec { +namespace implementation { + +HdmiCecPort::HdmiCecPort(unsigned int portId) { + mPortId = portId; + mCecFd = -1; + mExitFd = -1; +} + +HdmiCecPort::~HdmiCecPort() { + release(); +} + +// Initialise the cec file descriptor +Result HdmiCecPort::init(const char* path) { + mCecFd = open(path, O_RDWR); + if (mCecFd < 0) { + LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno); + return Result::FAILURE_NOT_SUPPORTED; + } + mExitFd = eventfd(0, EFD_NONBLOCK); + if (mExitFd < 0) { + LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno); + release(); + return Result::FAILURE_NOT_SUPPORTED; + } + + // Ensure the CEC device supports required capabilities + struct cec_caps caps = {}; + int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps); + if (ret) { + LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno); + release(); + return Result::FAILURE_NOT_SUPPORTED; + } + + if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) { + LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities; + release(); + return Result::FAILURE_NOT_SUPPORTED; + } + + uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU; + ret = ioctl(mCecFd, CEC_S_MODE, &mode); + if (ret) { + LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno); + release(); + return Result::FAILURE_NOT_SUPPORTED; + } + return Result::SUCCESS; +} + +void HdmiCecPort::release() { + if (mExitFd > 0) { + uint64_t tmp = 1; + write(mExitFd, &tmp, sizeof(tmp)); + } + if (mExitFd > 0) { + close(mExitFd); + } + if (mCecFd > 0) { + close(mCecFd); + } +} + +} // namespace implementation +} // namespace cec +} // namespace hdmi +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/hdmi/cec/HdmiCecPort.h b/hdmi/cec/HdmiCecPort.h new file mode 100644 index 0000000..c74c642 --- /dev/null +++ b/hdmi/cec/HdmiCecPort.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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 + +namespace android { +namespace hardware { +namespace tv { +namespace hdmi { +namespace cec { +namespace implementation { + +using ::aidl::android::hardware::tv::hdmi::cec::Result; + +class HdmiCecPort { + public: + HdmiCecPort(unsigned int portId); + ~HdmiCecPort(); + Result init(const char* path); + void release(); + + unsigned int mPortId; + int mCecFd; + int mExitFd; +}; + +} // namespace implementation +} // namespace cec +} // namespace hdmi +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/hdmi/cec/android.hardware.tv.hdmi.cec-service.opi.rc b/hdmi/cec/android.hardware.tv.hdmi.cec-service.opi.rc new file mode 100644 index 0000000..210a6bf --- /dev/null +++ b/hdmi/cec/android.hardware.tv.hdmi.cec-service.opi.rc @@ -0,0 +1,5 @@ +service vendor.cec-opi /apex/com.android.hardware.tv.hdmi.cec.opi5/bin/hw/android.hardware.tv.hdmi.cec-service.opi + interface aidl android.hardware.tv.hdmi.cec.IHdmiCec/default + class hal + user system + group system diff --git a/hdmi/cec/android.hardware.tv.hdmi.cec-service.opi.xml b/hdmi/cec/android.hardware.tv.hdmi.cec-service.opi.xml new file mode 100644 index 0000000..d48565c --- /dev/null +++ b/hdmi/cec/android.hardware.tv.hdmi.cec-service.opi.xml @@ -0,0 +1,10 @@ + + + android.hardware.tv.hdmi.cec + 1 + + IHdmiCec + default + + + diff --git a/hdmi/cec/apex_file_contexts b/hdmi/cec/apex_file_contexts new file mode 100644 index 0000000..881bf98 --- /dev/null +++ b/hdmi/cec/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.tv\.hdmi\.cec-service\.opi u:object_r:hal_tv_hdmi_cec_default_exec:s0 diff --git a/hdmi/cec/apex_manifest.json b/hdmi/cec/apex_manifest.json new file mode 100644 index 0000000..46e5822 --- /dev/null +++ b/hdmi/cec/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.tv.hdmi.cec.opi5", + "version": 1 +} diff --git a/hdmi/cec/main.cpp b/hdmi/cec/main.cpp new file mode 100644 index 0000000..597eb4d --- /dev/null +++ b/hdmi/cec/main.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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 "HdmiCec.h" + +#include +#include +#include + +using android::hardware::tv::hdmi::cec::implementation::HdmiCec; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + std::shared_ptr hdmiCecAidl = ndk::SharedRefBase::make(); + const std::string instance = std::string() + HdmiCec::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(hdmiCecAidl->asBinder().get(), instance.c_str()); + CHECK_EQ(status, STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/hdmi/connection/Android.bp b/hdmi/connection/Android.bp new file mode 100644 index 0000000..a26f1bf --- /dev/null +++ b/hdmi/connection/Android.bp @@ -0,0 +1,51 @@ +// Copyright (C) 2022 The Android Open Source Project +// Copyright (C) 2025 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "android.hardware.tv.hdmi.connection-service.opi", + relative_install_path: "hw", + vendor: true, + srcs: [ + "HdmiConnection.cpp", + "main.cpp", + ], + shared_libs: [ + "android.hardware.tv.hdmi.connection-V1-ndk", + "libbase", + "libbinder_ndk", + "liblog", + ], + installable: false, +} + +prebuilt_etc { + name: "android.hardware.tv.hdmi.connection-service.opi.rc", + src: "android.hardware.tv.hdmi.connection-service.opi.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.tv.hdmi.connection-service.opi.xml", + src: "android.hardware.tv.hdmi.connection-service.opi.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.tv.hdmi.connection.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["android.hardware.tv.hdmi.connection-service.opi"], + prebuilts: [ + "android.hardware.tv.hdmi.connection-service.opi.rc", + "android.hardware.tv.hdmi.connection-service.opi.xml", + ], +} diff --git a/hdmi/connection/HdmiConnection.cpp b/hdmi/connection/HdmiConnection.cpp new file mode 100644 index 0000000..9bd6c57 --- /dev/null +++ b/hdmi/connection/HdmiConnection.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "android.hardware.tv.hdmi.connection-service.opi" + +#include "HdmiConnection.h" + +#include +#include + +using android::base::ReadFileToString; +using ndk::ScopedAStatus; +using std::string; + +namespace android { +namespace hardware { +namespace tv { +namespace hdmi { +namespace connection { +namespace implementation { + +static const string drmCard = "card0"; + +HdmiConnection::HdmiConnection() { + mCallback = nullptr; + + for (int i = 0; i < 2; i++) { + mPortInfos.push_back( + {.type = HdmiPortType::OUTPUT, + .portId = i, + .cecSupported = true, + .arcSupported = false, + .eArcSupported = false, + .physicalAddress = 0xFFFF}); + mHpdSignal.push_back(HpdSignal::HDMI_HPD_PHYSICAL); + } +} + +HdmiConnection::~HdmiConnection() { + if (mCallback != nullptr) { + mCallback = nullptr; + } +} + +ScopedAStatus HdmiConnection::getPortInfo(std::vector* _aidl_return) { + *_aidl_return = mPortInfos; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiConnection::isConnected(int32_t portId, bool* _aidl_return) { + if (portId != 0 && portId != 1) { + *_aidl_return = false; + return ScopedAStatus::ok(); + } + + bool connected = false; + string hdmiStatusPath = "/sys/class/drm/" + drmCard + "-HDMI-A-" + to_string(portId + 1) + "/status"; + if (!access(hdmiStatusPath.c_str(), R_OK)) { + string connectedValue; + if (ReadFileToString(hdmiStatusPath, &connectedValue)) { + connected = !connectedValue.compare("connected\n"); + } + } + LOG(INFO) << "portId: " << portId << ", connected: " << connected; + + *_aidl_return = connected; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiConnection::setCallback(const std::shared_ptr& callback) { + if (mCallback != nullptr) { + mCallback = nullptr; + } + + if (callback != nullptr) { + mCallback = callback; + } + + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiConnection::setHpdSignal(HpdSignal signal, int32_t portId) { + if (portId != 0 && portId != 1) { + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + mHpdSignal.at(portId) = signal; + return ScopedAStatus::ok(); +} + +ScopedAStatus HdmiConnection::getHpdSignal(int32_t portId, HpdSignal* _aidl_return) { + if (portId != 0 && portId != 1) { + return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + *_aidl_return = mHpdSignal.at(portId); + return ScopedAStatus::ok(); +} + +} // namespace implementation +} // namespace connection +} // namespace hdmi +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/hdmi/connection/HdmiConnection.h b/hdmi/connection/HdmiConnection.h new file mode 100644 index 0000000..716b9da --- /dev/null +++ b/hdmi/connection/HdmiConnection.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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 +#include +#include +#include + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace hdmi { +namespace connection { +namespace implementation { + +using ::aidl::android::hardware::tv::hdmi::connection::BnHdmiConnection; +using ::aidl::android::hardware::tv::hdmi::connection::HdmiPortInfo; +using ::aidl::android::hardware::tv::hdmi::connection::HdmiPortType; +using ::aidl::android::hardware::tv::hdmi::connection::HpdSignal; +using ::aidl::android::hardware::tv::hdmi::connection::IHdmiConnection; +using ::aidl::android::hardware::tv::hdmi::connection::IHdmiConnectionCallback; +using ::aidl::android::hardware::tv::hdmi::connection::Result; + +struct HdmiConnection : public BnHdmiConnection { + HdmiConnection(); + ~HdmiConnection(); + ::ndk::ScopedAStatus getPortInfo(std::vector* _aidl_return) override; + ::ndk::ScopedAStatus isConnected(int32_t portId, bool* _aidl_return) override; + ::ndk::ScopedAStatus setCallback( + const std::shared_ptr& callback) override; + ::ndk::ScopedAStatus setHpdSignal(HpdSignal signal, int32_t portId) override; + ::ndk::ScopedAStatus getHpdSignal(int32_t portId, HpdSignal* _aidl_return) override; + + private: + std::vector mPortInfos; + std::vector mHpdSignal; + + std::shared_ptr mCallback; +}; + +} // namespace implementation +} // namespace connection +} // namespace hdmi +} // Namespace tv +} // namespace hardware +} // namespace android diff --git a/hdmi/connection/android.hardware.tv.hdmi.connection-service.opi.rc b/hdmi/connection/android.hardware.tv.hdmi.connection-service.opi.rc new file mode 100644 index 0000000..70c4ed2 --- /dev/null +++ b/hdmi/connection/android.hardware.tv.hdmi.connection-service.opi.rc @@ -0,0 +1,5 @@ +service vendor.hdmi-opi /apex/com.android.hardware.tv.hdmi.connection.opi5/bin/hw/android.hardware.tv.hdmi.connection-service.opi + interface aidl android.hardware.tv.hdmi.connection.IHdmiConnection/default + class hal + user system + group system diff --git a/hdmi/connection/android.hardware.tv.hdmi.connection-service.opi.xml b/hdmi/connection/android.hardware.tv.hdmi.connection-service.opi.xml new file mode 100644 index 0000000..144fef1 --- /dev/null +++ b/hdmi/connection/android.hardware.tv.hdmi.connection-service.opi.xml @@ -0,0 +1,10 @@ + + + android.hardware.tv.hdmi.connection + 1 + + IHdmiConnection + default + + + diff --git a/hdmi/connection/apex_file_contexts b/hdmi/connection/apex_file_contexts new file mode 100644 index 0000000..5dc54c7 --- /dev/null +++ b/hdmi/connection/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.tv\.hdmi\.connection-service\.opi u:object_r:hal_tv_hdmi_connection_default_exec:s0 diff --git a/hdmi/connection/apex_manifest.json b/hdmi/connection/apex_manifest.json new file mode 100644 index 0000000..0733781 --- /dev/null +++ b/hdmi/connection/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.tv.hdmi.connection.opi5", + "version": 1 +} diff --git a/hdmi/connection/main.cpp b/hdmi/connection/main.cpp new file mode 100644 index 0000000..2b1f991 --- /dev/null +++ b/hdmi/connection/main.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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 "HdmiConnection.h" + +#include +#include +#include + +using android::hardware::tv::hdmi::connection::implementation::HdmiConnection; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + std::shared_ptr hdmiAidl = ndk::SharedRefBase::make(); + const std::string instance = std::string() + HdmiConnection::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(hdmiAidl->asBinder().get(), instance.c_str()); + CHECK_EQ(status, STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/health/Android.bp b/health/Android.bp new file mode 100644 index 0000000..e089055 --- /dev/null +++ b/health/Android.bp @@ -0,0 +1,59 @@ +// Copyright (C) 2018 The Android Open Source Project +// Copyright (C) 2023 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "android.hardware.health-service.opi", + relative_install_path: "hw", + vendor: true, + srcs: [ + "HealthImpl.cpp", + "main.cpp", + ], + static_libs: [ + "libbatterymonitor", + "libhealth_aidl_impl", + "libhealthloop", + ], + shared_libs: [ + "android.hardware.health-V4-ndk", + "libbase", + "libbinder_ndk", + "libcutils", + "liblog", + "libutils", + ], + overrides: ["charger"], + installable: false, +} + +prebuilt_etc { + name: "android.hardware.health-service.opi.rc", + src: "android.hardware.health-service.opi.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.health-service.opi.xml", + src: "android.hardware.health-service.opi.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.health.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["android.hardware.health-service.opi"], + prebuilts: [ + "android.hardware.health-service.opi.rc", + "android.hardware.health-service.opi.xml", + ], +} diff --git a/health/HealthImpl.cpp b/health/HealthImpl.cpp new file mode 100644 index 0000000..44c7077 --- /dev/null +++ b/health/HealthImpl.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 KonstaKANG + * + * 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 "HealthImpl.h" + +using ::aidl::android::hardware::health::BatteryHealth; +using ::aidl::android::hardware::health::BatteryStatus; +using ::aidl::android::hardware::health::HealthInfo; + +namespace aidl::android::hardware::health { + +void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) { + health_info->chargerAcOnline = true; + health_info->chargerUsbOnline = true; + health_info->chargerWirelessOnline = false; + health_info->chargerDockOnline = false; + health_info->maxChargingCurrentMicroamps = 500000; + health_info->maxChargingVoltageMicrovolts = 5000000; + health_info->batteryStatus = BatteryStatus::FULL; + health_info->batteryHealth = BatteryHealth::GOOD; + health_info->batteryPresent = true; + health_info->batteryLevel = 100; + health_info->batteryVoltageMillivolts = 5000; + health_info->batteryTemperatureTenthsCelsius = 250; + health_info->batteryCurrentMicroamps = 500000; + health_info->batteryCycleCount = 25; + health_info->batteryFullChargeUah = 5000000; + health_info->batteryChargeCounterUah = 5000000; + health_info->batteryTechnology = "Li-ion"; + health_info->batteryCapacityLevel = BatteryCapacityLevel::FULL; + health_info->batteryFullChargeDesignCapacityUah = 5000000; +} + +ndk::ScopedAStatus HealthImpl::getChargeCounterUah(int32_t* out) { + *out = 5000000; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus HealthImpl::getCurrentNowMicroamps(int32_t* out) { + *out = 500000; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus HealthImpl::getCurrentAverageMicroamps(int32_t*) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +} + +ndk::ScopedAStatus HealthImpl::getCapacity(int32_t* out) { + *out = 100; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus HealthImpl::getChargeStatus(BatteryStatus* out) { + *out = BatteryStatus::FULL; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus HealthImpl::getBatteryHealthData(BatteryHealthData* out) { + out->batteryManufacturingDateSeconds = 1231006505; + out->batteryFirstUsageSeconds = 1231469665; + out->batteryStateOfHealth = 99; + out->batterySerialNumber = + "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"; + out->batteryPartStatus = BatteryPartStatus::ORIGINAL; + return ndk::ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::health diff --git a/health/HealthImpl.h b/health/HealthImpl.h new file mode 100644 index 0000000..cdad615 --- /dev/null +++ b/health/HealthImpl.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 KonstaKANG + * + * 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 + +using ::aidl::android::hardware::health::Health; + +namespace aidl::android::hardware::health { + +class HealthImpl : public Health { +public: + using Health::Health; + virtual ~HealthImpl() {} + + ndk::ScopedAStatus getChargeCounterUah(int32_t* out) override; + ndk::ScopedAStatus getCurrentNowMicroamps(int32_t* out) override; + ndk::ScopedAStatus getCurrentAverageMicroamps(int32_t* out) override; + ndk::ScopedAStatus getCapacity(int32_t* out) override; + ndk::ScopedAStatus getChargeStatus(BatteryStatus* out) override; + ndk::ScopedAStatus getBatteryHealthData(BatteryHealthData* out) override; + +protected: + void UpdateHealthInfo(HealthInfo* health_info) override; +}; + +} // namespace aidl::android::hardware::health diff --git a/health/android.hardware.health-service.opi.rc b/health/android.hardware.health-service.opi.rc new file mode 100644 index 0000000..493b9fd --- /dev/null +++ b/health/android.hardware.health-service.opi.rc @@ -0,0 +1,6 @@ +service vendor.health-opi /apex/com.android.hardware.health.opi5/bin/hw/android.hardware.health-service.opi + class hal + user system + group system + capabilities WAKE_ALARM BLOCK_SUSPEND + file /dev/kmsg w diff --git a/health/android.hardware.health-service.opi.xml b/health/android.hardware.health-service.opi.xml new file mode 100644 index 0000000..8ddfbda --- /dev/null +++ b/health/android.hardware.health-service.opi.xml @@ -0,0 +1,7 @@ + + + android.hardware.health + 4 + IHealth/default + + diff --git a/health/apex_file_contexts b/health/apex_file_contexts new file mode 100644 index 0000000..07c184d --- /dev/null +++ b/health/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.health-service\.opi u:object_r:hal_health_default_exec:s0 diff --git a/health/apex_manifest.json b/health/apex_manifest.json new file mode 100644 index 0000000..6d240e7 --- /dev/null +++ b/health/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.health.opi5", + "version": 1 +} diff --git a/health/main.cpp b/health/main.cpp new file mode 100644 index 0000000..877780e --- /dev/null +++ b/health/main.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 KonstaKANG + * + * 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 "HealthImpl.h" + +#include +#include + +using ::aidl::android::hardware::health::HalHealthLoop; +using ::aidl::android::hardware::health::HealthImpl; + +int main() { + auto config = std::make_unique(); + android::hardware::health::InitHealthdConfig(config.get()); + auto binder = ndk::SharedRefBase::make("default", std::move(config)); + auto hal_health_loop = std::make_shared(binder, binder); + return hal_health_loop->StartLoop(); +} diff --git a/keylayout/Generic.kl b/keylayout/Generic.kl new file mode 100644 index 0000000..697fd1f --- /dev/null +++ b/keylayout/Generic.kl @@ -0,0 +1,501 @@ +# Copyright (C) 2010 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. + +# +# Generic key layout file for full alphabetic US English PC style external keyboards. +# +# This file is intentionally very generic and is intended to support a broad range of keyboards. +# Do not edit the generic key layout to support a specific keyboard; instead, create +# a new key layout file with the required keyboard configuration. +# + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 55 NUMPAD_MULTIPLY +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 69 NUM_LOCK +key 70 SCROLL_LOCK +key 71 NUMPAD_7 +key 72 NUMPAD_8 +key 73 NUMPAD_9 +key 74 NUMPAD_SUBTRACT +key 75 NUMPAD_4 +key 76 NUMPAD_5 +key 77 NUMPAD_6 +key 78 NUMPAD_ADD +key 79 NUMPAD_1 +key 80 NUMPAD_2 +key 81 NUMPAD_3 +key 82 NUMPAD_0 +key 83 NUMPAD_DOT +# key 84 (undefined) +key 85 ZENKAKU_HANKAKU +key 86 BACKSLASH +key 87 F11 +key 88 F12 +key 89 RO +# key 90 "KEY_KATAKANA" +# key 91 "KEY_HIRAGANA" +key 92 HENKAN +key 93 KATAKANA_HIRAGANA +key 94 MUHENKAN +key 95 NUMPAD_COMMA +key 96 NUMPAD_ENTER +key 97 CTRL_RIGHT +key 98 NUMPAD_DIVIDE +key 99 SYSRQ +key 100 ALT_RIGHT +# key 101 "KEY_LINEFEED" +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 INSERT +key 111 FORWARD_DEL +# key 112 "KEY_MACRO" +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 116 POWER +key 117 NUMPAD_EQUALS +# key 118 "KEY_KPPLUSMINUS" +key 119 BREAK +key 120 RECENT_APPS +key 121 NUMPAD_COMMA +key 122 KANA +key 123 EISU +key 124 YEN +key 125 META_LEFT +key 126 META_RIGHT +key 127 MENU +key 128 MEDIA_STOP +# key 129 "KEY_AGAIN" +# key 130 "KEY_PROPS" +# key 131 "KEY_UNDO" +# key 132 "KEY_FRONT" +key 133 COPY +# key 134 "KEY_OPEN" +key 135 PASTE +# key 136 "KEY_FIND" +key 137 CUT +# key 138 "KEY_HELP" +key 139 MENU +key 140 CALCULATOR +# key 141 "KEY_SETUP" +key 142 SLEEP +key 143 WAKEUP +# key 144 "KEY_FILE" +# key 145 "KEY_SENDFILE" +# key 146 "KEY_DELETEFILE" +# key 147 "KEY_XFER" +# key 148 "KEY_PROG1" +# key 149 "KEY_PROG2" +key 150 EXPLORER +# key 151 "KEY_MSDOS" +key 152 LOCK +# key 153 "KEY_DIRECTION" +# key 154 "KEY_CYCLEWINDOWS" +key 155 ENVELOPE +key 156 BOOKMARK +# key 157 "KEY_COMPUTER" +key 158 BACK +key 159 FORWARD +key 160 MEDIA_CLOSE +key 161 MEDIA_EJECT +key 162 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 169 CALL +# key 170 "KEY_ISO" +key 171 MUSIC +key 172 HOME +key 173 REFRESH +# key 174 "KEY_EXIT" +# key 175 "KEY_MOVE" +# key 176 "KEY_EDIT" +key 177 PAGE_UP +key 178 PAGE_DOWN +key 179 NUMPAD_LEFT_PAREN +key 180 NUMPAD_RIGHT_PAREN +key 181 NEW +# key 182 "KEY_REDO" +key 183 F13 +key 184 F14 +key 185 F15 +key 186 F16 +key 187 F17 +key 188 F18 +key 189 F19 +key 190 F20 +key 191 F21 +key 192 F22 +key 193 F23 +key 194 F24 +# key 195 (undefined) +# key 196 (undefined) +# key 197 (undefined) +# key 198 (undefined) +# key 199 (undefined) +key 200 MEDIA_PLAY +key 201 MEDIA_PAUSE +# key 202 "KEY_PROG3" +# key 203 "KEY_PROG4" +key 204 NOTIFICATION +# key 205 "KEY_SUSPEND" +key 206 CLOSE +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +# key 209 "KEY_BASSBOOST" +key 210 PRINT +# key 211 "KEY_HP" +key 212 CAMERA +key 213 MUSIC +# key 214 "KEY_QUESTION" +key 215 ENVELOPE +# key 216 "KEY_CHAT" +key 217 SEARCH +# key 218 "KEY_CONNECT" +# key 219 "KEY_FINANCE" +# key 220 "KEY_SPORT" +# key 221 "KEY_SHOP" +# key 222 "KEY_ALTERASE" +# key 223 "KEY_CANCEL" +key 224 BRIGHTNESS_DOWN +key 225 BRIGHTNESS_UP +key 226 HEADSETHOOK +key 228 KEYBOARD_BACKLIGHT_TOGGLE +key 229 KEYBOARD_BACKLIGHT_DOWN +key 230 KEYBOARD_BACKLIGHT_UP +key 248 MUTE + +key 256 BUTTON_1 +key 257 BUTTON_2 +key 258 BUTTON_3 +key 259 BUTTON_4 +key 260 BUTTON_5 +key 261 BUTTON_6 +key 262 BUTTON_7 +key 263 BUTTON_8 +key 264 BUTTON_9 +key 265 BUTTON_10 +key 266 BUTTON_11 +key 267 BUTTON_12 +key 268 BUTTON_13 +key 269 BUTTON_14 +key 270 BUTTON_15 +key 271 BUTTON_16 + +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 + + +key 304 BUTTON_A +key 305 BUTTON_B +key 306 BUTTON_C +key 307 BUTTON_X +key 308 BUTTON_Y +key 309 BUTTON_Z +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 +key 314 BUTTON_SELECT +key 315 BUTTON_START +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + + +key 329 STYLUS_BUTTON_TERTIARY +key 331 STYLUS_BUTTON_PRIMARY +key 332 STYLUS_BUTTON_SECONDARY + + +# key 352 "KEY_OK" +key 353 DPAD_CENTER +# key 354 "KEY_GOTO" +# key 355 "KEY_CLEAR" +# key 356 "KEY_POWER2" +# key 357 "KEY_OPTION" +# key 358 "KEY_INFO" +# key 359 "KEY_TIME" +# key 360 "KEY_VENDOR" +# key 361 "KEY_ARCHIVE" +key 362 GUIDE +# key 363 "KEY_CHANNEL" +# key 364 "KEY_FAVORITES" +# key 365 "KEY_EPG" +key 366 DVR +# key 367 "KEY_MHP" +key 368 LANGUAGE_SWITCH +# key 369 "KEY_TITLE" +key 370 CAPTIONS +# key 371 "KEY_ANGLE" +key 372 FULLSCREEN +# key 373 "KEY_MODE" +# key 374 "KEY_KEYBOARD" +# key 375 "KEY_SCREEN" +# key 376 "KEY_PC" +key 377 TV +# key 378 "KEY_TV2" +# key 379 "KEY_VCR" +# key 380 "KEY_VCR2" +# key 381 "KEY_SAT" +# key 382 "KEY_SAT2" +# key 383 "KEY_CD" +# key 384 "KEY_TAPE" +# key 385 "KEY_RADIO" +# key 386 "KEY_TUNER" +# key 387 "KEY_PLAYER" +# key 388 "KEY_TEXT" +# key 389 "KEY_DVD" +# key 390 "KEY_AUX" +# key 391 "KEY_MP3" +# key 392 "KEY_AUDIO" +# key 393 "KEY_VIDEO" +# key 394 "KEY_DIRECTORY" +# key 395 "KEY_LIST" +# key 396 "KEY_MEMO" +key 397 CALENDAR +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +# key 404 "KEY_FIRST" +key 405 LAST_CHANNEL +# key 406 "KEY_AB" +# key 407 "KEY_NEXT" +# key 408 "KEY_RESTART" +# key 409 "KEY_SLOW" +# key 410 "KEY_SHUFFLE" +# key 411 "KEY_BREAK" +# key 412 "KEY_PREVIOUS" +# key 413 "KEY_DIGITS" +# key 414 "KEY_TEEN" +# key 415 "KEY_TWEN" +# key 418 "KEY_ZOOM_IN" +key 418 ZOOM_IN +# key 419 "KEY_ZOOM_OUT" +key 419 ZOOM_OUT +key 528 FOCUS + +key 429 CONTACTS + +# key 448 "KEY_DEL_EOL" +# key 449 "KEY_DEL_EOS" +# key 450 "KEY_INS_LINE" +# key 451 "KEY_DEL_LINE" + + +key 464 FUNCTION +key 465 ESCAPE FUNCTION +key 466 F1 FUNCTION +key 467 F2 FUNCTION +key 468 F3 FUNCTION +key 469 F4 FUNCTION +key 470 F5 FUNCTION +key 471 F6 FUNCTION +key 472 F7 FUNCTION +key 473 F8 FUNCTION +key 474 F9 FUNCTION +key 475 F10 FUNCTION +key 476 F11 FUNCTION +key 477 F12 FUNCTION +key 478 1 FUNCTION +key 479 2 FUNCTION +key 480 D FUNCTION +key 481 E FUNCTION +key 482 F FUNCTION +key 483 S FUNCTION +key 484 B FUNCTION + + +# key 497 KEY_BRL_DOT1 +# key 498 KEY_BRL_DOT2 +# key 499 KEY_BRL_DOT3 +# key 500 KEY_BRL_DOT4 +# key 501 KEY_BRL_DOT5 +# key 502 KEY_BRL_DOT6 +# key 503 KEY_BRL_DOT7 +# key 504 KEY_BRL_DOT8 + +key 522 STAR +key 523 POUND +key 580 APP_SWITCH +key 582 VOICE_ASSIST +# Linux KEY_ASSISTANT +key 583 ASSIST +key 585 EMOJI_PICKER +key 586 DICTATE +key 591 DO_NOT_DISTURB +key 656 MACRO_1 +key 657 MACRO_2 +key 658 MACRO_3 +key 659 MACRO_4 + +# CEC +key 352 ENTER +key 618 HOME +key 141 POWER +key 174 BACK + +# Keys defined by HID usages +key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING +key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING +key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING +key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING +key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING +key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING +key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING +key usage 0x0c00D8 DICTATE FALLBACK_USAGE_MAPPING +key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING +key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING +key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING +key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING +key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c0201 NEW FALLBACK_USAGE_MAPPING +key usage 0x0c0203 CLOSE FALLBACK_USAGE_MAPPING +key usage 0x0c0208 PRINT FALLBACK_USAGE_MAPPING +key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING +key usage 0x0c0232 FULLSCREEN FALLBACK_USAGE_MAPPING +key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING +key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING +key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING +key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING + +# Joystick and game controller axes. +# Axes that are not mapped will be assigned generic axis numbers by the input subsystem. +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x03 RX +axis 0x04 RY +axis 0x05 RZ +axis 0x06 THROTTLE +axis 0x07 RUDDER +axis 0x08 WHEEL +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# LEDs +led 0x00 NUM_LOCK +led 0x01 CAPS_LOCK +led 0x02 SCROLL_LOCK +led 0x03 COMPOSE +led 0x04 KANA +led 0x05 SLEEP +led 0x06 SUSPEND +led 0x07 MUTE +led 0x08 MISC +led 0x09 MAIL +led 0x0a CHARGING + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/light/Android.bp b/light/Android.bp new file mode 100644 index 0000000..79535e8 --- /dev/null +++ b/light/Android.bp @@ -0,0 +1,50 @@ +// Copyright (C) 2020 The Android Open Source Project +// Copyright (C) 2023 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "android.hardware.light-service.opi", + relative_install_path: "hw", + vendor: true, + srcs: [ + "Lights.cpp", + "main.cpp", + ], + shared_libs: [ + "android.hardware.light-V2-ndk", + "libbase", + "libbinder_ndk", + ], + installable: false, +} + +prebuilt_etc { + name: "android.hardware.light-service.opi.rc", + src: "android.hardware.light-service.opi.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.light-service.opi.xml", + src: "android.hardware.light-service.opi.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.light.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["android.hardware.light-service.opi"], + prebuilts: [ + "android.hardware.light-service.opi.rc", + "android.hardware.light-service.opi.xml", + ], +} diff --git a/light/Lights.cpp b/light/Lights.cpp new file mode 100644 index 0000000..f4e3d65 --- /dev/null +++ b/light/Lights.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2023 KonstaKANG + * + * 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 "Lights.h" + +#include + +using ::android::base::ReadFileToString; +using ::android::base::WriteStringToFile; + +namespace aidl::android::hardware::light { + +static const uint32_t defaultMaxBrightness = 255; + +static const std::string backlightBrightnessPath = "/sys/class/backlight/11-0045/brightness"; +static const std::string backlightMaxBrightnessPath = "/sys/class/backlight/11-0045/max_brightness"; + +static const std::vector availableLights = { + {.id = (int)LightType::BACKLIGHT, .type = LightType::BACKLIGHT, .ordinal = 0} +}; + +Lights::Lights() { + maxBrightness = defaultMaxBrightness; + + if (!access(backlightMaxBrightnessPath.c_str(), R_OK)) { + std::string maxBrightnessValue; + if (ReadFileToString(backlightMaxBrightnessPath, &maxBrightnessValue)) { + maxBrightness = std::stoi(maxBrightnessValue); + } + } +} + +ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) { + HwLight const& light = availableLights[id]; + std::string const brightness = std::to_string(rgbToScaledBrightness(state, maxBrightness)); + + switch (light.type) { + case LightType::BACKLIGHT: + if (!access(backlightBrightnessPath.c_str(), W_OK)) { + WriteStringToFile(brightness, backlightBrightnessPath); + } + break; + default: + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Lights::getLights(std::vector* lights) { + for (auto i = availableLights.begin(); i != availableLights.end(); i++) { + lights->push_back(*i); + } + + return ndk::ScopedAStatus::ok(); +} + +uint32_t Lights::rgbToScaledBrightness(const HwLightState& state, uint32_t maxBrightness) { + uint32_t color = state.color & 0x00ffffff; + uint32_t brightness = ((77 * ((color >> 16) & 0xff)) + (150 * ((color >> 8) & 0xff)) + + (29 * (color & 0xff))) >> 8; + return brightness * maxBrightness / 0xff; +} + +} // aidl::android::hardware::light diff --git a/light/Lights.h b/light/Lights.h new file mode 100644 index 0000000..dc44ac6 --- /dev/null +++ b/light/Lights.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2023 KonstaKANG + * + * 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 + +namespace aidl::android::hardware::light { + +class Lights : public BnLights { +public: + Lights(); + + ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override; + ndk::ScopedAStatus getLights(std::vector* types) override; + +private: + uint32_t maxBrightness; + uint32_t rgbToScaledBrightness(const HwLightState& state, uint32_t maxBrightness); +}; + +} // aidl::android::hardware::light diff --git a/light/android.hardware.light-service.opi.rc b/light/android.hardware.light-service.opi.rc new file mode 100644 index 0000000..22a5d77 --- /dev/null +++ b/light/android.hardware.light-service.opi.rc @@ -0,0 +1,11 @@ +on early-boot + chown system system /sys/class/backlight/11-0045/brightness + chown system system /sys/class/backlight/11-0045/max_brightness + chmod 660 /sys/class/backlight/11-0045/brightness + chmod 440 /sys/class/backlight/11-0045/max_brightness + +service vendor.light-opi /apex/com.android.hardware.light.opi5/bin/hw/android.hardware.light-service.opi + class hal + user system + group system + shutdown critical diff --git a/light/android.hardware.light-service.opi.xml b/light/android.hardware.light-service.opi.xml new file mode 100644 index 0000000..abdd74e --- /dev/null +++ b/light/android.hardware.light-service.opi.xml @@ -0,0 +1,7 @@ + + + android.hardware.light + 2 + ILights/default + + diff --git a/light/apex_file_contexts b/light/apex_file_contexts new file mode 100644 index 0000000..857bb7d --- /dev/null +++ b/light/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.light-service\.opi u:object_r:hal_light_default_exec:s0 diff --git a/light/apex_manifest.json b/light/apex_manifest.json new file mode 100644 index 0000000..1cc6879 --- /dev/null +++ b/light/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.light.opi5", + "version": 1 +} diff --git a/light/main.cpp b/light/main.cpp new file mode 100644 index 0000000..d4ed632 --- /dev/null +++ b/light/main.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG + * + * 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 "Lights.h" + +#include +#include +#include + +using ::aidl::android::hardware::light::Lights; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr lights = ndk::SharedRefBase::make(); + + const std::string instance = std::string() + Lights::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/lineage_orangepi5ultra.mk b/lineage_orangepi5ultra.mk new file mode 100644 index 0000000..e6ab18a --- /dev/null +++ b/lineage_orangepi5ultra.mk @@ -0,0 +1,47 @@ +# +# Copyright (C) 2021-2023 KonstaKANG +# Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +# Copyright (C) 2026 Brendan Szymanski +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Inherit device configuration +$(call inherit-product, device/rockchip/orangepi5ultra/device.mk) + +PRODUCT_AAPT_PREF_CONFIG := tvdpi +PRODUCT_CHARACTERISTICS := tv + +# LineageOS ATV base +$(call inherit-product, device/lineage/atv/lineage_atv.mk) +$(call inherit-product, vendor/lineage/build/target/product/lineage_generic_tv_target.mk) + +# Android TV packages +PRODUCT_PACKAGES += \ + DocumentsUI \ + LeanbackIME \ + TvProvision \ + TvSettingsTwoPanel \ + Catapult + +# Bluetooth TV class +PRODUCT_VENDOR_PROPERTIES += \ + bluetooth.device.class_of_device=34,4,36 + +# Boot animation +PRODUCT_COPY_FILES += \ + device/google/atv/products/bootanimations/bootanimation.zip:$(TARGET_COPY_OUT_SYSTEM)/media/bootanimation.zip + +# Overlays +PRODUCT_PACKAGES += \ + AndroidTvOpiOverlay \ + BluetoothOpiOverlay \ + SettingsProviderTvOpiOverlay \ + WifiOpiOverlay + +# Device identifier. This must come after all inclusions. +PRODUCT_DEVICE := orangepi5ultra +PRODUCT_NAME := lineage_orangepi5ultra +PRODUCT_BRAND := Orange +PRODUCT_MODEL := Pi 5 Ultra +PRODUCT_MANUFACTURER := Orange diff --git a/manifest.xml b/manifest.xml new file mode 100644 index 0000000..fcf2f80 --- /dev/null +++ b/manifest.xml @@ -0,0 +1,37 @@ + + + android.hardware.audio + hwbinder + 7.1 + + IDevicesFactory + default + + + + android.hardware.audio.effect + hwbinder + 7.0 + + IEffectsFactory + default + + + + android.hardware.camera.provider + 1 + + ICameraProvider + external/0 + + + + android.hardware.camera.provider + hwbinder + 2.5 + + ICameraProvider + legacy/0 + + + diff --git a/media/media_codecs.xml b/media/media_codecs.xml new file mode 100644 index 0000000..9165b42 --- /dev/null +++ b/media/media_codecs.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/media/media_codecs_ffmpeg_c2.xml b/media/media_codecs_ffmpeg_c2.xml new file mode 100644 index 0000000..34c9f5d --- /dev/null +++ b/media/media_codecs_ffmpeg_c2.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/media/media_codecs_v4l2_c2_video.xml b/media/media_codecs_v4l2_c2_video.xml new file mode 100644 index 0000000..f29fe82 --- /dev/null +++ b/media/media_codecs_v4l2_c2_video.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mkbootimg.mk b/mkbootimg.mk new file mode 100644 index 0000000..d0b01b2 --- /dev/null +++ b/mkbootimg.mk @@ -0,0 +1,41 @@ +# +# Copyright (C) 2021-2022 KonstaKANG +# Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +# +# SPDX-License-Identifier: Apache-2.0 +# + +DEVICE_PATH := device/rockchip/orangepi5ultra +KERNEL_PATH := device/rockchip/orangepi5ultra-kernel + +OPI_BOOT_OUT := $(PRODUCT_OUT)/opiboot +$(OPI_BOOT_OUT): $(INSTALLED_RAMDISK_TARGET) + mkdir -p $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/Image $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/rk3588s-orangepi-5-pro-1.dtb $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/rk3588-orangepi-5-max.dtb $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/rk3588-orangepi-5-plus.dtb $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/rk3588-orangepi-5-ultra.dtb $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/rk3588s-orangepi-5.dtb $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/rk3588s-orangepi-5b.dtb $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/android-sdcard.dtbo $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/boot.scr $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/uRamdisk $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/uRecovery $(OPI_BOOT_OUT) + cp $(PRODUCT_OUT)/ramdisk.img $(OPI_BOOT_OUT) + cp $(KERNEL_PATH)/config.txt $(OPI_BOOT_OUT) + + +$(INSTALLED_BOOTIMAGE_TARGET): $(OPI_BOOT_OUT) + $(call pretty,"Target boot image: $@") + + # Determine size in bytes, add 10 MiB padding, convert to 512-byte blocks + BOOT_SIZE_BYTES=`du -s -k $(OPI_BOOT_OUT) | awk '{ print $$1 * 1024 }'`; \ + PADDED_BYTES=`expr $$BOOT_SIZE_BYTES + 10485760`; \ + BLOCKS=`expr $$PADDED_BYTES / 512`; \ + echo "Creating boot image with $$BLOCKS blocks..."; \ + dd if=/dev/zero of=$@ bs=512 count=$$BLOCKS; \ + mkfs.fat -F 32 -n "boot" $@; \ + mcopy -s -i $@ $(OPI_BOOT_OUT)/* :: + + diff --git a/mkimg.sh b/mkimg.sh new file mode 100755 index 0000000..4170f59 --- /dev/null +++ b/mkimg.sh @@ -0,0 +1,98 @@ + +# +# Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +# +# SPDX-License-Identifier: Apache-2.0 +# +#!/bin/bash + +exit_with_error() { + echo $@ + exit 1 +} + +# Check required env vars +if [ -z ${TARGET_PRODUCT} ]; then + exit_with_error "TARGET_PRODUCT environment variable is not set. Run lunch first." +fi + +if [ -z ${ANDROID_PRODUCT_OUT} ]; then + exit_with_error "ANDROID_PRODUCT_OUT environment variable is not set. Run lunch first." +fi + +# Check required images +for PARTITION in "boot" "system" "vendor"; do + if [ ! -f ${ANDROID_PRODUCT_OUT}/${PARTITION}.img ]; then + exit_with_error "Partition image not found: ${PARTITION}.img. Run 'make ${PARTITION}image' first." + fi +done + +UBOOT_BIN=device/rockchip/orangepi5ultra-kernel/u-boot-rockchip.bin + +if [ ! -f ${UBOOT_BIN} ]; then + exit_with_error "Missing u-boot-rockchip.bin! Make sure it's built and placed at ${UBOOT_BIN}" +fi + +VERSION=OrangePi_5Pro_aosp +DATE=$(date +%Y%m%d) +TARGET=$(echo ${TARGET_PRODUCT} | sed 's/^aosp_//') +IMGNAME=${VERSION}-${DATE}-${TARGET}.img +IMGSIZE=19456MiB + +if [ -f ${ANDROID_PRODUCT_OUT}/${IMGNAME} ]; then + exit_with_error "${ANDROID_PRODUCT_OUT}/${IMGNAME} already exists!" +fi + +echo "Creating image file ${ANDROID_PRODUCT_OUT}/${IMGNAME}..." +sudo fallocate -l ${IMGSIZE} ${ANDROID_PRODUCT_OUT}/${IMGNAME} +sync + +echo "Writing U-Boot to sector 64..." +sudo dd if=${UBOOT_BIN} of=${ANDROID_PRODUCT_OUT}/${IMGNAME} seek=64 bs=512 conv=notrunc +sync + +echo "Partitioning image using sfdisk..." +PART_TABLE=$(cat <&2 + cleanup || true + exit 1 +} + +cleanup() { + + if [ -n "${LOOPDEV:-}" ]; then + if sudo kpartx -l "${IMAGE_PATH}" >/dev/null 2>&1; then + sudo kpartx -d "${IMAGE_PATH}" || true + fi + fi +} + +trap cleanup EXIT + + +: "${TARGET_PRODUCT:?TARGET_PRODUCT environment variable is not set. Run lunch first.}" +: "${ANDROID_PRODUCT_OUT:?ANDROID_PRODUCT_OUT environment variable is not set. Run lunch first.}" + + +for PART in boot system vendor; do + if [ ! -f "${ANDROID_PRODUCT_OUT}/${PART}.img" ]; then + exit_with_error "Missing partition image: ${ANDROID_PRODUCT_OUT}/${PART}.img — run 'make ${PART}image' first." + fi +done + +UBOOT_BIN=device/rockchip/orangepi5ultra-kernel/u-boot-rockchip.bin +if [ ! -f "${UBOOT_BIN}" ]; then + exit_with_error "Missing U-Boot: ${UBOOT_BIN}" +fi + +VERSION=OrangePi_5Ultra_lineage_atv +DATE=$(date +%Y%m%d) +TARGET=$(echo "${TARGET_PRODUCT}" | sed 's/^aosp_//') +IMGNAME=${VERSION}-${DATE}-${TARGET}_gpt.img +IMGSIZE=19456MiB +IMAGE_PATH="${ANDROID_PRODUCT_OUT}/${IMGNAME}" + +if [ -f "${IMAGE_PATH}" ]; then + exit_with_error "${IMAGE_PATH} already exists!" +fi + +echo "Creating image file ${IMAGE_PATH} (${IMGSIZE})..." +sudo fallocate -l "${IMGSIZE}" "${IMAGE_PATH}" +sync + +echo "Writing U-Boot to sector 64..." +# write u-boot binary to offset 64*512 = sector 64 +sudo dd if="${UBOOT_BIN}" of="${IMAGE_PATH}" seek=64 bs=512 conv=notrunc status=progress +sync + +echo "Partitioning image using sfdisk (GPT) with explicit partition NAMES..." + + +PART_TABLE=$(cat </dev/null +E2_RC=$? +if [ "${E2_RC}" -ne 0 ]; then + echo "Warning: /dev/mapper/${LOOPDEV}p2 does not appear to be ext4 or e2label failed. Continuing." +fi +sudo e2label "/dev/mapper/${LOOPDEV}p3" vendor 2>/dev/null || echo "Warning: e2label on vendor failed (maybe not ext4)." + +set -e + +# Create/format metadata and userdata partitions and set filesystem labels +sudo mkfs.ext4 -F -L metadata "/dev/mapper/${LOOPDEV}p4" +sudo mkfs.ext4 -F -L userdata "/dev/mapper/${LOOPDEV}p5" +sync + +# Final sanity: list by-name symlinks +echo "Partition mapping summary (kpartx):" +sudo ls -l /dev/mapper/"${LOOPDEV}"* || true + +# Unmap now that we have set labels +sudo kpartx -d "${IMAGE_PATH}" || true +# fix ownership +sudo chown "${USER}:${USER}" "${IMAGE_PATH}" + +echo "✅ Created ${IMAGE_PATH} with GPT partition names and filesystem labels." +echo "You should now be able to write this image to your SD/NVMe and boot RK3588. " + + +sudo sgdisk -p "${IMAGE_PATH}" +exit 0 diff --git a/overlay/AndroidTvOpiOverlay/Android.bp b/overlay/AndroidTvOpiOverlay/Android.bp new file mode 100644 index 0000000..3f02f9c --- /dev/null +++ b/overlay/AndroidTvOpiOverlay/Android.bp @@ -0,0 +1,11 @@ +// Copyright (C) 2021-2022 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +runtime_resource_overlay { + name: "AndroidTvOpiOverlay", + resource_dirs: ["res"], + sdk_version: "current", + vendor: true, +} diff --git a/overlay/AndroidTvOpiOverlay/AndroidManifest.xml b/overlay/AndroidTvOpiOverlay/AndroidManifest.xml new file mode 100644 index 0000000..9e96248 --- /dev/null +++ b/overlay/AndroidTvOpiOverlay/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/overlay/AndroidTvOpiOverlay/res/values/config.xml b/overlay/AndroidTvOpiOverlay/res/values/config.xml new file mode 100644 index 0000000..1f68152 --- /dev/null +++ b/overlay/AndroidTvOpiOverlay/res/values/config.xml @@ -0,0 +1,51 @@ + + + + + + 128 + + + 20 + + + 20 + + + true + + + 1 + + + false + true + + diff --git a/overlay/BluetoothOpiOverlay/Android.bp b/overlay/BluetoothOpiOverlay/Android.bp new file mode 100644 index 0000000..b04350f --- /dev/null +++ b/overlay/BluetoothOpiOverlay/Android.bp @@ -0,0 +1,11 @@ +// Copyright (C) 2021-2022 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +runtime_resource_overlay { + name: "BluetoothOpiOverlay", + resource_dirs: ["res"], + sdk_version: "current", + vendor: true, +} diff --git a/overlay/BluetoothOpiOverlay/AndroidManifest.xml b/overlay/BluetoothOpiOverlay/AndroidManifest.xml new file mode 100644 index 0000000..bc5fdd3 --- /dev/null +++ b/overlay/BluetoothOpiOverlay/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/overlay/BluetoothOpiOverlay/res/values/config.xml b/overlay/BluetoothOpiOverlay/res/values/config.xml new file mode 100644 index 0000000..498890a --- /dev/null +++ b/overlay/BluetoothOpiOverlay/res/values/config.xml @@ -0,0 +1,24 @@ + + + + + + + true + + diff --git a/overlay/SettingsProviderTvOpiOverlay/Android.bp b/overlay/SettingsProviderTvOpiOverlay/Android.bp new file mode 100644 index 0000000..a0f603c --- /dev/null +++ b/overlay/SettingsProviderTvOpiOverlay/Android.bp @@ -0,0 +1,11 @@ +// Copyright (C) 2021-2022 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +runtime_resource_overlay { + name: "SettingsProviderTvOpiOverlay", + resource_dirs: ["res"], + sdk_version: "current", + vendor: true, +} diff --git a/overlay/SettingsProviderTvOpiOverlay/AndroidManifest.xml b/overlay/SettingsProviderTvOpiOverlay/AndroidManifest.xml new file mode 100644 index 0000000..6960baa --- /dev/null +++ b/overlay/SettingsProviderTvOpiOverlay/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/overlay/SettingsProviderTvOpiOverlay/res/values/defaults.xml b/overlay/SettingsProviderTvOpiOverlay/res/values/defaults.xml new file mode 100644 index 0000000..befb71a --- /dev/null +++ b/overlay/SettingsProviderTvOpiOverlay/res/values/defaults.xml @@ -0,0 +1,35 @@ + + + + + + + Orange Pi 5 Pro + + + 128 + + + 0 + + + true + true + + diff --git a/overlay/WifiOpiOverlay/Android.bp b/overlay/WifiOpiOverlay/Android.bp new file mode 100644 index 0000000..d2c1299 --- /dev/null +++ b/overlay/WifiOpiOverlay/Android.bp @@ -0,0 +1,11 @@ +// Copyright (C) 2021-2022 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +runtime_resource_overlay { + name: "WifiOpiOverlay", + resource_dirs: ["res"], + sdk_version: "current", + vendor: true, +} diff --git a/overlay/WifiOpiOverlay/AndroidManifest.xml b/overlay/WifiOpiOverlay/AndroidManifest.xml new file mode 100644 index 0000000..736acb4 --- /dev/null +++ b/overlay/WifiOpiOverlay/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/overlay/WifiOpiOverlay/res/values/config.xml b/overlay/WifiOpiOverlay/res/values/config.xml new file mode 100644 index 0000000..3a0d5f3 --- /dev/null +++ b/overlay/WifiOpiOverlay/res/values/config.xml @@ -0,0 +1,46 @@ + + + + + + + true + + + true + + + true + + + Orange Pi 5 Pro + + + + 0 + + diff --git a/product_manifest.xml b/product_manifest.xml new file mode 100644 index 0000000..c1edbba --- /dev/null +++ b/product_manifest.xml @@ -0,0 +1 @@ + diff --git a/ramdisk/fstab.opi5ultra b/ramdisk/fstab.opi5ultra new file mode 100644 index 0000000..87c0a6f --- /dev/null +++ b/ramdisk/fstab.opi5ultra @@ -0,0 +1,10 @@ +# Android fstab file. +# The filesystem that contains the filesystem checker binary (typically /system) cannot +# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK + +# +/dev/block/by-name/system /system ext4 ro,barrier=1,nodelalloc wait,first_stage_mount +/dev/block/by-name/vendor /vendor ext4 ro,barrier=1,nodelalloc wait,check,first_stage_mount +/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid wait,check,formattable,first_stage_mount +/dev/block/by-name/userdata /data ext4 nodev,noatime,nosuid,errors=panic wait,check,formattable,quota +/devices/platform/*.usb/usb* auto auto defaults voldmanaged=usb:auto diff --git a/ramdisk/init.opi5ultra.rc b/ramdisk/init.opi5ultra.rc new file mode 100644 index 0000000..c725355 --- /dev/null +++ b/ramdisk/init.opi5ultra.rc @@ -0,0 +1,25 @@ +import /vendor/etc/init/hw/init.opi5ultra.usb.rc + +on init + symlink /sdcard /storage/sdcard0 + + +on fs + mount_all /vendor/etc/fstab.opi5ultra --early + +on late-fs + mount_all /vendor/etc/fstab.opi5ultra --late + +on boot + chmod 0666 /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu3/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu4/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu5/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu6/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpu7/cpufreq/scaling_governor + chmod 0666 /sys/devices/system/cpu/cpufreq/boost + + + diff --git a/ramdisk/init.opi5ultra.usb.rc b/ramdisk/init.opi5ultra.usb.rc new file mode 100644 index 0000000..e406934 --- /dev/null +++ b/ramdisk/init.opi5ultra.usb.rc @@ -0,0 +1,89 @@ +on boot + mount configfs none /config + mkdir /config/usb_gadget/g1 0770 + mkdir /config/usb_gadget/g1/strings/0x409 0770 + write /config/usb_gadget/g1/bcdDevice 0x0440 + write /config/usb_gadget/g1/bcdUSB 0x0200 + write /config/usb_gadget/g1/idVendor 0x18d1 + write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno} + write /config/usb_gadget/g1/strings/0x409/manufacturer "Orange pi" + write /config/usb_gadget/g1/strings/0x409/product "5 Pro" + mkdir /config/usb_gadget/g1/functions/ffs.adb + mkdir /config/usb_gadget/g1/functions/ffs.mtp + mkdir /config/usb_gadget/g1/functions/ffs.ptp + mkdir /config/usb_gadget/g1/functions/accessory.gs2 + mkdir /config/usb_gadget/g1/functions/audio_source.gs3 + mkdir /config/usb_gadget/g1/functions/rndis.gs4 + mkdir /config/usb_gadget/g1/functions/midi.gs5 + mkdir /config/usb_gadget/g1/configs/b.1 0770 + mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 + write /config/usb_gadget/g1/configs/b.1/MaxPower 500 + write /config/usb_gadget/g1/os_desc/b_vendor_code 0x1 + write /config/usb_gadget/g1/os_desc/qw_sign "MSFT100" + mkdir /dev/usb-ffs 0775 shell shell + mkdir /dev/usb-ffs/adb 0770 shell shell + mkdir /dev/usb-ffs/mtp 0770 mtp mtp + mkdir /dev/usb-ffs/ptp 0770 mtp mtp + setprop sys.usb.mtp.device_type 3 + setprop sys.usb.mtp.batchcancel true + setprop vendor.usb.rndis.config rndis.gs4 + symlink /config/usb_gadget/g1/configs/b.1 /config/usb_gadget/g1/os_desc/b.1 + + chown system system /config/usb_gadget/ + chown system system /config/usb_gadget/g1 + chown system system /config/usb_gadget/g1/UDC + chown system system /config/usb_gadget/g1/bDeviceClass + chown system system /config/usb_gadget/g1/bDeviceProtocol + chown system system /config/usb_gadget/g1/bDeviceSubClass + chown system system /config/usb_gadget/g1/bMaxPacketSize0 + chown system system /config/usb_gadget/g1/bcdDevice + chown system system /config/usb_gadget/g1/bcdUSB + chown system system /config/usb_gadget/g1/configs + chown system system /config/usb_gadget/g1/configs/b.1 + chown system system /config/usb_gadget/g1/configs/b.1/MaxPower + chown system system /config/usb_gadget/g1/configs/b.1/bmAttributes + chown system system /config/usb_gadget/g1/configs/b.1/strings + chown system system /config/usb_gadget/g1/functions + chown system system /config/usb_gadget/g1/functions/accessory.gs2 + chown system system /config/usb_gadget/g1/functions/audio_source.gs3 + chown system system /config/usb_gadget/g1/functions/ffs.adb + chown system system /config/usb_gadget/g1/functions/ffs.mtp + chown system system /config/usb_gadget/g1/functions/ffs.ptp + chown system system /config/usb_gadget/g1/functions/midi.gs5 + chown system system /config/usb_gadget/g1/functions/midi.gs5/buflen + chown system system /config/usb_gadget/g1/functions/midi.gs5/id + chown system system /config/usb_gadget/g1/functions/midi.gs5/in_ports + chown system system /config/usb_gadget/g1/functions/midi.gs5/index + chown system system /config/usb_gadget/g1/functions/midi.gs5/out_ports + chown system system /config/usb_gadget/g1/functions/midi.gs5/qlen + chown system system /config/usb_gadget/g1/functions/rndis.gs4 + chown system system /config/usb_gadget/g1/functions/rndis.gs4/class + chown system system /config/usb_gadget/g1/functions/rndis.gs4/dev_addr + chown system system /config/usb_gadget/g1/functions/rndis.gs4/host_addr + chown system system /config/usb_gadget/g1/functions/rndis.gs4/ifname + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc/interface.rndis + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc/interface.rndis/compatible_id + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc/interface.rndis/sub_compatible_id + chown system system /config/usb_gadget/g1/functions/rndis.gs4/protocol + chown system system /config/usb_gadget/g1/functions/rndis.gs4/qmult + chown system system /config/usb_gadget/g1/functions/rndis.gs4/subclass + chown system system /config/usb_gadget/g1/idProduct + chown system system /config/usb_gadget/g1/idVendor + chown system system /config/usb_gadget/g1/max_speed + chown system system /config/usb_gadget/g1/os_desc + chown system system /config/usb_gadget/g1/os_desc/b.1 + chown system system /config/usb_gadget/g1/os_desc/b_vendor_code + chown system system /config/usb_gadget/g1/os_desc/qw_sign + chown system system /config/usb_gadget/g1/os_desc/use + chown system system /config/usb_gadget/g1/strings + chown system system /config/usb_gadget/g1/strings/0x409 + chown system system /config/usb_gadget/g1/strings/0x409/manufacturer + chown system system /config/usb_gadget/g1/strings/0x409/product + chown system system /config/usb_gadget/g1/strings/0x409/serialnumber + +on property:sys.usb.controller=* + mount functionfs adb /dev/usb-ffs/adb rmode=0770,fmode=0660,uid=2000,gid=2000,no_disconnect=1 + mount functionfs mtp /dev/usb-ffs/mtp rmode=0770,fmode=0660,uid=1024,gid=1024,no_disconnect=1 + mount functionfs ptp /dev/usb-ffs/ptp rmode=0770,fmode=0660,uid=1024,gid=1024,no_disconnect=1 + setprop sys.usb.configfs 2 diff --git a/ramdisk/ueventd.opi5ultra.rc b/ramdisk/ueventd.opi5ultra.rc new file mode 100644 index 0000000..6345e5b --- /dev/null +++ b/ramdisk/ueventd.opi5ultra.rc @@ -0,0 +1,18 @@ +# Bluetooth +/dev/rfkill 0660 bluetooth bluetooth + +# Camera +/dev/media* 0660 system camera +/dev/v4l-subdev* 0660 system camera +/dev/video* 0660 system camera + +# DMA +/dev/dma_heap/linux,cma 0666 system graphics +/dev/dma_heap/system 0666 system graphics + +# FFmpeg +/dev/media* 0660 media media +/dev/video* 0660 media media + +# USB +/sys/class/udc/1000480000.usb current_speed 0664 system system diff --git a/seccomp_policy/android.hardware.media.c2-default-seccomp_policy b/seccomp_policy/android.hardware.media.c2-default-seccomp_policy new file mode 100644 index 0000000..5d0284f --- /dev/null +++ b/seccomp_policy/android.hardware.media.c2-default-seccomp_policy @@ -0,0 +1,81 @@ +# Copyright (C) 2021 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. + +futex: 1 +# ioctl calls are filtered via the selinux policy. +ioctl: 1 +sched_yield: 1 +close: 1 +dup: 1 +ppoll: 1 +mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +getuid: 1 +getrlimit: 1 +fstat: 1 +newfstatat: 1 +fstatfs: 1 +memfd_create: 1 +ftruncate: 1 + +# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail +# parser support for '<' is in this needs to be modified to also prevent +# |old_address| and |new_address| from touching the exception vector page, which +# on ARM is statically loaded at 0xffff 0000. See +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html +# for more details. +mremap: arg3 == 3 || arg3 == MREMAP_MAYMOVE +munmap: 1 +prctl: 1 +writev: 1 +sigaltstack: 1 +clone: 1 +exit: 1 +lseek: 1 +rt_sigprocmask: 1 +openat: 1 +write: 1 +nanosleep: 1 +setpriority: 1 +set_tid_address: 1 +getdents64: 1 +readlinkat: 1 +read: 1 +pread64: 1 +gettimeofday: 1 +faccessat: 1 +exit_group: 1 +restart_syscall: 1 +rt_sigreturn: 1 +getrandom: 1 +madvise: 1 + +# crash dump policy additions +clock_gettime: 1 +getpid: 1 +gettid: 1 +pipe2: 1 +recvmsg: 1 +process_vm_readv: 1 +tgkill: 1 +rt_sigaction: 1 +rt_tgsigqueueinfo: 1 +#mprotect: arg2 in 0x1|0x2 +munmap: 1 +#mmap: arg2 in 0x1|0x2 +geteuid: 1 +getgid: 1 +getegid: 1 +getgroups: 1 + diff --git a/seccomp_policy/android.hardware.media.c2-extended-seccomp_policy b/seccomp_policy/android.hardware.media.c2-extended-seccomp_policy new file mode 100644 index 0000000..68819e4 --- /dev/null +++ b/seccomp_policy/android.hardware.media.c2-extended-seccomp_policy @@ -0,0 +1,22 @@ +# device specific syscalls +_llseek: 1 +epoll_create1: 1 +epoll_ctl: 1 +epoll_pwait: 1 +eventfd2: 1 +fstat64: 1 +fstatat64: 1 +fstatfs64: 1 +getcwd: 1 +getdents64: 1 +getegid32: 1 +geteuid32: 1 +getgid32: 1 +getuid32: 1 +mmap2: 1 +open: 1 +pselect6: 1 +sched_getaffinity: 1 +statfs64: 1 +sysinfo: 1 +ugetrlimit: 1 \ No newline at end of file diff --git a/seccomp_policy/android.hardware.media.c2-ffmpeg.policy b/seccomp_policy/android.hardware.media.c2-ffmpeg.policy new file mode 100644 index 0000000..0ee4e78 --- /dev/null +++ b/seccomp_policy/android.hardware.media.c2-ffmpeg.policy @@ -0,0 +1,59 @@ +# device specific syscalls +clock_gettime: 1 +clone: 1 +close: 1 +dup: 1 +eventfd2: 1 +exit_group: 1 +exit: 1 +faccessat: 1 +fstat: 1 +fstatfs: 1 +ftruncate: 1 +futex: 1 +getdents64: 1 +getegid: 1 +geteuid: 1 +getgid: 1 +getgroups: 1 +getpid: 1 +getrandom: 1 +getrlimit: 1 +gettid: 1 +gettimeofday: 1 +getsockopt: 1 +getuid: 1 +ioctl: 1 +lseek: 1 +madvise: 1 +memfd_create: 1 +mmap: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +mprotect: arg2 in ~PROT_EXEC || arg2 in ~PROT_WRITE +mremap: arg3 == 3 || arg3 == MREMAP_MAYMOVE +munmap: 1 +nanosleep: 1 +newfstatat: 1 +openat: 1 +pipe2: 1 +ppoll: 1 +prctl: 1 +pread64: 1 +process_vm_readv: 1 +read: 1 +readlinkat: 1 +recvmsg: 1 +restart_syscall: 1 +rt_sigaction: 1 +rt_sigprocmask: 1 +rt_sigreturn: 1 +rt_tgsigqueueinfo: 1 +sched_getaffinity: 1 +sched_yield: 1 +set_tid_address: 1 +sendmsg: 1 +setpriority: 1 +sigaltstack: 1 +sysinfo: 1 +tgkill: 1 +write: 1 +writev: 1 diff --git a/seccomp_policy/mediacodec.policy b/seccomp_policy/mediacodec.policy new file mode 100644 index 0000000..77bb74e --- /dev/null +++ b/seccomp_policy/mediacodec.policy @@ -0,0 +1,9 @@ +# device specific syscalls +_llseek: 1 +eventfd2: 1 +getcwd: 1 +pselect6: 1 +recvfrom: 1 +sched_getaffinity: 1 +sendto: 1 +sysinfo: 1 diff --git a/seccomp_policy/mediaswcodec.policy b/seccomp_policy/mediaswcodec.policy new file mode 100644 index 0000000..66da4b7 --- /dev/null +++ b/seccomp_policy/mediaswcodec.policy @@ -0,0 +1,3 @@ +# device specific syscalls +sched_getaffinity: 1 +sysinfo: 1 diff --git a/sepolicy/bootanim.te b/sepolicy/bootanim.te new file mode 100644 index 0000000..e8e7494 --- /dev/null +++ b/sepolicy/bootanim.te @@ -0,0 +1 @@ +gpu_access(bootanim) diff --git a/sepolicy/device.te b/sepolicy/device.te new file mode 100644 index 0000000..14cdde4 --- /dev/null +++ b/sepolicy/device.te @@ -0,0 +1 @@ +type cec_device, dev_type; diff --git a/sepolicy/file.te b/sepolicy/file.te new file mode 100644 index 0000000..c55a5e0 --- /dev/null +++ b/sepolicy/file.te @@ -0,0 +1,2 @@ +type mediadrm_vendor_data_file, file_type, data_file_type; +type sysfs_hdmi, fs_type, sysfs_type; diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts new file mode 100644 index 0000000..cb5d0be --- /dev/null +++ b/sepolicy/file_contexts @@ -0,0 +1,46 @@ +# CEC +/dev/cec0 u:object_r:cec_device:s0 + + +# DRM +/data/vendor/mediadrm(/.*)? u:object_r:mediadrm_vendor_data_file:s0 + + + +# Graphics +#/dev/dri(/.*)? u:object_r:gpu_device:s0 +#/vendor/bin/hw/android\.hardware\.graphics\.allocator-service\.minigbm_gbm_mesa u:object_r:hal_graphics_allocator_default_exec:s0 +#/vendor/bin/hw/android\.hardware\.graphics\.allocator-service\.minigbm u:object_r:hal_graphics_allocator_default_exec:s0 +#/vendor/lib64/hw/mapper\.minigbm_gbm_mesa\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/hw/mapper\.minigbm\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/hw/vulkan\.panfrost\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/dri_gbm\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libdrm\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libgallium_dri\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libgbm_mesa\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libgbm\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libgbm_mesa_wrapper\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libminigbm_gralloc_gbm_mesa\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libminigbm_gralloc\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libminigbm_gralloc4_utils_gbm_mesa\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libminigbm_gralloc4_utils\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/libui\.so u:object_r:same_process_hal_file:s0 +#/vendor/lib64/gralloc\.minigbm\.so u:object_r:same_process_hal_file:s0 + +/dev/dri(/.*)? u:object_r:gpu_device:s0 +/vendor/lib64/android\.hardware\.graphics\.allocator@[2-4]\.0\.so u:object_r:same_process_hal_file:s0 +/vendor/lib64/hw/vulkan\.panfrost\.so u:object_r:same_process_hal_file:s0 +/vendor/lib64/libdrm\.so u:object_r:same_process_hal_file:s0 +/vendor/lib64/libgallium_dri\.so u:object_r:same_process_hal_file:s0 +/vendor/lib64/libui\.so u:object_r:same_process_hal_file:s0 + + +# Partitions (gpt) +/dev/block/by-name/system u:object_r:system_block_device:s0 +/dev/block/by-name/vendor u:object_r:system_block_device:s0 +/dev/block/by-name/metadata u:object_r:metadata_block_device:s0 +/dev/block/by-name/userdata u:object_r:userdata_block_device:s0 +/dev/block/by-name/boot u:object_r:boot_block_device:s0 + +#V4L2 +/vendor/bin/hw/android\.hardware\.media\.c2@1\.2-service-v4l2(.*)? u:object_r:mediacodec_exec:s0 \ No newline at end of file diff --git a/sepolicy/genfs_contexts b/sepolicy/genfs_contexts new file mode 100644 index 0000000..a1510cb --- /dev/null +++ b/sepolicy/genfs_contexts @@ -0,0 +1,18 @@ +# CEC +genfscon sysfs /devices/platform/display-subsystem/drm/card0/card0-HDMI-A-1/status u:object_r:sysfs_hdmi:s0 +genfscon sysfs /devices/platform/display-subsystem/drm/card1/card1-HDMI-A-1/status u:object_r:sysfs_hdmi:s0 + +# Graphics +genfscon sysfs /devices/platform/fde60000.gpu u:object_r:sysfs_gpu:s0 +genfscon sysfs /devices/platform/display-subsystem u:object_r:sysfs_gpu:s0 + +# Lights +genfscon sysfs /class/backlight/11-0045/brightness u:object_r:sysfs_leds:s0 +genfscon sysfs /class/backlight/11-0045/max_brightness u:object_r:sysfs_leds:s0 + +# # Serial number +# genfscon sysfs /firmware/devicetree/base/serial-number u:object_r:sysfs_dt_firmware_android:s0 + +# # Suspend +# genfscon sysfs /devices/platform/soc/soc:rpi_rtc/rtc/rtc0/alarmtimer.0.auto/wakeup/wakeup1 u:object_r:sysfs_wakeup:s0 +# genfscon sysfs /devices/platform/soc/soc:rpi_rtc/wakeup/wakeup0 u:object_r:sysfs_wakeup:s0 diff --git a/sepolicy/hal_audio_default.te b/sepolicy/hal_audio_default.te new file mode 100644 index 0000000..e9f5c72 --- /dev/null +++ b/sepolicy/hal_audio_default.te @@ -0,0 +1 @@ +get_prop(hal_audio_default, vendor_audio_config_prop) diff --git a/sepolicy/hal_camera.te b/sepolicy/hal_camera.te new file mode 100644 index 0000000..3a9e61f --- /dev/null +++ b/sepolicy/hal_camera.te @@ -0,0 +1,12 @@ +vndbinder_use(hal_camera_default); + +allow hal_camera_default hal_graphics_mapper_hwservice:hwservice_manager find; +hal_client_domain(hal_camera_default, hal_graphics_allocator); +hal_client_domain(hal_camera_default, hal_graphics_composer); + +allow cameraserver device:dir r_dir_perms; +allow cameraserver video_device:dir r_dir_perms; +allow cameraserver video_device:chr_file rw_file_perms; + +gpu_access(hal_camera_default) +gpu_access(cameraserver) diff --git a/sepolicy/hal_drm_clearkey.te b/sepolicy/hal_drm_clearkey.te new file mode 100644 index 0000000..0e0a5c2 --- /dev/null +++ b/sepolicy/hal_drm_clearkey.te @@ -0,0 +1,5 @@ +type hal_drm_clearkey, domain; +type hal_drm_clearkey_exec, vendor_file_type, exec_type, file_type; +init_daemon_domain(hal_drm_clearkey) + +hal_server_domain(hal_drm_clearkey, hal_drm) diff --git a/sepolicy/hal_drm_widevine.te b/sepolicy/hal_drm_widevine.te new file mode 100644 index 0000000..08b6582 --- /dev/null +++ b/sepolicy/hal_drm_widevine.te @@ -0,0 +1,8 @@ +type hal_drm_widevine, domain; +type hal_drm_widevine_exec, vendor_file_type, exec_type, file_type; +init_daemon_domain(hal_drm_widevine) + +hal_server_domain(hal_drm_widevine, hal_drm) + +allow hal_drm_widevine mediadrm_vendor_data_file:file create_file_perms; +allow hal_drm_widevine mediadrm_vendor_data_file:dir create_dir_perms; diff --git a/sepolicy/hal_graphics_allocator_default.te b/sepolicy/hal_graphics_allocator_default.te new file mode 100644 index 0000000..00f38cc --- /dev/null +++ b/sepolicy/hal_graphics_allocator_default.te @@ -0,0 +1 @@ +gpu_access(hal_graphics_allocator_default) diff --git a/sepolicy/hal_graphics_composer_default.te b/sepolicy/hal_graphics_composer_default.te new file mode 100644 index 0000000..9c0c169 --- /dev/null +++ b/sepolicy/hal_graphics_composer_default.te @@ -0,0 +1,5 @@ +vndbinder_use(hal_graphics_composer_default) +gpu_access(hal_graphics_composer_default) +get_prop(hal_graphics_composer_default, vendor_hwc_config_prop) + +allow hal_graphics_composer_default self:netlink_kobject_uevent_socket { bind create read }; diff --git a/sepolicy/hal_keymint_default.te b/sepolicy/hal_keymint_default.te new file mode 100644 index 0000000..8b5d0ca --- /dev/null +++ b/sepolicy/hal_keymint_default.te @@ -0,0 +1 @@ +get_prop(hal_keymint_default, serialno_prop) diff --git a/sepolicy/hal_tv_hdmi_cec_default.te b/sepolicy/hal_tv_hdmi_cec_default.te new file mode 100644 index 0000000..2ccb439 --- /dev/null +++ b/sepolicy/hal_tv_hdmi_cec_default.te @@ -0,0 +1,2 @@ +allow hal_tv_hdmi_cec_default cec_device:chr_file rw_file_perms; +get_prop(hal_tv_hdmi_cec_default, vendor_hdmi_config_prop) diff --git a/sepolicy/hal_tv_hdmi_connection_default.te b/sepolicy/hal_tv_hdmi_connection_default.te new file mode 100644 index 0000000..d016287 --- /dev/null +++ b/sepolicy/hal_tv_hdmi_connection_default.te @@ -0,0 +1 @@ +allow hal_tv_hdmi_connection_default sysfs_hdmi:file r_file_perms; diff --git a/sepolicy/init.te b/sepolicy/init.te new file mode 100644 index 0000000..1da3af0 --- /dev/null +++ b/sepolicy/init.te @@ -0,0 +1,2 @@ +allow init kernel:system module_request; +allow init tmpfs:lnk_file create; diff --git a/sepolicy/kernel.te b/sepolicy/kernel.te new file mode 100644 index 0000000..6d977fb --- /dev/null +++ b/sepolicy/kernel.te @@ -0,0 +1,4 @@ +allow kernel self:capability mknod; +allow kernel self:system module_request; +allow kernel device:dir { create write add_name remove_name rmdir }; +allow kernel device:chr_file { create setattr getattr unlink }; diff --git a/sepolicy/keystore.te b/sepolicy/keystore.te new file mode 100644 index 0000000..287151b --- /dev/null +++ b/sepolicy/keystore.te @@ -0,0 +1 @@ +hal_client_domain(keystore, hal_gatekeeper) diff --git a/sepolicy/mediacodec.te b/sepolicy/mediacodec.te new file mode 100644 index 0000000..7a645a2 --- /dev/null +++ b/sepolicy/mediacodec.te @@ -0,0 +1,3 @@ +gpu_access(mediacodec) +get_prop(mediacodec, vendor_ffmpeg_config_prop) +get_prop(mediacodec, vendor_v4l2_config_prop) diff --git a/sepolicy/mediaserver.te b/sepolicy/mediaserver.te new file mode 100644 index 0000000..922af2c --- /dev/null +++ b/sepolicy/mediaserver.te @@ -0,0 +1 @@ +gpu_access(mediaserver) diff --git a/sepolicy/mediaswcodec.te b/sepolicy/mediaswcodec.te new file mode 100644 index 0000000..ff9c5b5 --- /dev/null +++ b/sepolicy/mediaswcodec.te @@ -0,0 +1 @@ +gpu_access(mediaswcodec) diff --git a/sepolicy/netd.te b/sepolicy/netd.te new file mode 100644 index 0000000..00c4a0a --- /dev/null +++ b/sepolicy/netd.te @@ -0,0 +1,2 @@ +dontaudit netd self:capability sys_module; +allow netd kernel:system module_request; diff --git a/sepolicy/platform_app.te b/sepolicy/platform_app.te new file mode 100644 index 0000000..775e964 --- /dev/null +++ b/sepolicy/platform_app.te @@ -0,0 +1 @@ +gpu_access(platform_app) diff --git a/sepolicy/priv_app.te b/sepolicy/priv_app.te new file mode 100644 index 0000000..05c9e47 --- /dev/null +++ b/sepolicy/priv_app.te @@ -0,0 +1 @@ +gpu_access(priv_app) diff --git a/sepolicy/proc_net.te b/sepolicy/proc_net.te new file mode 100644 index 0000000..0f22770 --- /dev/null +++ b/sepolicy/proc_net.te @@ -0,0 +1 @@ +allow proc_net proc:filesystem associate; diff --git a/sepolicy/property.te b/sepolicy/property.te new file mode 100644 index 0000000..195753b --- /dev/null +++ b/sepolicy/property.te @@ -0,0 +1,6 @@ +vendor_internal_prop(vendor_audio_config_prop) +vendor_internal_prop(vendor_ffmpeg_config_prop) +vendor_internal_prop(vendor_hdmi_config_prop) +vendor_internal_prop(vendor_v4l2_config_prop) +vendor_internal_prop(vendor_hwc_config_prop) + diff --git a/sepolicy/property_contexts b/sepolicy/property_contexts new file mode 100644 index 0000000..bdfc5fc --- /dev/null +++ b/sepolicy/property_contexts @@ -0,0 +1,23 @@ +# Audio +persist.vendor.audio.device u:object_r:vendor_audio_config_prop:s0 +persist.vendor.audio.hdmi.device u:object_r:vendor_audio_config_prop:s0 +persist.vendor.audio.pcm.card.auto u:object_r:vendor_audio_config_prop:s0 +persist.vendor.audio.pcm.card u:object_r:vendor_audio_config_prop:s0 +persist.vendor.audio.pcm.device u:object_r:vendor_audio_config_prop:s0 + +# CEC +persist.vendor.hdmi.cec_device u:object_r:vendor_hdmi_config_prop:s0 + +# FFmpeg +persist.vendor.ffmpeg_codec2.rank.audio u:object_r:vendor_ffmpeg_config_prop:s0 +persist.vendor.ffmpeg_codec2.rank.video u:object_r:vendor_ffmpeg_config_prop:s0 +persist.vendor.ffmpeg_codec2.v4l2.h265 u:object_r:vendor_ffmpeg_config_prop:s0 + +#V4L2 +persist.vendor.v4l2_codec2.rank.decoder u:object_r:vendor_v4l2_config_prop:s0 +persist.vendor.v4l2_codec2.rank.encoder u:object_r:vendor_v4l2_config_prop:s0 + +# Graphics +vendor.hwc.drm.ctm u:object_r:vendor_hwc_config_prop:s0 +vendor.hwc.drm.disable_hdr u:object_r:vendor_hwc_config_prop:s0 +vendor.hwc.drm.force_mode u:object_r:vendor_hwc_config_prop:s0 \ No newline at end of file diff --git a/sepolicy/service_contexts b/sepolicy/service_contexts new file mode 100644 index 0000000..0ac9ae9 --- /dev/null +++ b/sepolicy/service_contexts @@ -0,0 +1,10 @@ +# DRM +android.hardware.drm.IDrmFactory/widevine u:object_r:hal_drm_service:s0 + +# Gatekeeper +android.hardware.security.sharedsecret.ISharedSecret/gatekeeper u:object_r:hal_gatekeeper_service:s0 + +# Graphics +mapper/minigbm_gbm_mesa u:object_r:hal_graphics_mapper_service:s0 +mapper/minigbm u:object_r:hal_graphics_mapper_service:s0 + diff --git a/sepolicy/surfaceflinger.te b/sepolicy/surfaceflinger.te new file mode 100644 index 0000000..17b66a8 --- /dev/null +++ b/sepolicy/surfaceflinger.te @@ -0,0 +1 @@ +gpu_access(surfaceflinger) diff --git a/sepolicy/suspend_blocker.te b/sepolicy/suspend_blocker.te new file mode 100644 index 0000000..fa6e02a --- /dev/null +++ b/sepolicy/suspend_blocker.te @@ -0,0 +1,6 @@ +type suspend_blocker, domain; +type suspend_blocker_exec, exec_type, vendor_file_type, file_type; + +init_daemon_domain(suspend_blocker); + +wakelock_use(suspend_blocker); diff --git a/sepolicy/system_app.te b/sepolicy/system_app.te new file mode 100644 index 0000000..4a85066 --- /dev/null +++ b/sepolicy/system_app.te @@ -0,0 +1 @@ +gpu_access(system_app) diff --git a/sepolicy/system_server.te b/sepolicy/system_server.te new file mode 100644 index 0000000..3ea1d13 --- /dev/null +++ b/sepolicy/system_server.te @@ -0,0 +1,2 @@ +gpu_access(system_server) + diff --git a/sepolicy/te_macros b/sepolicy/te_macros new file mode 100644 index 0000000..f94fe2b --- /dev/null +++ b/sepolicy/te_macros @@ -0,0 +1,9 @@ +##################################### +# gpu_access(client_domain) +# Allow client_domain to communicate with the GPU +define(`gpu_access', ` +allow $1 gpu_device:dir r_dir_perms; +allow $1 gpu_device:chr_file rw_file_perms; +allow $1 sysfs_gpu:dir r_dir_perms; +allow $1 sysfs_gpu:file r_file_perms; +') diff --git a/sepolicy/untrusted_app_all.te b/sepolicy/untrusted_app_all.te new file mode 100644 index 0000000..c429fc2 --- /dev/null +++ b/sepolicy/untrusted_app_all.te @@ -0,0 +1 @@ +gpu_access(untrusted_app_all) diff --git a/sepolicy/vendor_init.te b/sepolicy/vendor_init.te new file mode 100644 index 0000000..816ae52 --- /dev/null +++ b/sepolicy/vendor_init.te @@ -0,0 +1,7 @@ +set_prop(vendor_init, vendor_audio_config_prop) +set_prop(vendor_init, vendor_ffmpeg_config_prop) +set_prop(vendor_init, vendor_hdmi_config_prop) +set_prop(vendor_init, vendor_v4l2_config_prop) +set_prop(vendor_init, vendor_hwc_config_prop) + + diff --git a/suspend_blocker/Android.bp b/suspend_blocker/Android.bp new file mode 100644 index 0000000..49bc0ca --- /dev/null +++ b/suspend_blocker/Android.bp @@ -0,0 +1,34 @@ +// Copyright (C) 2019 The Android Open Source Project +// Copyright (C) 2021-2022 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "suspend_blocker_opi", + vendor: true, + srcs: ["suspend_blocker_opi.cpp"], + shared_libs: ["libpower"], + installable: false, +} + +prebuilt_etc { + name: "suspend_blocker_opi.rc", + src: "suspend_blocker_opi.rc", + installable: false, +} + +apex { + name: "com.android.hardware.suspend_blocker.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["suspend_blocker_opi"], + prebuilts: [ + "suspend_blocker_opi.rc", + ], +} diff --git a/suspend_blocker/apex_file_contexts b/suspend_blocker/apex_file_contexts new file mode 100644 index 0000000..d33962d --- /dev/null +++ b/suspend_blocker/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/suspend_blocker_opi u:object_r:suspend_blocker_exec:s0 diff --git a/suspend_blocker/apex_manifest.json b/suspend_blocker/apex_manifest.json new file mode 100644 index 0000000..b2be307 --- /dev/null +++ b/suspend_blocker/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.suspend_blocker.opi5", + "version": 1 +} diff --git a/suspend_blocker/suspend_blocker_opi.cpp b/suspend_blocker/suspend_blocker_opi.cpp new file mode 100644 index 0000000..0e6e1c0 --- /dev/null +++ b/suspend_blocker/suspend_blocker_opi.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2021-2022 KonstaKANG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int main() { + auto wl = android::wakelock::WakeLock::tryGet("suspend_blocker_opi"); // RAII object + if (!wl.has_value()) { + return EXIT_FAILURE; + } + + sigset_t mask; + sigemptyset(&mask); + return sigsuspend(&mask); // Infinite sleep +} diff --git a/suspend_blocker/suspend_blocker_opi.rc b/suspend_blocker/suspend_blocker_opi.rc new file mode 100644 index 0000000..7a8c608 --- /dev/null +++ b/suspend_blocker/suspend_blocker_opi.rc @@ -0,0 +1,4 @@ +service vendor.suspend_blocker_opi /apex/com.android.hardware.suspend_blocker.opi5/bin/suspend_blocker_opi + class hal + group system + user root diff --git a/usb/Android.bp b/usb/Android.bp new file mode 100644 index 0000000..a18b43e --- /dev/null +++ b/usb/Android.bp @@ -0,0 +1,54 @@ +// Copyright (C) 2020 The Android Open Source Project +// Copyright (C) 2024 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +cc_binary { + name: "android.hardware.usb.gadget-service.opi", + relative_install_path: "hw", + vendor: true, + srcs: [ + "MonitorFfs.cpp", + "UsbGadget.cpp", + "UsbGadgetUtils.cpp", + "main.cpp", + ], + shared_libs: [ + "android.hardware.usb.gadget-V1-ndk", + "libbase", + "libbinder_ndk", + "liblog", + "libutils", + ], + installable: false, +} + +prebuilt_etc { + name: "android.hardware.usb.gadget-service.opi.rc", + src: "android.hardware.usb.gadget-service.opi.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.usb.gadget-service.opi.xml", + src: "android.hardware.usb.gadget-service.opi.xml", + sub_dir: "vintf", + installable: false, +} + +apex { + name: "com.android.hardware.usb.gadget.opi5", + manifest: "apex_manifest.json", + file_contexts: "apex_file_contexts", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: ["android.hardware.usb.gadget-service.opi"], + prebuilts: [ + "android.hardware.usb.gadget-service.opi.rc", + "android.hardware.usb.gadget-service.opi.xml", + ], +} diff --git a/usb/MonitorFfs.cpp b/usb/MonitorFfs.cpp new file mode 100644 index 0000000..54e4764 --- /dev/null +++ b/usb/MonitorFfs.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "libusbconfigfs" + +#include "UsbGadgetCommon.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { +namespace gadget { + +static volatile bool gadgetPullup; + +MonitorFfs::MonitorFfs(const char* const gadget) + : mWatchFd(), + mEndpointList(), + mLock(), + mCv(), + mLockFd(), + mCurrentUsbFunctionsApplied(false), + mMonitor(), + mCallback(NULL), + mPayload(NULL), + mGadgetName(gadget), + mMonitorRunning(false) { + unique_fd eventFd(eventfd(0, 0)); + if (eventFd == -1) { + ALOGE("mEventFd failed to create %d", errno); + abort(); + } + + unique_fd epollFd(epoll_create(2)); + if (epollFd == -1) { + ALOGE("mEpollFd failed to create %d", errno); + abort(); + } + + unique_fd inotifyFd(inotify_init()); + if (inotifyFd < 0) { + ALOGE("inotify init failed"); + abort(); + } + + if (addEpollFd(epollFd, inotifyFd) == -1) abort(); + + if (addEpollFd(epollFd, eventFd) == -1) abort(); + + mEpollFd = std::move(epollFd); + mInotifyFd = std::move(inotifyFd); + mEventFd = std::move(eventFd); + gadgetPullup = false; +} + +static void displayInotifyEvent(struct inotify_event* i) { + ALOGE(" wd =%2d; ", i->wd); + if (i->cookie > 0) ALOGE("cookie =%4d; ", i->cookie); + + ALOGE("mask = "); + if (i->mask & IN_ACCESS) ALOGE("IN_ACCESS "); + if (i->mask & IN_ATTRIB) ALOGE("IN_ATTRIB "); + if (i->mask & IN_CLOSE_NOWRITE) ALOGE("IN_CLOSE_NOWRITE "); + if (i->mask & IN_CLOSE_WRITE) ALOGE("IN_CLOSE_WRITE "); + if (i->mask & IN_CREATE) ALOGE("IN_CREATE "); + if (i->mask & IN_DELETE) ALOGE("IN_DELETE "); + if (i->mask & IN_DELETE_SELF) ALOGE("IN_DELETE_SELF "); + if (i->mask & IN_IGNORED) ALOGE("IN_IGNORED "); + if (i->mask & IN_ISDIR) ALOGE("IN_ISDIR "); + if (i->mask & IN_MODIFY) ALOGE("IN_MODIFY "); + if (i->mask & IN_MOVE_SELF) ALOGE("IN_MOVE_SELF "); + if (i->mask & IN_MOVED_FROM) ALOGE("IN_MOVED_FROM "); + if (i->mask & IN_MOVED_TO) ALOGE("IN_MOVED_TO "); + if (i->mask & IN_OPEN) ALOGE("IN_OPEN "); + if (i->mask & IN_Q_OVERFLOW) ALOGE("IN_Q_OVERFLOW "); + if (i->mask & IN_UNMOUNT) ALOGE("IN_UNMOUNT "); + ALOGE("\n"); + + if (i->len > 0) ALOGE(" name = %s\n", i->name); +} + +void* MonitorFfs::startMonitorFd(void* param) { + MonitorFfs* monitorFfs = (MonitorFfs*)param; + char buf[kBufferSize]; + bool writeUdc = true, stopMonitor = false; + struct epoll_event events[kEpollEvents]; + steady_clock::time_point disconnect; + + bool descriptorWritten = true; + for (int i = 0; i < static_cast(monitorFfs->mEndpointList.size()); i++) { + if (access(monitorFfs->mEndpointList.at(i).c_str(), R_OK)) { + descriptorWritten = false; + break; + } + } + + // notify here if the endpoints are already present. + if (descriptorWritten) { + usleep(kPullUpDelay); + if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) { + lock_guard lock(monitorFfs->mLock); + monitorFfs->mCurrentUsbFunctionsApplied = true; + monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied, monitorFfs->mPayload); + gadgetPullup = true; + writeUdc = false; + ALOGI("GADGET pulled up"); + monitorFfs->mCv.notify_all(); + } + } + + while (!stopMonitor) { + int nrEvents = epoll_wait(monitorFfs->mEpollFd, events, kEpollEvents, -1); + + if (nrEvents <= 0) { + ALOGE("epoll wait did not return descriptor number"); + continue; + } + + for (int i = 0; i < nrEvents; i++) { + ALOGI("event=%u on fd=%d\n", events[i].events, events[i].data.fd); + + if (events[i].data.fd == monitorFfs->mInotifyFd) { + // Process all of the events in buffer returned by read(). + int numRead = read(monitorFfs->mInotifyFd, buf, kBufferSize); + for (char* p = buf; p < buf + numRead;) { + struct inotify_event* event = (struct inotify_event*)p; + if (kDebug) displayInotifyEvent(event); + + p += sizeof(struct inotify_event) + event->len; + + bool descriptorPresent = true; + for (int j = 0; j < static_cast(monitorFfs->mEndpointList.size()); j++) { + if (access(monitorFfs->mEndpointList.at(j).c_str(), R_OK)) { + if (kDebug) ALOGI("%s absent", monitorFfs->mEndpointList.at(j).c_str()); + descriptorPresent = false; + break; + } + } + + if (!descriptorPresent && !writeUdc) { + if (kDebug) ALOGI("endpoints not up"); + writeUdc = true; + disconnect = std::chrono::steady_clock::now(); + } else if (descriptorPresent && writeUdc) { + steady_clock::time_point temp = steady_clock::now(); + + if (std::chrono::duration_cast(temp - disconnect).count() < + kPullUpDelay) + usleep(kPullUpDelay); + + if (!!WriteStringToFile(monitorFfs->mGadgetName, PULLUP_PATH)) { + lock_guard lock(monitorFfs->mLock); + monitorFfs->mCurrentUsbFunctionsApplied = true; + monitorFfs->mCallback(monitorFfs->mCurrentUsbFunctionsApplied, + monitorFfs->mPayload); + ALOGI("GADGET pulled up"); + writeUdc = false; + gadgetPullup = true; + // notify the main thread to signal userspace. + monitorFfs->mCv.notify_all(); + } + } + } + } else { + uint64_t flag; + read(monitorFfs->mEventFd, &flag, sizeof(flag)); + if (flag == 100) { + stopMonitor = true; + break; + } + } + } + } + return NULL; +} + +void MonitorFfs::reset() { + lock_guard lock(mLockFd); + uint64_t flag = 100; + unsigned long ret; + + if (mMonitorRunning) { + // Stop the monitor thread by writing into signal fd. + ret = TEMP_FAILURE_RETRY(write(mEventFd, &flag, sizeof(flag))); + if (ret < 0) ALOGE("Error writing eventfd errno=%d", errno); + + ALOGI("mMonitor signalled to exit"); + mMonitor->join(); + ALOGI("mMonitor destroyed"); + mMonitorRunning = false; + } + + for (std::vector::size_type i = 0; i != mWatchFd.size(); i++) + inotify_rm_watch(mInotifyFd, mWatchFd[i]); + + mEndpointList.clear(); + gadgetPullup = false; + mCallback = NULL; + mPayload = NULL; +} + +bool MonitorFfs::startMonitor() { + mMonitor = unique_ptr(new thread(this->startMonitorFd, this)); + mMonitorRunning = true; + return true; +} + +bool MonitorFfs::isMonitorRunning() { + return mMonitorRunning; +} + +bool MonitorFfs::waitForPullUp(int timeout_ms) { + std::unique_lock lk(mLock); + + if (gadgetPullup) return true; + + if (mCv.wait_for(lk, timeout_ms * 1ms, [] { return gadgetPullup; })) { + ALOGI("monitorFfs signalled true"); + return true; + } else { + ALOGI("monitorFfs signalled error"); + // continue monitoring as the descriptors might be written at a later + // point. + return false; + } +} + +bool MonitorFfs::addInotifyFd(string fd) { + lock_guard lock(mLockFd); + int wfd; + + wfd = inotify_add_watch(mInotifyFd, fd.c_str(), IN_ALL_EVENTS); + if (wfd == -1) + return false; + else + mWatchFd.push_back(wfd); + + return true; +} + +void MonitorFfs::addEndPoint(string ep) { + lock_guard lock(mLockFd); + + mEndpointList.push_back(ep); +} + +void MonitorFfs::registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied, + void* payload), + void* payload) { + mCallback = callback; + mPayload = payload; +} + +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/usb/UsbGadget.cpp b/usb/UsbGadget.cpp new file mode 100644 index 0000000..616c259 --- /dev/null +++ b/usb/UsbGadget.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "android.hardware.usb.gadget-service.opi" + +#include "UsbGadget.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { +namespace gadget { + +UsbGadget::UsbGadget() { + if (access(OS_DESC_PATH, R_OK) != 0) { + ALOGE("configfs setup not done yet"); + abort(); + } +} + +void currentFunctionsAppliedCallback(bool functionsApplied, void* payload) { + UsbGadget* gadget = (UsbGadget*)payload; + gadget->mCurrentUsbFunctionsApplied = functionsApplied; +} + +ScopedAStatus UsbGadget::getCurrentUsbFunctions(const shared_ptr& callback, + int64_t in_transactionId) { + if (callback == nullptr) { + return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER); + } + + ScopedAStatus ret = callback->getCurrentUsbFunctionsCb( + mCurrentUsbFunctions, + mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED : Status::FUNCTIONS_NOT_APPLIED, + in_transactionId); + if (!ret.isOk()) + ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.getDescription().c_str()); + + return ScopedAStatus::ok(); +} + +ScopedAStatus UsbGadget::getUsbSpeed(const shared_ptr &callback, + int64_t in_transactionId) { + std::string current_speed; + if (ReadFileToString(SPEED_PATH, ¤t_speed)) { + current_speed = Trim(current_speed); + ALOGI("current USB speed is %s", current_speed.c_str()); + if (current_speed == "low-speed") + mUsbSpeed = UsbSpeed::LOWSPEED; + else if (current_speed == "full-speed") + mUsbSpeed = UsbSpeed::FULLSPEED; + else if (current_speed == "high-speed") + mUsbSpeed = UsbSpeed::HIGHSPEED; + else if (current_speed == "super-speed") + mUsbSpeed = UsbSpeed::SUPERSPEED; + else if (current_speed == "super-speed-plus") + mUsbSpeed = UsbSpeed::SUPERSPEED_10Gb; + else if (current_speed == "UNKNOWN") + mUsbSpeed = UsbSpeed::UNKNOWN; + else + mUsbSpeed = UsbSpeed::UNKNOWN; + } else { + ALOGE("Fail to read current speed"); + mUsbSpeed = UsbSpeed::UNKNOWN; + } + + if (callback) { + ScopedAStatus ret = callback->getUsbSpeedCb(mUsbSpeed, in_transactionId); + + if (!ret.isOk()) + ALOGE("Call to getUsbSpeedCb failed %s", ret.getDescription().c_str()); + } + + return ScopedAStatus::ok(); +} + +Status UsbGadget::tearDownGadget() { + if (resetGadget() != Status::SUCCESS) return Status::ERROR; + + if (monitorFfs.isMonitorRunning()) { + monitorFfs.reset(); + } else { + ALOGI("mMonitor not running"); + } + return Status::SUCCESS; +} + +ScopedAStatus UsbGadget::reset(const shared_ptr &callback, + int64_t in_transactionId) { + if (!WriteStringToFile("none", PULLUP_PATH)) { + ALOGI("Gadget cannot be pulled down"); + if (callback) + callback->resetCb(Status::ERROR, in_transactionId); + + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Error while calling resetCb"); + } + + usleep(kDisconnectWaitUs); + + if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) { + ALOGI("Gadget cannot be pulled up"); + if (callback) + callback->resetCb(Status::ERROR, in_transactionId); + + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Error while calling resetCb"); + } + + if (callback) + callback->resetCb(Status::SUCCESS, in_transactionId); + + return ScopedAStatus::ok(); +} + +static Status validateAndSetVidPid(uint64_t functions) { + Status ret = Status::SUCCESS; + + switch (functions) { + case GadgetFunction::MTP: + ret = setVidPid("0x18d1", "0x4ee1"); + break; + case GadgetFunction::ADB | GadgetFunction::MTP: + ret = setVidPid("0x18d1", "0x4ee2"); + break; + case GadgetFunction::RNDIS: + ret = setVidPid("0x18d1", "0x4ee3"); + break; + case GadgetFunction::ADB | GadgetFunction::RNDIS: + ret = setVidPid("0x18d1", "0x4ee4"); + break; + case GadgetFunction::PTP: + ret = setVidPid("0x18d1", "0x4ee5"); + break; + case GadgetFunction::ADB | GadgetFunction::PTP: + ret = setVidPid("0x18d1", "0x4ee6"); + break; + case GadgetFunction::ADB: + ret = setVidPid("0x18d1", "0x4ee7"); + break; + case GadgetFunction::MIDI: + ret = setVidPid("0x18d1", "0x4ee8"); + break; + case GadgetFunction::ADB | GadgetFunction::MIDI: + ret = setVidPid("0x18d1", "0x4ee9"); + break; + case GadgetFunction::NCM: + ret = setVidPid("0x18d1", "0x4eeb"); + break; + case GadgetFunction::ADB | GadgetFunction::NCM: + ret = setVidPid("0x18d1", "0x4eec"); + break; + case GadgetFunction::ACCESSORY: + ret = setVidPid("0x18d1", "0x2d00"); + break; + case GadgetFunction::ADB | GadgetFunction::ACCESSORY: + ret = setVidPid("0x18d1", "0x2d01"); + break; + case GadgetFunction::AUDIO_SOURCE: + ret = setVidPid("0x18d1", "0x2d02"); + break; + case GadgetFunction::ADB | GadgetFunction::AUDIO_SOURCE: + ret = setVidPid("0x18d1", "0x2d03"); + break; + case GadgetFunction::ACCESSORY | GadgetFunction::AUDIO_SOURCE: + ret = setVidPid("0x18d1", "0x2d04"); + break; + case GadgetFunction::ADB | GadgetFunction::ACCESSORY | + GadgetFunction::AUDIO_SOURCE: + ret = setVidPid("0x18d1", "0x2d05"); + break; + default: + ALOGE("Combination not supported"); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } + return ret; +} + +Status UsbGadget::setupFunctions(long functions, + const shared_ptr &callback, uint64_t timeout, + int64_t in_transactionId) { + bool ffsEnabled = false; + int i = 0; + + if (addGenericAndroidFunctions(&monitorFfs, functions, &ffsEnabled, &i) != + Status::SUCCESS) + return Status::ERROR; + + if ((functions & GadgetFunction::ADB) != 0) { + ffsEnabled = true; + if (addAdb(&monitorFfs, &i) != Status::SUCCESS) return Status::ERROR; + } + + // Pull up the gadget right away when there are no ffs functions. + if (!ffsEnabled) { + if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) + return Status::ERROR; + mCurrentUsbFunctionsApplied = true; + + if (callback) + callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId); + + return Status::SUCCESS; + } + + monitorFfs.registerFunctionsAppliedCallback(¤tFunctionsAppliedCallback, this); + // Monitors the ffs paths to pull up the gadget when descriptors are written. + // Also takes of the pulling up the gadget again if the userspace process + // dies and restarts. + monitorFfs.startMonitor(); + + if (kDebug) ALOGI("Mainthread in Cv"); + + if (callback) { + bool pullup = monitorFfs.waitForPullUp(timeout); + ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, + pullup ? Status::SUCCESS : Status::ERROR, + in_transactionId); + if (!ret.isOk()) + ALOGE("setCurrentUsbFunctionsCb error %s", ret.getMessage()); + } + + return Status::SUCCESS; +} + +ScopedAStatus UsbGadget::setCurrentUsbFunctions(int64_t functions, + const shared_ptr &callback, + int64_t timeoutMs, + int64_t in_transactionId) { + std::unique_lock lk(mLockSetCurrentFunction); + + mCurrentUsbFunctions = functions; + mCurrentUsbFunctionsApplied = false; + + // Unlink the gadget and stop the monitor if running. + Status status = tearDownGadget(); + if (status != Status::SUCCESS) { + goto error; + } + + ALOGI("Returned from tearDown gadget"); + + // Leave the gadget pulled down to give time for the host to sense disconnect. + usleep(kDisconnectWaitUs); + + if (functions == GadgetFunction::NONE) { + if (callback == NULL) + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "callback == NULL"); + ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId); + if (!ret.isOk()) + ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.getDescription().c_str()); + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Error while calling setCurrentUsbFunctionsCb"); + } + + status = validateAndSetVidPid(functions); + + if (status != Status::SUCCESS) { + goto error; + } + + status = setupFunctions(functions, callback, timeoutMs, in_transactionId); + if (status != Status::SUCCESS) { + goto error; + } + + ALOGI("Usb Gadget setcurrent functions called successfully"); + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Usb Gadget setcurrent functions called successfully"); + +error: + ALOGI("Usb Gadget setcurrent functions failed"); + if (callback == NULL) + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Usb Gadget setcurrent functions failed"); + ScopedAStatus ret = callback->setCurrentUsbFunctionsCb(functions, status, in_transactionId); + if (!ret.isOk()) + ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.getDescription().c_str()); + return ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Error while calling setCurrentUsbFunctionsCb"); +} +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/usb/UsbGadget.h b/usb/UsbGadget.h new file mode 100644 index 0000000..637dc13 --- /dev/null +++ b/usb/UsbGadget.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { +namespace gadget { + +using ::aidl::android::hardware::usb::gadget::GadgetFunction; +using ::aidl::android::hardware::usb::gadget::IUsbGadgetCallback; +using ::aidl::android::hardware::usb::gadget::IUsbGadget; +using ::aidl::android::hardware::usb::gadget::Status; +using ::aidl::android::hardware::usb::gadget::UsbSpeed; +using ::android::base::GetProperty; +using ::android::base::SetProperty; +using ::android::base::unique_fd; +using ::android::base::ReadFileToString; +using ::android::base::Trim; +using ::android::base::WriteStringToFile; +using ::ndk::ScopedAStatus; +using ::std::shared_ptr; +using ::std::string; + +//constexpr char kGadgetName[] = "1000480000.usb"; +constexpr char kGadgetName[] = "1000480000.usb"; +static MonitorFfs monitorFfs(kGadgetName); + +#define UDC_PATH "/sys/class/udc/1000480000.usb/" +#define SPEED_PATH UDC_PATH "current_speed" + +struct UsbGadget : public BnUsbGadget { + UsbGadget(); + + // Makes sure that only one request is processed at a time. + std::mutex mLockSetCurrentFunction; + long mCurrentUsbFunctions; + bool mCurrentUsbFunctionsApplied; + UsbSpeed mUsbSpeed; + + ScopedAStatus setCurrentUsbFunctions(int64_t functions, + const shared_ptr &callback, + int64_t timeoutMs, int64_t in_transactionId) override; + + ScopedAStatus getCurrentUsbFunctions(const shared_ptr &callback, + int64_t in_transactionId) override; + + ScopedAStatus reset(const shared_ptr &callback, + int64_t in_transactionId) override; + + ScopedAStatus getUsbSpeed(const shared_ptr &callback, + int64_t in_transactionId) override; + + private: + Status tearDownGadget(); + Status setupFunctions(long functions, const shared_ptr &callback, + uint64_t timeout, int64_t in_transactionId); +}; + +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/usb/UsbGadgetCommon.h b/usb/UsbGadgetCommon.h new file mode 100644 index 0000000..e928852 --- /dev/null +++ b/usb/UsbGadgetCommon.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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. + */ + +#ifndef HARDWARE_USB_USBGADGETCOMMON_H +#define HARDWARE_USB_USBGADGETCOMMON_H + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { +namespace gadget { + +constexpr int kBufferSize = 512; +constexpr int kMaxFilePathLength = 256; +constexpr int kEpollEvents = 10; +constexpr bool kDebug = false; +constexpr int kDisconnectWaitUs = 100000; +constexpr int kPullUpDelay = 500000; +constexpr int kShutdownMonitor = 100; + +constexpr char kBuildType[] = "ro.build.type"; +constexpr char kPersistentVendorConfig[] = "persist.vendor.usb.usbradio.config"; +constexpr char kVendorConfig[] = "vendor.usb.config"; +constexpr char kVendorRndisConfig[] = "vendor.usb.rndis.config"; + +#define GADGET_PATH "/config/usb_gadget/g1/" +#define PULLUP_PATH GADGET_PATH "UDC" +#define PERSISTENT_BOOT_MODE "ro.bootmode" +#define VENDOR_ID_PATH GADGET_PATH "idVendor" +#define PRODUCT_ID_PATH GADGET_PATH "idProduct" +#define DEVICE_CLASS_PATH GADGET_PATH "bDeviceClass" +#define DEVICE_SUB_CLASS_PATH GADGET_PATH "bDeviceSubClass" +#define DEVICE_PROTOCOL_PATH GADGET_PATH "bDeviceProtocol" +#define DESC_USE_PATH GADGET_PATH "os_desc/use" +#define OS_DESC_PATH GADGET_PATH "os_desc/b.1" +#define CONFIG_PATH GADGET_PATH "configs/b.1/" +#define FUNCTIONS_PATH GADGET_PATH "functions/" +#define FUNCTION_NAME "function" +#define FUNCTION_PATH CONFIG_PATH FUNCTION_NAME +#define RNDIS_PATH FUNCTIONS_PATH "gsi.rndis" + +using ::android::base::GetProperty; +using ::android::base::SetProperty; +using ::android::base::unique_fd; +using ::android::base::WriteStringToFile; +using ::aidl::android::hardware::usb::gadget::GadgetFunction; +using ::aidl::android::hardware::usb::gadget::Status; + +using ::std::lock_guard; +using ::std::move; +using ::std::mutex; +using ::std::string; +using ::std::thread; +using ::std::unique_ptr; +using ::std::vector; +using ::std::chrono::microseconds; +using ::std::chrono::steady_clock; +using ::std::literals::chrono_literals::operator""ms; + +// MonitorFfs automously manages gadget pullup by monitoring +// the ep file status. Restarts the usb gadget when the ep +// owner restarts. +class MonitorFfs { + private: + // Monitors the endpoints Inotify events. + unique_fd mInotifyFd; + // Control pipe for shutting down the mMonitor thread. + // mMonitor exits when SHUTDOWN_MONITOR is written into + // mEventFd/ + unique_fd mEventFd; + // Pools on mInotifyFd and mEventFd. + unique_fd mEpollFd; + vector mWatchFd; + + // Maintains the list of Endpoints. + vector mEndpointList; + // protects the CV. + std::mutex mLock; + std::condition_variable mCv; + // protects mInotifyFd, mEpollFd. + std::mutex mLockFd; + + // Flag to maintain the current status of gadget pullup. + bool mCurrentUsbFunctionsApplied; + + // Thread object that executes the ep monitoring logic. + unique_ptr mMonitor; + // Callback to be invoked when gadget is pulled up. + void (*mCallback)(bool functionsApplied, void* payload); + void* mPayload; + // Name of the USB gadget. Used for pullup. + const char* const mGadgetName; + // Monitor State + bool mMonitorRunning; + + public: + MonitorFfs(const char* const gadget); + // Inits all the UniqueFds. + void reset(); + // Starts monitoring endpoints and pullup the gadget when + // the descriptors are written. + bool startMonitor(); + // Waits for timeout_ms for gadget pull up to happen. + // Returns immediately if the gadget is already pulled up. + bool waitForPullUp(int timeout_ms); + // Adds the given fd to the watch list. + bool addInotifyFd(string fd); + // Adds the given endpoint to the watch list. + void addEndPoint(string ep); + // Registers the async callback from the caller to notify the caller + // when the gadget pull up happens. + void registerFunctionsAppliedCallback(void (*callback)(bool functionsApplied, void*(payload)), + void* payload); + bool isMonitorRunning(); + // Ep monitoring and the gadget pull up logic. + static void* startMonitorFd(void* param); +}; + +//**************** Helper functions ************************// + +// Adds the given fd to the epollfd(epfd). +int addEpollFd(const unique_fd& epfd, const unique_fd& fd); +// Removes all the usb functions link in the specified path. +int unlinkFunctions(const char* path); +// Craetes a configfs link for the function. +int linkFunction(const char* function, int index); +// Sets the USB VID and PID. +Status setVidPid(const char* vid, const char* pid); +// Extracts vendor functions from the vendor init properties. +std::string getVendorFunctions(); +// Adds Adb to the usb configuration. +Status addAdb(MonitorFfs* monitorFfs, int* functionCount); +// Adds all applicable generic android usb functions other than ADB. +Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled, + int* functionCount); +// Pulls down USB gadget. +Status resetGadget(); + +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android +} // namespace aidl +#endif diff --git a/usb/UsbGadgetUtils.cpp b/usb/UsbGadgetUtils.cpp new file mode 100644 index 0000000..d9843dc --- /dev/null +++ b/usb/UsbGadgetUtils.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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. + */ + +#define LOG_TAG "libusbconfigfs" + +#include "UsbGadgetCommon.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { +namespace gadget { + +int unlinkFunctions(const char* path) { + DIR* config = opendir(path); + struct dirent* function; + char filepath[kMaxFilePathLength]; + int ret = 0; + + if (config == NULL) return -1; + + // d_type does not seems to be supported in /config + // so filtering by name. + while (((function = readdir(config)) != NULL)) { + if ((strstr(function->d_name, FUNCTION_NAME) == NULL)) continue; + // build the path for each file in the folder. + sprintf(filepath, "%s/%s", path, function->d_name); + ret = remove(filepath); + if (ret) { + ALOGE("Unable remove file %s errno:%d", filepath, errno); + break; + } + } + + closedir(config); + return ret; +} + +int addEpollFd(const unique_fd& epfd, const unique_fd& fd) { + struct epoll_event event; + int ret; + + event.data.fd = fd; + event.events = EPOLLIN; + + ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); + if (ret) ALOGE("epoll_ctl error %d", errno); + + return ret; +} + +int linkFunction(const char* function, int index) { + char functionPath[kMaxFilePathLength]; + char link[kMaxFilePathLength]; + + sprintf(functionPath, "%s%s", FUNCTIONS_PATH, function); + sprintf(link, "%s%d", FUNCTION_PATH, index); + if (symlink(functionPath, link)) { + ALOGE("Cannot create symlink %s -> %s errno:%d", link, functionPath, errno); + return -1; + } + return 0; +} + +Status setVidPid(const char* vid, const char* pid) { + if (!WriteStringToFile(vid, VENDOR_ID_PATH)) return Status::ERROR; + + if (!WriteStringToFile(pid, PRODUCT_ID_PATH)) return Status::ERROR; + + return Status::SUCCESS; +} + +std::string getVendorFunctions() { + if (GetProperty(kBuildType, "") == "user") return "user"; + + std::string bootMode = GetProperty(PERSISTENT_BOOT_MODE, ""); + std::string persistVendorFunctions = GetProperty(kPersistentVendorConfig, ""); + std::string vendorFunctions = GetProperty(kVendorConfig, ""); + std::string ret = ""; + + if (vendorFunctions != "") { + ret = vendorFunctions; + } else if (bootMode == "usbradio" || bootMode == "factory" || bootMode == "ffbm-00" || + bootMode == "ffbm-01") { + if (persistVendorFunctions != "") + ret = persistVendorFunctions; + else + ret = "diag"; + // vendor.usb.config will reflect the current configured functions + SetProperty(kVendorConfig, ret); + } + + return ret; +} + +Status resetGadget() { + ALOGI("setCurrentUsbFunctions None"); + + if (!WriteStringToFile("none", PULLUP_PATH)) ALOGI("Gadget cannot be pulled down"); + + if (!WriteStringToFile("0", DEVICE_CLASS_PATH)) return Status::ERROR; + + if (!WriteStringToFile("0", DEVICE_SUB_CLASS_PATH)) return Status::ERROR; + + if (!WriteStringToFile("0", DEVICE_PROTOCOL_PATH)) return Status::ERROR; + + if (!WriteStringToFile("0", DESC_USE_PATH)) return Status::ERROR; + + if (unlinkFunctions(CONFIG_PATH)) return Status::ERROR; + + return Status::SUCCESS; +} + +Status addGenericAndroidFunctions(MonitorFfs* monitorFfs, uint64_t functions, bool* ffsEnabled, + int* functionCount) { + if (((functions & GadgetFunction::MTP) != 0)) { + *ffsEnabled = true; + ALOGI("setCurrentUsbFunctions mtp"); + if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR; + + if (!monitorFfs->addInotifyFd("/dev/usb-ffs/mtp/")) return Status::ERROR; + + if (linkFunction("ffs.mtp", (*functionCount)++)) return Status::ERROR; + + // Add endpoints to be monitored. + monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep1"); + monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep2"); + monitorFfs->addEndPoint("/dev/usb-ffs/mtp/ep3"); + } else if (((functions & GadgetFunction::PTP) != 0)) { + *ffsEnabled = true; + ALOGI("setCurrentUsbFunctions ptp"); + if (!WriteStringToFile("1", DESC_USE_PATH)) return Status::ERROR; + + if (!monitorFfs->addInotifyFd("/dev/usb-ffs/ptp/")) return Status::ERROR; + + if (linkFunction("ffs.ptp", (*functionCount)++)) return Status::ERROR; + + // Add endpoints to be monitored. + monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep1"); + monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep2"); + monitorFfs->addEndPoint("/dev/usb-ffs/ptp/ep3"); + } + + if ((functions & GadgetFunction::MIDI) != 0) { + ALOGI("setCurrentUsbFunctions MIDI"); + if (linkFunction("midi.gs5", (*functionCount)++)) return Status::ERROR; + } + + if ((functions & GadgetFunction::ACCESSORY) != 0) { + ALOGI("setCurrentUsbFunctions Accessory"); + if (linkFunction("accessory.gs2", (*functionCount)++)) return Status::ERROR; + } + + if ((functions & GadgetFunction::AUDIO_SOURCE) != 0) { + ALOGI("setCurrentUsbFunctions Audio Source"); + if (linkFunction("audio_source.gs3", (*functionCount)++)) return Status::ERROR; + } + + if ((functions & GadgetFunction::RNDIS) != 0) { + ALOGI("setCurrentUsbFunctions rndis"); + std::string rndisFunction = GetProperty(kVendorRndisConfig, ""); + if (rndisFunction != "") { + if (linkFunction(rndisFunction.c_str(), (*functionCount)++)) return Status::ERROR; + } else { + // link gsi.rndis for older pixel projects + if (linkFunction("gsi.rndis", (*functionCount)++)) return Status::ERROR; + } + } + + if ((functions & GadgetFunction::NCM) != 0) { + ALOGI("setCurrentUsbFunctions ncm"); + if (linkFunction("ncm.gs6", (*functionCount)++)) return Status::ERROR; + } + + return Status::SUCCESS; +} + +Status addAdb(MonitorFfs* monitorFfs, int* functionCount) { + ALOGI("setCurrentUsbFunctions Adb"); + if (!WriteStringToFile("1", DESC_USE_PATH)) + return Status::ERROR; + + if (!monitorFfs->addInotifyFd("/dev/usb-ffs/adb/")) return Status::ERROR; + + if (linkFunction("ffs.adb", (*functionCount)++)) return Status::ERROR; + monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep1"); + monitorFfs->addEndPoint("/dev/usb-ffs/adb/ep2"); + ALOGI("Service started"); + return Status::SUCCESS; +} + +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/usb/android.hardware.usb.gadget-service.opi.rc b/usb/android.hardware.usb.gadget-service.opi.rc new file mode 100644 index 0000000..54a4405 --- /dev/null +++ b/usb/android.hardware.usb.gadget-service.opi.rc @@ -0,0 +1,4 @@ +service vendor.usb_gadget-opi /apex/com.android.hardware.usb.gadget.opi5/bin/hw/android.hardware.usb.gadget-service.opi + class hal + user system + group system shell mtp diff --git a/usb/android.hardware.usb.gadget-service.opi.xml b/usb/android.hardware.usb.gadget-service.opi.xml new file mode 100644 index 0000000..e7eebc3 --- /dev/null +++ b/usb/android.hardware.usb.gadget-service.opi.xml @@ -0,0 +1,10 @@ + + + android.hardware.usb.gadget + 1 + + IUsbGadget + default + + + diff --git a/usb/apex_file_contexts b/usb/apex_file_contexts new file mode 100644 index 0000000..4b14048 --- /dev/null +++ b/usb/apex_file_contexts @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/android\.hardware\.usb\.gadget-service\.opi u:object_r:hal_usb_gadget_default_exec:s0 diff --git a/usb/apex_manifest.json b/usb/apex_manifest.json new file mode 100644 index 0000000..fbd0033 --- /dev/null +++ b/usb/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.usb.gadget.opi5", + "version": 1 +} diff --git a/usb/main.cpp b/usb/main.cpp new file mode 100644 index 0000000..693a163 --- /dev/null +++ b/usb/main.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 KonstaKANG + * + * 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 "UsbGadget.h" + +#include +#include +#include + +using ::aidl::android::hardware::usb::gadget::UsbGadget; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr usbgadget = ndk::SharedRefBase::make(); + + const std::string instance = std::string() + UsbGadget::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(usbgadget->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/vendor.prop b/vendor.prop new file mode 100644 index 0000000..7d8df4a --- /dev/null +++ b/vendor.prop @@ -0,0 +1,96 @@ +# Audio +persist.vendor.audio.device=jack +persist.vendor.audio.hdmi.device=vc4hdmi0 +#persist.vendor.audio.pcm.card.auto=true +persist.audio.pcm.card=2 +persist.vendor.audio.pcm.device=0 +ro.config.media_vol_default=20 +ro.config.media_vol_steps=25 +ro.hardware.audio.primary=opi + +# Desktop mode +#persist.wm.debug.desktop_experience_devopts=1 +#persist.wm.debug.desktop_mode_enforce_device_restrictions=false +#persist.wm.debug.force_desktop_first_on_default_display_for_testing=true + +# Bluetooth +bluetooth.device.class_of_device?=90,2,12 +bluetooth.device.default_name=OrangePi 5 Pro +bluetooth.profile.a2dp.sink.enabled?=true +bluetooth.profile.a2dp.source.enabled?=true +bluetooth.profile.asha.central.enabled?=true +bluetooth.profile.avrcp.controller.enabled?=true +bluetooth.profile.avrcp.target.enabled?=true +bluetooth.profile.bap.broadcast.assist.enabled?=true +bluetooth.profile.bap.unicast.client.enabled?=true +bluetooth.profile.bas.client.enabled?=true +bluetooth.profile.ccp.server.enabled?=true +bluetooth.profile.csip.set_coordinator.enabled?=true +bluetooth.profile.gatt.enabled?=true +bluetooth.profile.hap.client.enabled?=true +bluetooth.profile.hfp.ag.enabled?=true +bluetooth.profile.hid.device.enabled?=true +bluetooth.profile.hid.host.enabled?=true +bluetooth.profile.map.server.enabled?=true +bluetooth.profile.mcp.server.enabled?=true +bluetooth.profile.opp.enabled?=true +bluetooth.profile.pan.nap.enabled?=true +bluetooth.profile.pan.panu.enabled?=true +bluetooth.profile.pbap.server.enabled?=true +bluetooth.profile.sap.server.enabled?=true +bluetooth.profile.vcp.controller.enabled?=true +persist.bluetooth.a2dp_aac.vbr_supported=true + +# Camera +ro.hardware.camera=libcamera + +# CEC +#persist.vendor.hdmi.cec_device=cec0 +#ro.hdmi.cec_device_types=playback_device +#ro.hdmi.device_type=4 + +# Chipset +ro.soc.manufacturer=Rockchip +ro.soc.model=RK3588s + + +# FFmpeg +persist.vendor.ffmpeg_codec2.rank.audio=16 +persist.vendor.ffmpeg_codec2.rank.video=256 +persist.vendor.ffmpeg_codec2.v4l2.h265=true + +# Graphics + +#ro.vulkan.abi=arm64-v8a +#ro.vulkan.version=1.2.0 +vendor.hwc.drm.ctm=DRM_OR_IGNORE +#vendor.hwc.drm.disable_hdr=1 +#vendor.hwc.drm.force_mode=1920x1080 + +# Lockscreen +ro.lockscreen.disable.default=true + +# Media +debug.stagefright.c2inputsurface=-1 +media.c2.hal.selection=aidl + +# MGLRU +persist.device_config.mglru_native.lru_gen_config=core + +# Virtualization +ro.boot.hypervisor.vm.supported=1 + +# Wifi +ro.boot.wificountrycode=00 +wifi.interface=wlan0 + +# Window extensions +persist.settings.large_screen_opt.enabled=true + +#minigbm +vendor.minigbm.debug=logbos + +# Mesa +ro.hardware.egl=mesa + + diff --git a/wifi/Android.bp b/wifi/Android.bp new file mode 100644 index 0000000..2ac32dc --- /dev/null +++ b/wifi/Android.bp @@ -0,0 +1,69 @@ +// Copyright (C) 2025 KonstaKANG +// Copyright (C) 2025 Venkata Atchuta Bheemeswara Sarma Darbha +// +// SPDX-License-Identifier: Apache-2.0 + +prebuilt_etc { + name: "android.hardware.wifi.hostapd-service.rc", + src: "android.hardware.wifi.hostapd-service.rc", + installable: false, +} + +prebuilt_etc { + name: "android.hardware.wifi.supplicant-service.rc", + src: "android.hardware.wifi.supplicant-service.rc", + installable: false, +} + +prebuilt_etc { + name: "wpa_supplicant.conf.opi", + src: ":wpa_supplicant_template.conf", + filename: "wpa_supplicant.conf", + relative_install_path: "wifi", + installable: false, +} + +prebuilt_etc { + name: "wpa_supplicant_overlay.conf", + src: "wpa_supplicant_overlay.conf", + relative_install_path: "wifi", + installable: false, +} + +apex { + name: "com.android.hardware.wifi.hostapd.opi5", + manifest: "apex_manifest_hostapd.json", + file_contexts: "apex_file_contexts_hostapd", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: [ + "//external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant:hostapd", + ], + prebuilts: [ + "android.hardware.wifi.hostapd-service.rc", + "android.hardware.wifi.hostapd.xml.prebuilt", + ], +} + +apex { + name: "com.android.hardware.wifi.supplicant.opi5", + manifest: "apex_manifest_supplicant.json", + file_contexts: "apex_file_contexts_supplicant", + key: "com.android.hardware.key", + certificate: ":com.android.hardware.certificate", + updatable: false, + vendor: true, + + binaries: [ + "//external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant:wpa_supplicant", + ], + prebuilts: [ + "android.hardware.wifi.supplicant-service.rc", + "android.hardware.wifi.supplicant.xml.prebuilt", + "wpa_supplicant.conf.opi", + "wpa_supplicant_overlay.conf", + ], +} diff --git a/wifi/android.hardware.wifi.hostapd-service.rc b/wifi/android.hardware.wifi.hostapd-service.rc new file mode 100644 index 0000000..8a020b3 --- /dev/null +++ b/wifi/android.hardware.wifi.hostapd-service.rc @@ -0,0 +1,13 @@ +on property:apex.all.ready=true + mkdir /data/vendor/wifi 0770 wifi wifi + mkdir /data/vendor/wifi/hostapd 0770 wifi wifi + mkdir /data/vendor/wifi/hostapd/sockets 0770 wifi wifi + +service hostapd /apex/com.android.hardware.wifi.hostapd.opi5/bin/hw/hostapd + interface aidl android.hardware.wifi.hostapd.IHostapd/default + class main + capabilities NET_ADMIN NET_RAW + user wifi + group wifi net_raw net_admin + disabled + oneshot diff --git a/wifi/android.hardware.wifi.supplicant-service.rc b/wifi/android.hardware.wifi.supplicant-service.rc new file mode 100644 index 0000000..2fef458 --- /dev/null +++ b/wifi/android.hardware.wifi.supplicant-service.rc @@ -0,0 +1,20 @@ +on property:apex.all.ready=true + mkdir /data/vendor/wifi 0770 wifi wifi + mkdir /data/vendor/wifi/wpa 0770 wifi wifi + mkdir /data/vendor/wifi/wpa/sockets 0770 wifi wifi + +service wpa_supplicant /apex/com.android.hardware.wifi.supplicant.opi5/bin/hw/wpa_supplicant \ + -O/data/vendor/wifi/wpa/sockets -dd \ + -g@android:wpa_wlan0 + # we will start as root and wpa_supplicant will switch to user wifi + # after setting up the capabilities required for WEXT + # user wifi + # group wifi inet keystore + interface aidl android.hardware.wifi.supplicant.ISupplicant/default + class main + socket wpa_wlan0 dgram 660 wifi wifi + user wifi + group wifi net_raw net_admin + capabilities NET_RAW NET_ADMIN + disabled + oneshot diff --git a/wifi/apex_file_contexts_hostapd b/wifi/apex_file_contexts_hostapd new file mode 100644 index 0000000..aff5bfc --- /dev/null +++ b/wifi/apex_file_contexts_hostapd @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/hostapd u:object_r:hal_wifi_hostapd_default_exec:s0 diff --git a/wifi/apex_file_contexts_supplicant b/wifi/apex_file_contexts_supplicant new file mode 100644 index 0000000..48e4be6 --- /dev/null +++ b/wifi/apex_file_contexts_supplicant @@ -0,0 +1,3 @@ +(/.*)? u:object_r:vendor_file:s0 +/etc(/.*)? u:object_r:vendor_configs_file:s0 +/bin/hw/wpa_supplicant u:object_r:hal_wifi_supplicant_default_exec:s0 diff --git a/wifi/apex_manifest_hostapd.json b/wifi/apex_manifest_hostapd.json new file mode 100644 index 0000000..3171524 --- /dev/null +++ b/wifi/apex_manifest_hostapd.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.wifi.hostapd.opi5", + "version": 1 +} diff --git a/wifi/apex_manifest_supplicant.json b/wifi/apex_manifest_supplicant.json new file mode 100644 index 0000000..2b5aed3 --- /dev/null +++ b/wifi/apex_manifest_supplicant.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.hardware.wifi.supplicant.opi5", + "version": 1 +} diff --git a/wifi/wpa_supplicant_overlay.conf b/wifi/wpa_supplicant_overlay.conf new file mode 100644 index 0000000..740792b --- /dev/null +++ b/wifi/wpa_supplicant_overlay.conf @@ -0,0 +1,5 @@ +disable_scan_offload=1 +wowlan_triggers=any +p2p_disabled=1 +filter_rssi=-75 +no_ctrl_interface= \ No newline at end of file diff --git a/wrimg.sh b/wrimg.sh new file mode 100755 index 0000000..6146266 --- /dev/null +++ b/wrimg.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# +# Copyright (C) 2025 KonstaKANG +# +# SPDX-License-Identifier: Apache-2.0 +# + +exit_with_error() { + echo $@ + exit 1 +} + +check_device() { + for PARTITION in "1" "2" "3" "4"; do + if [ ! -b /dev/${1}${PARTITION} ]; then + return 1 + fi + done + + PARTITION1=$(lsblk -o LABEL,SIZE -b /dev/${1}1 | tail -n 1) + PARTITION2=$(lsblk -o LABEL,SIZE -b /dev/${1}2 | tail -n 1) + PARTITION3=$(lsblk -o LABEL,SIZE -b /dev/${1}3 | tail -n 1) + PARTITION4=$(lsblk -o LABEL,SIZE -b /dev/${1}4 | tail -n 1) + + if [ $(echo ${PARTITION1} | awk {'print $1'}) != "boot" ] || [ $(echo ${PARTITION1} | awk {'print $2'}) != "134217728" ]; then + return 1 + fi + if [ $(echo ${PARTITION2} | awk {'print $1'}) != "/" ] || [ $(echo ${PARTITION2} | awk {'print $2'}) != "2684354560" ]; then + return 1 + fi + if [ $(echo ${PARTITION3} | awk {'print $1'}) != "vendor" ] || [ $(echo ${PARTITION3} | awk {'print $2'}) != "268435456" ]; then + return 1 + fi + if [ $(echo ${PARTITION4} | awk {'print $1'}) != "userdata" ]; then + return 1 + fi + + DEVICE=${1} + return 0 +} + +find_device() { + for SDX in "sda" "sdb" "sdc" "sdd"; do + check_device ${SDX} + if [ $? == "0" ]; then + break + fi + done + + if [ -z ${DEVICE} ]; then + exit_with_error "Unable to find suitable block device!" + fi +} + +confirm() { + echo "Build target ${1}..." + if [ "${2}" == "wipe" ]; then + echo "Wiping userdata partition..." + else + echo "Writing ${2} image..." + fi + echo "Writing to device /dev/${DEVICE}..." + lsblk -o NAME,LABEL,SIZE /dev/${DEVICE} + + read -p "Continue (y/n)? " -n 1 -r RESPONSE + echo "" + if [[ ! ${RESPONSE} =~ ^[Yy]$ ]]; then + exit_with_error "Exiting!" + fi +} + +write_partition() { + if [ ! -f ${ANDROID_PRODUCT_OUT}/${1}.img ]; then + exit_with_error "Partition image not found. Run 'make ${1}image' first." + fi + + echo "Copying ${1}..." + sudo umount /dev/${DEVICE}${2} + sudo dd if=${ANDROID_PRODUCT_OUT}/${1}.img of=/dev/${DEVICE}${2} bs=1M +} + +wipe_userdata() { + echo "Creating userdata..." + sudo umount /dev/${DEVICE}4 + sudo wipefs -a /dev/${DEVICE}4 + sudo mkfs.ext4 /dev/${DEVICE}4 -I 512 -L userdata +} + +finish() { + sync + echo "Done!" + exit 0 +} + +if [ -z ${TARGET_PRODUCT} ]; then + exit_with_error "TARGET_PRODUCT environment variable is not set. Run lunch first." +fi + +if [ -z ${ANDROID_PRODUCT_OUT} ]; then + exit_with_error "ANDROID_PRODUCT_OUT environment variable is not set. Run lunch first." +fi + +TARGET=$(echo ${TARGET_PRODUCT} | sed 's/^aosp_//') +DEVICE= + +if [ -z $1 ]; then + find_device + confirm ${TARGET} "boot, system, and vendor" + write_partition boot 1 + write_partition system 2 + write_partition vendor 3 + finish +elif [ ! -z $1 ] && [ $1 == "boot" ]; then + find_device + confirm ${TARGET} "boot" + write_partition boot 1 + finish +elif [ ! -z $1 ] && [ $1 == "system" ]; then + find_device + confirm ${TARGET} "system" + write_partition system 2 + finish +elif [ ! -z $1 ] && [ $1 == "vendor" ]; then + find_device + confirm ${TARGET} "vendor" + write_partition vendor 3 + finish +elif [ ! -z $1 ] && [ $1 == "wipe" ]; then + find_device + confirm ${TARGET} "wipe" + wipe_userdata + finish +else + exit_with_error "Usage: $0 [boot|system|vendor|wipe]" +fi