Merge "Drop <name>I</name> from mapper HAL."
diff --git a/audio/aidl/Android.bp b/audio/aidl/Android.bp
index e69306e..56ac510 100644
--- a/audio/aidl/Android.bp
+++ b/audio/aidl/Android.bp
@@ -109,7 +109,6 @@
"android.hardware.audio_defaults",
],
srcs: [
- "android/hardware/audio/core/AudioMode.aidl",
"android/hardware/audio/core/AudioPatch.aidl",
"android/hardware/audio/core/AudioRoute.aidl",
"android/hardware/audio/core/IBluetooth.aidl",
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetooth.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetooth.aidl
index 289c0c2..9357a15 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetooth.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IBluetooth.aidl
@@ -44,10 +44,10 @@
@nullable @utf8InCpp String debugName;
@VintfStability
enum Mode {
- UNSPECIFIED = 0,
- SCO = 1,
- SCO_WB = 2,
- SCO_SWB = 3,
+ UNSPECIFIED,
+ SCO,
+ SCO_WB,
+ SCO_SWB,
}
}
@JavaDerive(equals=true, toString=true) @VintfStability
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
index 4486b66..45217e7 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/IModule.aidl
@@ -59,7 +59,7 @@
boolean getMicMute();
void setMicMute(boolean mute);
android.hardware.audio.core.MicrophoneInfo[] getMicrophones();
- void updateAudioMode(android.hardware.audio.core.AudioMode mode);
+ void updateAudioMode(android.media.audio.common.AudioMode mode);
void updateScreenRotation(android.hardware.audio.core.IModule.ScreenRotation rotation);
void updateScreenState(boolean isTurnedOn);
@nullable android.hardware.audio.core.sounddose.ISoundDose getSoundDose();
@@ -68,6 +68,12 @@
void setVendorParameters(in android.hardware.audio.core.VendorParameter[] parameters, boolean async);
void addDeviceEffect(int portConfigId, in android.hardware.audio.effect.IEffect effect);
void removeDeviceEffect(int portConfigId, in android.hardware.audio.effect.IEffect effect);
+ android.media.audio.common.AudioMMapPolicyInfo[] getMmapPolicyInfos(android.media.audio.common.AudioMMapPolicyType mmapPolicyType);
+ boolean supportsVariableLatency();
+ int getAAudioMixerBurstCount();
+ int getAAudioHardwareBurstMinUsec();
+ const int DEFAULT_AAUDIO_MIXER_BURST_COUNT = 2;
+ const int DEFAULT_AAUDIO_HARDWARE_BURST_MIN_DURATION_US = 1000;
@VintfStability
parcelable OpenInputStreamArguments {
int portConfigId;
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
index 77063df..001d074 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/ITelephony.aidl
@@ -34,8 +34,8 @@
package android.hardware.audio.core;
@VintfStability
interface ITelephony {
- android.hardware.audio.core.AudioMode[] getSupportedAudioModes();
- void switchAudioMode(android.hardware.audio.core.AudioMode mode);
+ android.media.audio.common.AudioMode[] getSupportedAudioModes();
+ void switchAudioMode(android.media.audio.common.AudioMode mode);
android.hardware.audio.core.ITelephony.TelecomConfig setTelecomConfig(in android.hardware.audio.core.ITelephony.TelecomConfig config);
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable TelecomConfig {
diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
index eaa4bb1..3ba44a0 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl
@@ -35,7 +35,7 @@
@VintfStability
union Parameter {
android.hardware.audio.effect.Parameter.Common common;
- android.media.audio.common.AudioDeviceDescription deviceDescription;
+ android.media.audio.common.AudioDeviceDescription[] deviceDescription;
android.media.audio.common.AudioMode mode;
android.media.audio.common.AudioSource source;
android.hardware.audio.effect.Parameter.VolumeStereo volumeStereo;
diff --git a/audio/aidl/android/hardware/audio/core/AudioMode.aidl b/audio/aidl/android/hardware/audio/core/AudioMode.aidl
deleted file mode 100644
index 0943a55..0000000
--- a/audio/aidl/android/hardware/audio/core/AudioMode.aidl
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.audio.core;
-
-/**
- * The audio mode describes states of the audio system of the device that
- * can significantly affect the rules of audio routing, volume control, etc.
- * The audio mode is controlled by the framework, however the HAL has some
- * flexibility in the choice of modes to support, see 'IModule.updateAudioMode'.
- */
-@VintfStability
-@Backing(type="int")
-enum AudioMode {
- /** No active calls. */
- NORMAL = 0,
- /** The device is playing the ringtone. */
- RINGTONE = 1,
- /** The call is handled by the telephony stack ("voice call"). */
- IN_CALL = 2,
- /** The call is handled by an application ("VoIP call"). */
- IN_COMMUNICATION = 3,
- /** Call screening is in progress. */
- CALL_SCREEN = 4,
-}
diff --git a/audio/aidl/android/hardware/audio/core/IModule.aidl b/audio/aidl/android/hardware/audio/core/IModule.aidl
index 7bc1b9c..968b573 100644
--- a/audio/aidl/android/hardware/audio/core/IModule.aidl
+++ b/audio/aidl/android/hardware/audio/core/IModule.aidl
@@ -18,7 +18,6 @@
import android.hardware.audio.common.SinkMetadata;
import android.hardware.audio.common.SourceMetadata;
-import android.hardware.audio.core.AudioMode;
import android.hardware.audio.core.AudioPatch;
import android.hardware.audio.core.AudioRoute;
import android.hardware.audio.core.IBluetooth;
@@ -33,6 +32,9 @@
import android.hardware.audio.core.VendorParameter;
import android.hardware.audio.core.sounddose.ISoundDose;
import android.hardware.audio.effect.IEffect;
+import android.media.audio.common.AudioMMapPolicyInfo;
+import android.media.audio.common.AudioMMapPolicyType;
+import android.media.audio.common.AudioMode;
import android.media.audio.common.AudioOffloadInfo;
import android.media.audio.common.AudioPort;
import android.media.audio.common.AudioPortConfig;
@@ -684,6 +686,7 @@
* method.
*
* @param mode The current mode.
+ * @throws EX_ILLEGAL_ARGUMENT If the mode is out of range of valid values.
*/
void updateAudioMode(AudioMode mode);
@@ -806,4 +809,55 @@
* @throws EX_UNSUPPORTED_OPERATION If the module does not support device port effects.
*/
void removeDeviceEffect(int portConfigId, in IEffect effect);
+
+ /**
+ * Provide information describing how aaudio MMAP is supported per queried aaudio
+ * MMAP policy type.
+ *
+ * If there are no devices that support aaudio MMAP for the queried aaudio MMAP policy
+ * type in the HAL module, it must return an empty vector. Otherwise, return a vector
+ * describing how the devices support aaudio MMAP.
+ *
+ * @param mmapPolicyType the aaudio mmap policy type to query.
+ * @return The vector with mmap policy information.
+ */
+ AudioMMapPolicyInfo[] getMmapPolicyInfos(AudioMMapPolicyType mmapPolicyType);
+
+ /**
+ * Indicates if this module supports variable latency control for instance
+ * over Bluetooth A2DP or LE Audio links.
+ *
+ * If supported, all instances of IStreamOut interface returned by this module must
+ * implement getRecommendedLatencyModes() and setLatencyMode() APIs.
+ *
+ * @return Whether the module supports variable latency control.
+ */
+ boolean supportsVariableLatency();
+
+ /**
+ * Default value for number of bursts per aaudio mixer cycle. This is a suggested value
+ * to return for the HAL module, unless it is known that a better option exists.
+ */
+ const int DEFAULT_AAUDIO_MIXER_BURST_COUNT = 2;
+ /**
+ * Get the number of bursts per aaudio mixer cycle.
+ *
+ * @return The number of burst per aaudio mixer cycle.
+ * @throw EX_UNSUPPORTED_OPERATION If the module does not support aaudio MMAP.
+ */
+ int getAAudioMixerBurstCount();
+
+ /**
+ * Default value for minimum duration in microseconds for a MMAP hardware burst. This
+ * is a suggested value to return for the HAL module, unless it is known that a better
+ * option exists.
+ */
+ const int DEFAULT_AAUDIO_HARDWARE_BURST_MIN_DURATION_US = 1000;
+ /**
+ * Get the minimum duration in microseconds for a MMAP hardware burst.
+ *
+ * @return The minimum number of microseconds for a MMAP hardware burst.
+ * @throw EX_UNSUPPORTED_OPERATION If the module does not support aaudio MMAP.
+ */
+ int getAAudioHardwareBurstMinUsec();
}
diff --git a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
index b60b0fd..0e58add 100644
--- a/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
+++ b/audio/aidl/android/hardware/audio/core/IStreamOut.aidl
@@ -157,7 +157,8 @@
*
* Implementation for this method is mandatory only on specific spatial
* audio streams indicated by AUDIO_OUTPUT_FLAG_SPATIALIZER flag if they can
- * be routed to a BT classic sink.
+ * be routed to a BT sinks or if the implementation indicates support
+ * on all streams via IModule.supportsVariableLatency().
*
* @return Currently supported latency modes.
* @throws EX_ILLEGAL_STATE If the stream is closed.
@@ -172,7 +173,8 @@
*
* Implementation for this method is mandatory only on specific spatial
* audio streams indicated by AUDIO_OUTPUT_FLAG_SPATIALIZER flag if they can
- * be routed to a BT classic sink.
+ * be routed to a BT sinks or if the implementation indicates support
+ * on all streams via IModule.supportsVariableLatency().
*
* @throws EX_ILLEGAL_ARGUMENT If the specified mode is not supported.
* @throws EX_ILLEGAL_STATE If the stream is closed.
diff --git a/audio/aidl/android/hardware/audio/core/ITelephony.aidl b/audio/aidl/android/hardware/audio/core/ITelephony.aidl
index a817032..7fc1ace 100644
--- a/audio/aidl/android/hardware/audio/core/ITelephony.aidl
+++ b/audio/aidl/android/hardware/audio/core/ITelephony.aidl
@@ -16,7 +16,7 @@
package android.hardware.audio.core;
-import android.hardware.audio.core.AudioMode;
+import android.media.audio.common.AudioMode;
import android.media.audio.common.Boolean;
import android.media.audio.common.Float;
@@ -52,6 +52,7 @@
*
* @param mode The mode to switch to.
* @throws EX_UNSUPPORTED_OPERATION If the HAL does not support the specified mode.
+ * @throws EX_ILLEGAL_ARGUMENT If the mode is out of range of valid values.
* @throws EX_ILLEGAL_STATE If there was an error during switching.
*/
void switchAudioMode(AudioMode mode);
diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
index 1c001d2..473dfb5 100644
--- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl
+++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl
@@ -120,10 +120,11 @@
Common common;
/**
- * Used by audio framework to set the device type to effect engine.
- * Effect must implement setParameter(device) if Flags.deviceIndication set to true.
+ * Used by audio framework to set the device type(s) to effect engine.
+ * Effect engine must apply all AudioDeviceDescription in the list.
+ * Effect must implement setParameter(deviceDescription) if Flags.deviceIndication set to true.
*/
- AudioDeviceDescription deviceDescription;
+ AudioDeviceDescription[] deviceDescription;
/**
* Used by audio framework to set the audio mode to effect engine.
* Effect must implement setParameter(mode) if Flags.audioModeIndication set to true.
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 8f0c986..2aaa781 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -16,6 +16,8 @@
#pragma once
+#include <algorithm>
+#include <array>
#include <initializer_list>
#include <type_traits>
@@ -23,11 +25,22 @@
#include <aidl/android/media/audio/common/AudioDeviceType.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <aidl/android/media/audio/common/AudioInputFlags.h>
+#include <aidl/android/media/audio/common/AudioMode.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <aidl/android/media/audio/common/PcmType.h>
namespace android::hardware::audio::common {
+// Some values are reserved for use by the system code only.
+// HALs must not accept or emit values outside from the provided list.
+constexpr std::array<::aidl::android::media::audio::common::AudioMode, 5> kValidAudioModes = {
+ ::aidl::android::media::audio::common::AudioMode::NORMAL,
+ ::aidl::android::media::audio::common::AudioMode::RINGTONE,
+ ::aidl::android::media::audio::common::AudioMode::IN_CALL,
+ ::aidl::android::media::audio::common::AudioMode::IN_COMMUNICATION,
+ ::aidl::android::media::audio::common::AudioMode::CALL_SCREEN,
+};
+
constexpr size_t getPcmSampleSizeInBytes(::aidl::android::media::audio::common::PcmType pcm) {
using ::aidl::android::media::audio::common::PcmType;
switch (pcm) {
@@ -91,6 +104,11 @@
device == ::aidl::android::media::audio::common::AudioDeviceType::OUT_TELEPHONY_TX;
}
+constexpr bool isValidAudioMode(::aidl::android::media::audio::common::AudioMode mode) {
+ return std::find(kValidAudioModes.begin(), kValidAudioModes.end(), mode) !=
+ kValidAudioModes.end();
+}
+
// The helper functions defined below are only applicable to the case when an enum type
// specifies zero-based bit positions, not bit masks themselves. This is why instantiation
// is restricted to certain enum types.
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 248ba84..95043f7 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -164,6 +164,7 @@
"libloudnessenhanceraidl",
"libnssw",
"libpresetreverbsw",
+ "libreverbaidl",
"libtinyxml2",
"libvirtualizersw",
"libvisualizeraidl",
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index 3b40ae0..5cd87fd 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -165,7 +165,7 @@
return status;
}
-void Factory::openEffectLibrary(const AudioUuid& impl, const std::string& libName) {
+bool Factory::openEffectLibrary(const AudioUuid& impl, const std::string& libName) {
std::function<void(void*)> dlClose = [](void* handle) -> void {
if (handle && dlclose(handle)) {
LOG(ERROR) << "dlclose failed " << dlerror();
@@ -176,7 +176,7 @@
std::unique_ptr<void, decltype(dlClose)>{dlopen(libName.c_str(), RTLD_LAZY), dlClose};
if (!libHandle) {
LOG(ERROR) << __func__ << ": dlopen failed, err: " << dlerror();
- return;
+ return false;
}
LOG(INFO) << __func__ << " dlopen lib:" << libName << "\nimpl:" << impl.toString()
@@ -186,6 +186,7 @@
{impl,
std::make_tuple(std::move(libHandle),
std::unique_ptr<struct effect_dl_interface_s>(interface), libName)});
+ return true;
}
void Factory::createIdentityWithConfig(const EffectConfig::LibraryUuid& configLib,
@@ -201,8 +202,9 @@
LOG(DEBUG) << __func__ << ": typeUuid " << id.type.toString() << "\nimplUuid "
<< id.uuid.toString() << " proxyUuid "
<< (proxyUuid.has_value() ? proxyUuid->toString() : "null");
- openEffectLibrary(id.uuid, path->second);
- mIdentitySet.insert(std::move(id));
+ if (openEffectLibrary(id.uuid, path->second)) {
+ mIdentitySet.insert(std::move(id));
+ }
} else {
LOG(ERROR) << __func__ << ": library " << libName << " not exist!";
return;
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index acad70f..7e829e3 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -41,6 +41,10 @@
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioInputFlags;
using aidl::android::media::audio::common::AudioIoFlags;
+using aidl::android::media::audio::common::AudioMMapPolicy;
+using aidl::android::media::audio::common::AudioMMapPolicyInfo;
+using aidl::android::media::audio::common::AudioMMapPolicyType;
+using aidl::android::media::audio::common::AudioMode;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioOutputFlags;
using aidl::android::media::audio::common::AudioPort;
@@ -52,6 +56,7 @@
using aidl::android::media::audio::common::PcmType;
using android::hardware::audio::common::getFrameSizeInBytes;
using android::hardware::audio::common::isBitPositionFlagSet;
+using android::hardware::audio::common::isValidAudioMode;
namespace aidl::android::hardware::audio::core {
@@ -952,6 +957,10 @@
}
ndk::ScopedAStatus Module::updateAudioMode(AudioMode in_mode) {
+ if (!isValidAudioMode(in_mode)) {
+ LOG(ERROR) << __func__ << ": invalid mode " << toString(in_mode);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
// No checks for supported audio modes here, it's an informative notification.
LOG(DEBUG) << __func__ << ": " << toString(in_mode);
return ndk::ScopedAStatus::ok();
@@ -1074,4 +1083,109 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
+ndk::ScopedAStatus Module::getMmapPolicyInfos(AudioMMapPolicyType mmapPolicyType,
+ std::vector<AudioMMapPolicyInfo>* _aidl_return) {
+ LOG(DEBUG) << __func__ << ": mmap policy type " << toString(mmapPolicyType);
+ std::set<int32_t> mmapSinks;
+ std::set<int32_t> mmapSources;
+ auto& ports = getConfig().ports;
+ for (const auto& port : ports) {
+ if (port.flags.getTag() == AudioIoFlags::Tag::input &&
+ isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::input>(),
+ AudioInputFlags::MMAP_NOIRQ)) {
+ mmapSinks.insert(port.id);
+ } else if (port.flags.getTag() == AudioIoFlags::Tag::output &&
+ isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::MMAP_NOIRQ)) {
+ mmapSources.insert(port.id);
+ }
+ }
+ for (const auto& route : getConfig().routes) {
+ if (mmapSinks.count(route.sinkPortId) != 0) {
+ // The sink is a mix port, add the sources if they are device ports.
+ for (int sourcePortId : route.sourcePortIds) {
+ auto sourcePortIt = findById<AudioPort>(ports, sourcePortId);
+ if (sourcePortIt == ports.end()) {
+ // This must not happen
+ LOG(ERROR) << __func__ << ": port id " << sourcePortId << " cannot be found";
+ continue;
+ }
+ if (sourcePortIt->ext.getTag() != AudioPortExt::Tag::device) {
+ // The source is not a device port, skip
+ continue;
+ }
+ AudioMMapPolicyInfo policyInfo;
+ policyInfo.device = sourcePortIt->ext.get<AudioPortExt::Tag::device>().device;
+ // Always return AudioMMapPolicy.AUTO if the device supports mmap for
+ // default implementation.
+ policyInfo.mmapPolicy = AudioMMapPolicy::AUTO;
+ _aidl_return->push_back(policyInfo);
+ }
+ } else {
+ auto sinkPortIt = findById<AudioPort>(ports, route.sinkPortId);
+ if (sinkPortIt == ports.end()) {
+ // This must not happen
+ LOG(ERROR) << __func__ << ": port id " << route.sinkPortId << " cannot be found";
+ continue;
+ }
+ if (sinkPortIt->ext.getTag() != AudioPortExt::Tag::device) {
+ // The sink is not a device port, skip
+ continue;
+ }
+ if (count_any(mmapSources, route.sourcePortIds)) {
+ AudioMMapPolicyInfo policyInfo;
+ policyInfo.device = sinkPortIt->ext.get<AudioPortExt::Tag::device>().device;
+ // Always return AudioMMapPolicy.AUTO if the device supports mmap for
+ // default implementation.
+ policyInfo.mmapPolicy = AudioMMapPolicy::AUTO;
+ _aidl_return->push_back(policyInfo);
+ }
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::supportsVariableLatency(bool* _aidl_return) {
+ LOG(DEBUG) << __func__;
+ *_aidl_return = false;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::getAAudioMixerBurstCount(int32_t* _aidl_return) {
+ if (!isMmapSupported()) {
+ LOG(DEBUG) << __func__ << ": mmap is not supported ";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ *_aidl_return = DEFAULT_AAUDIO_MIXER_BURST_COUNT;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Module::getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) {
+ if (!isMmapSupported()) {
+ LOG(DEBUG) << __func__ << ": mmap is not supported ";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ *_aidl_return = DEFAULT_AAUDIO_HARDWARE_BURST_MIN_DURATION_US;
+ LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
+ return ndk::ScopedAStatus::ok();
+}
+
+bool Module::isMmapSupported() {
+ if (mIsMmapSupported.has_value()) {
+ return mIsMmapSupported.value();
+ }
+ std::vector<AudioMMapPolicyInfo> mmapPolicyInfos;
+ if (!getMmapPolicyInfos(AudioMMapPolicyType::DEFAULT, &mmapPolicyInfos).isOk()) {
+ mIsMmapSupported = false;
+ } else {
+ mIsMmapSupported =
+ std::find_if(mmapPolicyInfos.begin(), mmapPolicyInfos.end(), [](const auto& info) {
+ return info.mmapPolicy == AudioMMapPolicy::AUTO ||
+ info.mmapPolicy == AudioMMapPolicy::ALWAYS;
+ }) != mmapPolicyInfos.end();
+ }
+ return mIsMmapSupported.value();
+}
+
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/Telephony.cpp b/audio/aidl/default/Telephony.cpp
index d873178..ad22470 100644
--- a/audio/aidl/default/Telephony.cpp
+++ b/audio/aidl/default/Telephony.cpp
@@ -14,14 +14,18 @@
* limitations under the License.
*/
-#include <android/binder_to_string.h>
#define LOG_TAG "AHAL_Telephony"
#include <android-base/logging.h>
+#include <Utils.h>
+#include <android/binder_to_string.h>
+
#include "core-impl/Telephony.h"
+using aidl::android::media::audio::common::AudioMode;
using aidl::android::media::audio::common::Boolean;
using aidl::android::media::audio::common::Float;
+using android::hardware::audio::common::isValidAudioMode;
namespace aidl::android::hardware::audio::core {
@@ -38,6 +42,10 @@
}
ndk::ScopedAStatus Telephony::switchAudioMode(AudioMode in_mode) {
+ if (!isValidAudioMode(in_mode)) {
+ LOG(ERROR) << __func__ << ": invalid mode " << toString(in_mode);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
if (std::find(mSupportedAudioModes.begin(), mSupportedAudioModes.end(), in_mode) !=
mSupportedAudioModes.end()) {
LOG(DEBUG) << __func__ << ": " << toString(in_mode);
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
index e460f89..6714a7e 100644
--- a/audio/aidl/default/audio_effects_config.xml
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -40,6 +40,7 @@
<library name="nssw" path="libnssw.so"/>
<library name="env_reverbsw" path="libenvreverbsw.so"/>
<library name="preset_reverbsw" path="libpresetreverbsw.so"/>
+ <library name="reverb" path="libreverbaidl.so"/>
<library name="virtualizersw" path="libvirtualizersw.so"/>
<library name="visualizer" path="libvisualizeraidl.so"/>
<library name="volumesw" path="libvolumesw.so"/>
@@ -78,6 +79,10 @@
<effect name="env_reverb" library="env_reverbsw" uuid="fa819886-588b-11ed-9b6a-0242ac120002"/>
<effect name="noise_suppression" library="nssw" uuid="c06c8400-8e06-11e0-9cb6-0002a5d5c51b"/>
<effect name="preset_reverb" library="preset_reverbsw" uuid="fa8199c6-588b-11ed-9b6a-0242ac120002"/>
+ <effect name="reverb_env_aux" library="reverb" uuid="4a387fc0-8ab3-11df-8bad-0002a5d5c51b"/>
+ <effect name="reverb_env_ins" library="reverb" uuid="c7a511a0-a3bb-11df-860e-0002a5d5c51b"/>
+ <effect name="reverb_pre_aux" library="reverb" uuid="f29a1400-a3bb-11df-8ddc-0002a5d5c51b"/>
+ <effect name="reverb_pre_ins" library="reverb" uuid="172cdf00-a3bc-11df-a72f-0002a5d5c51b"/>
<effectProxy name="virtualizer" uuid="d3467faa-acc7-4d34-acaf-0002a5d5c51b">
<libsw library="virtualizersw" uuid="fa819d86-588b-11ed-9b6a-0242ac120002"/>
<libsw library="bundle" uuid="1d4033c0-8557-11df-9f2d-0002a5d5c51b"/>
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 9e4499b..acdbe4f 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -92,7 +92,7 @@
ndk::ScopedAStatus setMicMute(bool in_mute) override;
ndk::ScopedAStatus getMicrophones(std::vector<MicrophoneInfo>* _aidl_return) override;
ndk::ScopedAStatus updateAudioMode(
- ::aidl::android::hardware::audio::core::AudioMode in_mode) override;
+ ::aidl::android::media::audio::common::AudioMode in_mode) override;
ndk::ScopedAStatus updateScreenRotation(
::aidl::android::hardware::audio::core::IModule::ScreenRotation in_rotation) override;
ndk::ScopedAStatus updateScreenState(bool in_isTurnedOn) override;
@@ -110,6 +110,13 @@
int32_t in_portConfigId,
const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
override;
+ ndk::ScopedAStatus getMmapPolicyInfos(
+ ::aidl::android::media::audio::common::AudioMMapPolicyType mmapPolicyType,
+ std::vector<::aidl::android::media::audio::common::AudioMMapPolicyInfo>* _aidl_return)
+ override;
+ ndk::ScopedAStatus supportsVariableLatency(bool* _aidl_return) override;
+ ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
+ ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
void cleanUpPatch(int32_t patchId);
ndk::ScopedAStatus createStreamContext(
@@ -127,6 +134,7 @@
std::set<int32_t> portIdsFromPortConfigIds(C portConfigIds);
void registerPatch(const AudioPatch& patch);
void updateStreamsConnectedState(const AudioPatch& oldPatch, const AudioPatch& newPatch);
+ bool isMmapSupported();
// This value is used for all AudioPatches.
static constexpr int32_t kMinimumStreamBufferSizeFrames = 16;
@@ -154,6 +162,7 @@
bool mMicMute = false;
std::shared_ptr<sounddose::ISoundDose> mSoundDose;
ndk::SpAIBinder mSoundDoseBinder;
+ std::optional<bool> mIsMmapSupported;
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Telephony.h b/audio/aidl/default/include/core-impl/Telephony.h
index 0936172..0f8e93f 100644
--- a/audio/aidl/default/include/core-impl/Telephony.h
+++ b/audio/aidl/default/include/core-impl/Telephony.h
@@ -27,13 +27,20 @@
Telephony();
private:
- ndk::ScopedAStatus getSupportedAudioModes(std::vector<AudioMode>* _aidl_return) override;
- ndk::ScopedAStatus switchAudioMode(AudioMode in_mode) override;
+ ndk::ScopedAStatus getSupportedAudioModes(
+ std::vector<::aidl::android::media::audio::common::AudioMode>* _aidl_return) override;
+ ndk::ScopedAStatus switchAudioMode(
+ ::aidl::android::media::audio::common::AudioMode in_mode) override;
ndk::ScopedAStatus setTelecomConfig(const TelecomConfig& in_config,
TelecomConfig* _aidl_return) override;
- const std::vector<AudioMode> mSupportedAudioModes = {::ndk::enum_range<AudioMode>().begin(),
- ::ndk::enum_range<AudioMode>().end()};
+ const std::vector<::aidl::android::media::audio::common::AudioMode> mSupportedAudioModes = {
+ ::aidl::android::media::audio::common::AudioMode::NORMAL,
+ ::aidl::android::media::audio::common::AudioMode::RINGTONE,
+ ::aidl::android::media::audio::common::AudioMode::IN_CALL,
+ ::aidl::android::media::audio::common::AudioMode::IN_COMMUNICATION,
+ // Omit CALL_SCREEN for a better VTS coverage.
+ };
TelecomConfig mTelecomConfig;
};
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
index a3e7ff2..7bbf19e 100644
--- a/audio/aidl/default/include/effect-impl/EffectContext.h
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -91,11 +91,13 @@
int getSessionId() { return mSessionId; }
virtual RetCode setOutputDevice(
- const aidl::android::media::audio::common::AudioDeviceDescription& device) {
+ const std::vector<aidl::android::media::audio::common::AudioDeviceDescription>&
+ device) {
mOutputDevice = device;
return RetCode::SUCCESS;
}
- virtual aidl::android::media::audio::common::AudioDeviceDescription getOutputDevice() {
+ virtual std::vector<aidl::android::media::audio::common::AudioDeviceDescription>
+ getOutputDevice() {
return mOutputDevice;
}
@@ -133,7 +135,7 @@
size_t mInputFrameSize;
size_t mOutputFrameSize;
Parameter::Common mCommon;
- aidl::android::media::audio::common::AudioDeviceDescription mOutputDevice;
+ std::vector<aidl::android::media::audio::common::AudioDeviceDescription> mOutputDevice;
aidl::android::media::audio::common::AudioMode mMode;
aidl::android::media::audio::common::AudioSource mSource;
Parameter::VolumeStereo mVolumeStereo;
diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h
index 1a60829..7703091 100644
--- a/audio/aidl/default/include/effect-impl/EffectUUID.h
+++ b/audio/aidl/default/include/effect-impl/EffectUUID.h
@@ -189,6 +189,18 @@
0x11ed,
0x9b6a,
{0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// 4a387fc0-8ab3-11df-8bad-0002a5d5c51b
+static const AudioUuid kAuxEnvReverbImplUUID = {static_cast<int32_t>(0x4a387fc0),
+ 0x8ab3,
+ 0x11df,
+ 0x8bad,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+// c7a511a0-a3bb-11df-860e-0002a5d5c51b
+static const AudioUuid kInsertEnvReverbImplUUID = {static_cast<int32_t>(0xc7a511a0),
+ 0xa3bb,
+ 0x11df,
+ 0x860e,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
// 58b4b260-8e06-11e0-aa8e-0002a5d5c51b
static const AudioUuid kNoiseSuppressionTypeUUID = {static_cast<int32_t>(0x58b4b260),
0x8e06,
@@ -213,6 +225,18 @@
0x11ed,
0x9b6a,
{0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+// f29a1400-a3bb-11df-8ddc-0002a5d5c51b
+static const AudioUuid kAuxPresetReverbImplUUID = {static_cast<int32_t>(0xf29a1400),
+ 0xa3bb,
+ 0x11df,
+ 0x8ddc,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+// 172cdf00-a3bc-11df-a72f-0002a5d5c51b
+static const AudioUuid kInsertPresetReverbImplUUID = {static_cast<int32_t>(0x172cdf00),
+ 0xa3bc,
+ 0x11df,
+ 0xa72f,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
// 37cc2c00-dddd-11db-8577-0002a5d5c51b
static const AudioUuid kVirtualizerTypeUUID = {static_cast<int32_t>(0x37cc2c00),
0xdddd,
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
index 04bd1bb..b32ec56 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -101,7 +101,7 @@
ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
void cleanupEffectMap();
- void openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
+ bool openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
const std::string& libName);
void createIdentityWithConfig(
const EffectConfig::LibraryUuid& configLib,
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 7e4b148..7b002ad 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -18,6 +18,7 @@
#include <chrono>
#include <Utils.h>
+#include <aidl/android/media/audio/common/AudioInputFlags.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
@@ -32,6 +33,7 @@
using aidl::android::media::audio::common::AudioEncapsulationMode;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::AudioInputFlags;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioOutputFlags;
@@ -162,6 +164,20 @@
});
}
+std::vector<AudioPort> ModuleConfig::getMmapOutMixPorts(bool attachedOnly, bool singlePort) const {
+ return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+ return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::MMAP_NOIRQ);
+ });
+}
+
+std::vector<AudioPort> ModuleConfig::getMmapInMixPorts(bool attachedOnly, bool singlePort) const {
+ return findMixPorts(true /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) {
+ return isBitPositionFlagSet(port.flags.get<AudioIoFlags::Tag::input>(),
+ AudioInputFlags::MMAP_NOIRQ);
+ });
+}
+
std::vector<AudioPort> ModuleConfig::getAttachedDevicesPortsForMixPort(
bool isInput, const AudioPortConfig& mixPortConfig) const {
const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
@@ -422,3 +438,11 @@
}
return result;
}
+
+bool ModuleConfig::isMmapSupported() const {
+ const std::vector<AudioPort> mmapOutMixPorts =
+ getMmapOutMixPorts(false /*attachedOnly*/, false /*singlePort*/);
+ const std::vector<AudioPort> mmapInMixPorts =
+ getMmapInMixPorts(false /*attachedOnly*/, false /*singlePort*/);
+ return !mmapOutMixPorts.empty() || !mmapInMixPorts.empty();
+}
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index 7247f3b..6a22075 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -63,6 +63,10 @@
bool attachedOnly, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getPrimaryMixPorts(
bool attachedOnly, bool singlePort) const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getMmapOutMixPorts(
+ bool attachedOnly, bool singlePort) const;
+ std::vector<aidl::android::media::audio::common::AudioPort> getMmapInMixPorts(
+ bool attachedOnly, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicesPortsForMixPort(
bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
@@ -135,6 +139,8 @@
return *config.begin();
}
+ bool isMmapSupported() const;
+
std::string toString() const;
private:
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index d4f2811..3ca51c7 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -40,6 +40,8 @@
#include <aidl/android/hardware/audio/core/ITelephony.h>
#include <aidl/android/hardware/audio/core/sounddose/ISoundDose.h>
#include <aidl/android/media/audio/common/AudioIoFlags.h>
+#include <aidl/android/media/audio/common/AudioMMapPolicyInfo.h>
+#include <aidl/android/media/audio/common/AudioMMapPolicyType.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <android-base/chrono_utils.h>
#include <android/binder_enums.h>
@@ -54,7 +56,6 @@
using aidl::android::hardware::audio::common::RecordTrackMetadata;
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
-using aidl::android::hardware::audio::core::AudioMode;
using aidl::android::hardware::audio::core::AudioPatch;
using aidl::android::hardware::audio::core::AudioRoute;
using aidl::android::hardware::audio::core::IBluetooth;
@@ -78,6 +79,9 @@
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioLatencyMode;
+using aidl::android::media::audio::common::AudioMMapPolicyInfo;
+using aidl::android::media::audio::common::AudioMMapPolicyType;
+using aidl::android::media::audio::common::AudioMode;
using aidl::android::media::audio::common::AudioOutputFlags;
using aidl::android::media::audio::common::AudioPlaybackRate;
using aidl::android::media::audio::common::AudioPort;
@@ -93,6 +97,7 @@
using android::hardware::audio::common::getChannelCount;
using android::hardware::audio::common::isBitPositionFlagSet;
using android::hardware::audio::common::isTelephonyDeviceType;
+using android::hardware::audio::common::isValidAudioMode;
using android::hardware::audio::common::StreamLogic;
using android::hardware::audio::common::StreamWorker;
using ndk::enum_range;
@@ -1805,7 +1810,11 @@
TEST_P(AudioCoreModule, UpdateAudioMode) {
for (const auto mode : ::ndk::enum_range<AudioMode>()) {
- EXPECT_IS_OK(module->updateAudioMode(mode)) << toString(mode);
+ if (isValidAudioMode(mode)) {
+ EXPECT_IS_OK(module->updateAudioMode(mode)) << toString(mode);
+ } else {
+ EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->updateAudioMode(mode)) << toString(mode);
+ }
}
EXPECT_IS_OK(module->updateAudioMode(AudioMode::NORMAL));
}
@@ -1882,6 +1891,51 @@
}
}
+TEST_P(AudioCoreModule, GetMmapPolicyInfos) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ const bool isMmapSupported = moduleConfig->isMmapSupported();
+ for (const auto mmapPolicyType :
+ {AudioMMapPolicyType::DEFAULT, AudioMMapPolicyType::EXCLUSIVE}) {
+ std::vector<AudioMMapPolicyInfo> policyInfos;
+ EXPECT_IS_OK(module->getMmapPolicyInfos(mmapPolicyType, &policyInfos))
+ << toString(mmapPolicyType);
+ EXPECT_EQ(isMmapSupported, !policyInfos.empty());
+ }
+}
+
+TEST_P(AudioCoreModule, BluetoothVariableLatency) {
+ bool isSupported = false;
+ EXPECT_IS_OK(module->supportsVariableLatency(&isSupported));
+ LOG(INFO) << "supportsVariableLatency: " << isSupported;
+}
+
+TEST_P(AudioCoreModule, GetAAudioMixerBurstCount) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ const bool isMmapSupported = moduleConfig->isMmapSupported();
+ int32_t mixerBursts = 0;
+ ndk::ScopedAStatus status = module->getAAudioMixerBurstCount(&mixerBursts);
+ EXPECT_EQ(isMmapSupported, status.getExceptionCode() != EX_UNSUPPORTED_OPERATION)
+ << "Support for AAudio MMAP and getting AAudio mixer burst count must be consistent";
+ if (!isMmapSupported) {
+ GTEST_SKIP() << "AAudio MMAP is not supported";
+ }
+ EXPECT_GE(mixerBursts, 0);
+}
+
+TEST_P(AudioCoreModule, GetAAudioHardwareBurstMinUsec) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ const bool isMmapSupported = moduleConfig->isMmapSupported();
+ int32_t aaudioHardwareBurstMinUsec = 0;
+ ndk::ScopedAStatus status = module->getAAudioHardwareBurstMinUsec(&aaudioHardwareBurstMinUsec);
+ EXPECT_EQ(isMmapSupported, status.getExceptionCode() != EX_UNSUPPORTED_OPERATION)
+ << "Support for AAudio MMAP and getting AAudio hardware burst minimum usec "
+ << "must be consistent";
+ if (!isMmapSupported) {
+ GTEST_SKIP() << "AAudio MMAP is not supported";
+ }
+ EXPECT_GE(aaudioHardwareBurstMinUsec, 0);
+}
+
class AudioCoreBluetooth : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
public:
void SetUp() override {
@@ -1994,6 +2048,9 @@
}
std::vector<AudioMode> modes1;
ASSERT_IS_OK(telephony->getSupportedAudioModes(&modes1));
+ for (const auto mode : modes1) {
+ EXPECT_TRUE(isValidAudioMode(mode)) << toString(mode);
+ }
const std::vector<AudioMode> kMandatoryModes = {AudioMode::NORMAL, AudioMode::RINGTONE,
AudioMode::IN_CALL,
AudioMode::IN_COMMUNICATION};
@@ -2025,7 +2082,9 @@
unsupportedModes.erase(mode);
}
for (const auto mode : unsupportedModes) {
- EXPECT_STATUS(EX_UNSUPPORTED_OPERATION, telephony->switchAudioMode(mode)) << toString(mode);
+ EXPECT_STATUS(isValidAudioMode(mode) ? EX_UNSUPPORTED_OPERATION : EX_ILLEGAL_ARGUMENT,
+ telephony->switchAudioMode(mode))
+ << toString(mode);
}
}
@@ -2314,7 +2373,7 @@
void HwGainHwVolume() {
const auto ports =
- moduleConfig->getMixPorts(IOTraits<Stream>::is_input, false /*attachedOnly*/);
+ moduleConfig->getMixPorts(IOTraits<Stream>::is_input, true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No mix ports";
}
@@ -2353,7 +2412,7 @@
// it as an invalid argument, or say that offloaded effects are not supported.
void AddRemoveEffectInvalidArguments() {
const auto ports =
- moduleConfig->getMixPorts(IOTraits<Stream>::is_input, false /*attachedOnly*/);
+ moduleConfig->getMixPorts(IOTraits<Stream>::is_input, true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No mix ports";
}
@@ -2634,7 +2693,7 @@
}
TEST_P(AudioStreamOut, AudioDescriptionMixLevel) {
- const auto ports = moduleConfig->getOutputMixPorts(false /*attachedOnly*/);
+ const auto ports = moduleConfig->getOutputMixPorts(true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No output mix ports";
}
@@ -2662,7 +2721,7 @@
}
TEST_P(AudioStreamOut, DualMonoMode) {
- const auto ports = moduleConfig->getOutputMixPorts(false /*attachedOnly*/);
+ const auto ports = moduleConfig->getOutputMixPorts(true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No output mix ports";
}
@@ -2686,7 +2745,7 @@
}
TEST_P(AudioStreamOut, LatencyMode) {
- const auto ports = moduleConfig->getOutputMixPorts(false /*attachedOnly*/);
+ const auto ports = moduleConfig->getOutputMixPorts(true /*attachedOnly*/);
if (ports.empty()) {
GTEST_SKIP() << "No output mix ports";
}
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 8938618..c5a0943 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -47,6 +47,10 @@
using aidl::android::hardware::audio::effect::IFactory;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::State;
+using aidl::android::media::audio::common::AudioDeviceDescription;
+using aidl::android::media::audio::common::AudioDeviceType;
+using aidl::android::media::audio::common::AudioMode;
+using aidl::android::media::audio::common::AudioSource;
enum ParamName { PARAM_INSTANCE_NAME };
using EffectTestParam = std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>>;
@@ -73,6 +77,13 @@
std::shared_ptr<IFactory> mFactory;
std::shared_ptr<IEffect> mEffect;
Descriptor mDescriptor;
+
+ void setAndGetParameter(Parameter::Id id, const Parameter& set) {
+ Parameter get;
+ EXPECT_IS_OK(mEffect->setParameter(set));
+ EXPECT_IS_OK(mEffect->getParameter(id, &get));
+ EXPECT_EQ(set, get) << set.toString() << " vs " << get.toString();
+ }
};
TEST_P(AudioEffectTest, SetupAndTearDown) {
@@ -392,14 +403,8 @@
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */);
- Parameter get = Parameter(), set = Parameter();
- set.set<Parameter::common>(common);
- EXPECT_IS_OK(mEffect->setParameter(set));
-
- Parameter::Id id;
- id.set<Parameter::Id::commonTag>(Parameter::common);
- EXPECT_IS_OK(mEffect->getParameter(id, &get));
- EXPECT_EQ(set, get) << set.toString() << " vs " << get.toString();
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::common);
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(id, Parameter::make<Parameter::common>(common)));
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
@@ -414,14 +419,8 @@
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */);
- Parameter get = Parameter(), set = Parameter();
- set.set<Parameter::common>(common);
- EXPECT_IS_OK(mEffect->setParameter(set));
-
- Parameter::Id id;
- id.set<Parameter::Id::commonTag>(Parameter::common);
- EXPECT_IS_OK(mEffect->getParameter(id, &get));
- EXPECT_EQ(set, get) << set.toString() << " vs " << get.toString();
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::common);
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(id, Parameter::make<Parameter::common>(common)));
ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
ASSERT_NO_FATAL_FAILURE(close(mEffect));
@@ -439,14 +438,8 @@
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */);
- Parameter get = Parameter(), set = Parameter();
- set.set<Parameter::common>(common);
- EXPECT_IS_OK(mEffect->setParameter(set));
-
- Parameter::Id id;
- id.set<Parameter::Id::commonTag>(Parameter::common);
- EXPECT_IS_OK(mEffect->getParameter(id, &get));
- EXPECT_EQ(set, get) << set.toString() << " vs " << get.toString();
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::common);
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(id, Parameter::make<Parameter::common>(common)));
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
@@ -461,18 +454,11 @@
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */);
- Parameter get = Parameter(), set = Parameter();
- set.set<Parameter::common>(common);
- EXPECT_IS_OK(mEffect->setParameter(set));
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::common);
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(id, Parameter::make<Parameter::common>(common)));
ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
-
- Parameter::Id id;
- id.set<Parameter::Id::commonTag>(Parameter::common);
- EXPECT_IS_OK(mEffect->getParameter(id, &get));
- EXPECT_EQ(set, get) << set.toString() << " vs " << get.toString();
-
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
}
@@ -487,21 +473,102 @@
Parameter::Common common = EffectHelper::createParamCommon(
0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */);
- Parameter get = Parameter(), set = Parameter();
- set.set<Parameter::common>(common);
- EXPECT_IS_OK(mEffect->setParameter(set));
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::common);
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(id, Parameter::make<Parameter::common>(common)));
ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET));
ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
- Parameter::Id id;
- id.set<Parameter::Id::commonTag>(Parameter::common);
- EXPECT_IS_OK(mEffect->getParameter(id, &get));
- EXPECT_EQ(set, get) << set.toString() << " vs " << get.toString();
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(id, Parameter::make<Parameter::common>(common)));
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
ASSERT_NO_FATAL_FAILURE(close(mEffect));
ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
}
+
+// Set and get AudioDeviceDescription in Parameter
+TEST_P(AudioEffectTest, SetAndGetParameterDeviceDescription) {
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ ASSERT_NO_FATAL_FAILURE(open(mEffect));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
+
+ std::vector<AudioDeviceDescription> deviceDescs = {
+ {.type = AudioDeviceType::IN_DEFAULT,
+ .connection = AudioDeviceDescription::CONNECTION_ANALOG},
+ {.type = AudioDeviceType::IN_DEVICE,
+ .connection = AudioDeviceDescription::CONNECTION_BT_A2DP}};
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::deviceDescription);
+ ASSERT_NO_FATAL_FAILURE(
+ setAndGetParameter(id, Parameter::make<Parameter::deviceDescription>(deviceDescs)));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+}
+
+// Set and get AudioMode in Parameter
+TEST_P(AudioEffectTest, SetAndGetParameterAudioMode) {
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ ASSERT_NO_FATAL_FAILURE(open(mEffect));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
+
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::mode);
+ ASSERT_NO_FATAL_FAILURE(
+ setAndGetParameter(id, Parameter::make<Parameter::mode>(AudioMode::NORMAL)));
+ ASSERT_NO_FATAL_FAILURE(
+ setAndGetParameter(id, Parameter::make<Parameter::mode>(AudioMode::IN_COMMUNICATION)));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+}
+
+// Set and get AudioSource in Parameter
+TEST_P(AudioEffectTest, SetAndGetParameterAudioSource) {
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ ASSERT_NO_FATAL_FAILURE(open(mEffect));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
+
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::source);
+ ASSERT_NO_FATAL_FAILURE(
+ setAndGetParameter(id, Parameter::make<Parameter::source>(AudioSource::DEFAULT)));
+ ASSERT_NO_FATAL_FAILURE(setAndGetParameter(
+ id, Parameter::make<Parameter::source>(AudioSource::VOICE_RECOGNITION)));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+}
+
+// Set and get VolumeStereo in Parameter
+TEST_P(AudioEffectTest, SetAndGetParameterVolume) {
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ ASSERT_NO_FATAL_FAILURE(open(mEffect));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
+
+ Parameter::Id id = Parameter::Id::make<Parameter::Id::commonTag>(Parameter::volumeStereo);
+ Parameter::VolumeStereo volume = {.left = 10.0, .right = 10.0};
+ ASSERT_NO_FATAL_FAILURE(
+ setAndGetParameter(id, Parameter::make<Parameter::volumeStereo>(volume)));
+
+ ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::STOP));
+ ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+}
+
/// Data processing test
// Send data to effects and expect it to be consumed by checking statusMQ.
TEST_P(AudioEffectTest, ConsumeDataInProcessingState) {
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index 87e1ab7..5aecd32 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -240,16 +240,6 @@
};
bool ProcessThread::threadLoop() {
- // For a spatializer effect, we perform scheduler adjustments to reduce glitches and power.
- {
- effect_descriptor_t halDescriptor{};
- if ((*mEffect)->get_descriptor(mEffect, &halDescriptor) == NO_ERROR &&
- memcmp(&halDescriptor.type, FX_IID_SPATIALIZER, sizeof(effect_uuid_t)) == 0) {
- const status_t status = scheduler::updateSpatializerPriority(gettid());
- ALOGW_IF(status != OK, "Failed to update Spatializer priority");
- }
- }
-
// This implementation doesn't return control back to the Thread until it decides to stop,
// as the Thread uses mutexes, and this can lead to priority inversion.
while (!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
@@ -570,6 +560,15 @@
return Void();
}
+ // For a spatializer effect, we perform scheduler adjustments to reduce glitches and power.
+ // We do it here instead of the ProcessThread::threadLoop to ensure that mHandle is valid.
+ if (effect_descriptor_t halDescriptor{};
+ (*mHandle)->get_descriptor(mHandle, &halDescriptor) == NO_ERROR &&
+ memcmp(&halDescriptor.type, FX_IID_SPATIALIZER, sizeof(effect_uuid_t)) == 0) {
+ const status_t status = scheduler::updateSpatializerPriority(mProcessThread->getTid());
+ ALOGW_IF(status != OK, "Failed to update Spatializer priority");
+ }
+
mStatusMQ = std::move(tempStatusMQ);
_hidl_cb(Result::OK, *mStatusMQ->getDesc());
return Void();
diff --git a/bluetooth/aidl/TEST_MAPPING b/bluetooth/aidl/TEST_MAPPING
new file mode 100644
index 0000000..342a1e4
--- /dev/null
+++ b/bluetooth/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit" : [
+ {
+ "name" : "VtsHalBluetoothTargetTest"
+ }
+ ]
+}
diff --git a/bluetooth/aidl/default/Android.bp b/bluetooth/aidl/default/Android.bp
index d1761f5..3f4ba99 100644
--- a/bluetooth/aidl/default/Android.bp
+++ b/bluetooth/aidl/default/Android.bp
@@ -30,6 +30,7 @@
defaults: ["android.hardware.bluetooth-service-build-defaults"],
srcs: [
"BluetoothHci.cpp",
+ "net_bluetooth_mgmt.cpp",
],
}
@@ -37,7 +38,7 @@
name: "android.hardware.bluetooth-service.default",
relative_install_path: "hw",
init_rc: ["bluetooth-service-default.rc"],
- vintf_fragments: ["bluetooth-service-default.xml"],
+ vintf_fragments: [":manifest_android.hardware.bluetooth-service.default.xml"],
vendor: true,
defaults: ["android.hardware.bluetooth-service-build-defaults"],
srcs: [
@@ -77,3 +78,8 @@
],
},
}
+
+filegroup {
+ name: "manifest_android.hardware.bluetooth-service.default.xml",
+ srcs: ["bluetooth-service-default.xml"],
+}
diff --git a/bluetooth/aidl/default/BluetoothHci.cpp b/bluetooth/aidl/default/BluetoothHci.cpp
index 4d4896d..eebbbc0 100644
--- a/bluetooth/aidl/default/BluetoothHci.cpp
+++ b/bluetooth/aidl/default/BluetoothHci.cpp
@@ -44,6 +44,7 @@
using namespace ::android::hardware::bluetooth::hci;
using namespace ::android::hardware::bluetooth::async;
+using aidl::android::hardware::bluetooth::Status;
namespace aidl::android::hardware::bluetooth::impl {
@@ -97,21 +98,25 @@
mDeathRecipient = std::make_shared<BluetoothDeathRecipient>(this);
}
-ndk::ScopedAStatus BluetoothHci::initialize(
- const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
- ALOGI(__func__);
-
- mFd = open(mDevPath.c_str(), O_RDWR);
- if (mFd < 0) {
+int BluetoothHci::getFdFromDevPath() {
+ int fd = open(mDevPath.c_str(), O_RDWR);
+ if (fd < 0) {
ALOGE("Could not connect to bt: %s (%s)", mDevPath.c_str(),
strerror(errno));
- return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
+ return fd;
}
if (int ret = SetTerminalRaw(mFd) < 0) {
ALOGE("Could not make %s a raw terminal %d(%s)", mDevPath.c_str(), ret,
strerror(errno));
- return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
+ ::close(fd);
+ return -1;
}
+ return fd;
+}
+
+ndk::ScopedAStatus BluetoothHci::initialize(
+ const std::shared_ptr<IBluetoothHciCallbacks>& cb) {
+ ALOGI(__func__);
mCb = cb;
if (mCb == nullptr) {
@@ -119,32 +124,36 @@
return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
}
+ management_.reset(new NetBluetoothMgmt);
+ mFd = management_->openHci();
+ if (mFd < 0) {
+ management_.reset();
+
+ ALOGI("Unable to open Linux interface, trying default path.");
+ mFd = getFdFromDevPath();
+ if (mFd < 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(STATUS_BAD_VALUE);
+ }
+ }
+
mDeathRecipient->LinkToDeath(mCb);
- auto init_ret = cb->initializationComplete(Status::SUCCESS);
- if (!init_ret.isOk()) {
- if (!mDeathRecipient->getHasDied()) {
- ALOGE("Error sending init callback, but no death notification.");
- }
- return ndk::ScopedAStatus::fromServiceSpecificError(
- STATUS_FAILED_TRANSACTION);
- }
mH4 = std::make_shared<H4Protocol>(
mFd,
[](const std::vector<uint8_t>& /* raw_command */) {
LOG_ALWAYS_FATAL("Unexpected command!");
},
+ [this](const std::vector<uint8_t>& raw_acl) {
+ mCb->aclDataReceived(raw_acl);
+ },
+ [this](const std::vector<uint8_t>& raw_sco) {
+ mCb->scoDataReceived(raw_sco);
+ },
[this](const std::vector<uint8_t>& raw_event) {
mCb->hciEventReceived(raw_event);
},
- [this](const std::vector<uint8_t>& raw_acl) {
- mCb->hciEventReceived(raw_acl);
- },
- [this](const std::vector<uint8_t>& raw_sco) {
- mCb->hciEventReceived(raw_sco);
- },
[this](const std::vector<uint8_t>& raw_iso) {
- mCb->hciEventReceived(raw_iso);
+ mCb->isoDataReceived(raw_iso);
},
[this]() {
ALOGI("HCI socket device disconnected");
@@ -152,13 +161,29 @@
});
mFdWatcher.WatchFdForNonBlockingReads(mFd,
[this](int) { mH4->OnDataReady(); });
+
+ 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__);
mFdWatcher.StopWatchingFileDescriptors();
- ::close(mFd);
+ if (management_) {
+ management_->closeHci();
+ } else {
+ ::close(mFd);
+ }
return ndk::ScopedAStatus::ok();
}
diff --git a/bluetooth/aidl/default/BluetoothHci.h b/bluetooth/aidl/default/BluetoothHci.h
index 0ed0623..a0908f8 100644
--- a/bluetooth/aidl/default/BluetoothHci.h
+++ b/bluetooth/aidl/default/BluetoothHci.h
@@ -24,6 +24,7 @@
#include "async_fd_watcher.h"
#include "h4_protocol.h"
+#include "net_bluetooth_mgmt.h"
namespace aidl::android::hardware::bluetooth::impl {
@@ -64,8 +65,10 @@
::android::hardware::bluetooth::async::AsyncFdWatcher mFdWatcher;
+ int getFdFromDevPath();
void send(::android::hardware::bluetooth::hci::PacketType type,
const std::vector<uint8_t>& packet);
+ std::unique_ptr<NetBluetoothMgmt> management_{};
};
} // namespace aidl::android::hardware::bluetooth::impl
diff --git a/bluetooth/aidl/default/bluetooth-service-default.rc b/bluetooth/aidl/default/bluetooth-service-default.rc
index 1841c77..dc78698 100644
--- a/bluetooth/aidl/default/bluetooth-service-default.rc
+++ b/bluetooth/aidl/default/bluetooth-service-default.rc
@@ -1,4 +1,4 @@
-service bluetooth_hal_service /vendor/bin/hw/android.hardware.bluetooth-service.default
+service vendor.bluetooth-default /vendor/bin/hw/android.hardware.bluetooth-service.default
class hal
capabilities BLOCK_SUSPEND NET_ADMIN SYS_NICE
user bluetooth
diff --git a/bluetooth/aidl/default/net_bluetooth_mgmt.cpp b/bluetooth/aidl/default/net_bluetooth_mgmt.cpp
new file mode 100644
index 0000000..937cd57
--- /dev/null
+++ b/bluetooth/aidl/default/net_bluetooth_mgmt.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.bluetooth.service.default"
+
+#include "net_bluetooth_mgmt.h"
+
+#include <fcntl.h>
+#include <log/log.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+
+// Definitions imported from <linux/net/bluetooth/bluetooth.h>
+#define BTPROTO_HCI 1
+
+// Definitions imported from <linux/net/bluetooth/hci_sock.h>
+#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 <linux/net/bluetooth/mgmt.h>
+#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
+
+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));
+
+// Definitions imported from <linux/rfkill.h>
+#define RFKILL_STATE_SOFT_BLOCKED 0
+#define RFKILL_STATE_UNBLOCKED 1
+#define RFKILL_STATE_HARD_BLOCKED 2
+
+#define RFKILL_TYPE_BLUETOOTH 2
+
+#define RFKILL_OP_ADD 0
+#define RFKILL_OP_CHANGE 2
+
+struct rfkill_event {
+ uint32_t idx;
+ uint8_t type;
+ uint8_t op;
+ uint8_t soft;
+ uint8_t hard;
+} __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;
+
+ for (int i = 0; i < data->num_controllers; i++) {
+ if (data->index[i] == hci_interface) {
+ ALOGI("hci interface %d found", hci_interface);
+ ret = 0;
+ 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 = 0;
+ goto end;
+ }
+ }
+
+end:
+ ::close(fd);
+ return ret;
+}
+
+int NetBluetoothMgmt::openRfkill() {
+ int fd = open("/dev/rfkill", O_RDWR);
+ if (fd < 0) {
+ ALOGE("unable to open /dev/rfkill: %s", strerror(errno));
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ ALOGE("unable to set rfkill control device to non-blocking: %s",
+ strerror(errno));
+ ::close(fd);
+ return -1;
+ }
+
+ for (;;) {
+ struct rfkill_event event {};
+ ssize_t res = read(fd, &event, sizeof(event));
+ if (res < 0) {
+ ALOGE("error reading rfkill events: %s", strerror(errno));
+ break;
+ }
+
+ ALOGI("index:%d type:%d op:%d", event.idx, event.type, event.op);
+
+ if (event.op == RFKILL_OP_ADD && event.type == RFKILL_TYPE_BLUETOOTH) {
+ rfkill_bt_index_ = event.idx;
+ rfkill_fd_ = fd;
+ return 0;
+ }
+ }
+
+ ::close(fd);
+ return -1;
+}
+
+// Block or unblock Bluetooth.
+int NetBluetoothMgmt::rfkill(int block) {
+ if (rfkill_fd_ == -1) {
+ openRfkill();
+ }
+
+ if (rfkill_fd_ == -1) {
+ ALOGE("rfkill unavailable");
+ return -1;
+ }
+
+ struct rfkill_event event = {
+ .idx = static_cast<uint32_t>(rfkill_bt_index_),
+ .type = RFKILL_TYPE_BLUETOOTH,
+ .op = RFKILL_OP_CHANGE,
+ .soft = static_cast<uint8_t>(block),
+ .hard = 0,
+ };
+
+ int res = write(rfkill_fd_, &event, sizeof(event));
+ if (res < 0) {
+ ALOGE("error writing rfkill command: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int NetBluetoothMgmt::openHci(int hci_interface) {
+ ALOGI("opening hci interface %d", hci_interface);
+
+ // Block Bluetooth.
+ rfkill(1);
+
+ // Wait for the HCI interface to complete initialization or to come online.
+ if (waitHciDev(hci_interface)) {
+ 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<uint16_t>(hci_interface),
+ .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_interface);
+ bt_fd_ = fd;
+ return fd;
+}
+
+void NetBluetoothMgmt::closeHci() {
+ if (bt_fd_ != -1) {
+ ::close(bt_fd_);
+ bt_fd_ = -1;
+ }
+
+ // Unblock Bluetooth.
+ rfkill(0);
+}
+
+} // namespace aidl::android::hardware::bluetooth::impl
diff --git a/bluetooth/aidl/default/net_bluetooth_mgmt.h b/bluetooth/aidl/default/net_bluetooth_mgmt.h
new file mode 100644
index 0000000..5c473f2
--- /dev/null
+++ b/bluetooth/aidl/default/net_bluetooth_mgmt.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unistd.h>
+
+namespace aidl::android::hardware::bluetooth::impl {
+
+class NetBluetoothMgmt {
+ public:
+ NetBluetoothMgmt() {}
+ ~NetBluetoothMgmt() {
+ ::close(rfkill_fd_);
+ ::close(bt_fd_);
+ }
+
+ int openHci(int hci_interface = 0);
+ void closeHci();
+
+ private:
+ int waitHciDev(int hci_interface);
+ int openRfkill();
+ int rfkill(int block);
+
+ // Index of the first rfkill device of type bluetooth.
+ int rfkill_bt_index_{-1};
+ // File descriptor opened to /dev/rfkill.
+ int rfkill_fd_{-1};
+ // File descriptor opened to the bluetooth user channel.
+ int bt_fd_{-1};
+};
+
+} // namespace aidl::android::hardware::bluetooth::impl
diff --git a/bluetooth/aidl/vts/Android.bp b/bluetooth/aidl/vts/Android.bp
new file mode 100644
index 0000000..414f707
--- /dev/null
+++ b/bluetooth/aidl/vts/Android.bp
@@ -0,0 +1,48 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalBluetoothTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalBluetoothTargetTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ ],
+ static_libs: [
+ "android.hardware.bluetooth-V1-ndk",
+ "libbluetooth-types",
+ ],
+ test_config: "VtsHalBluetoothTargetTest.xml",
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "readability-inconsistent-declaration-parameter-name",
+ "readability-*",
+ "-readability-function-size",
+ "-readability-identifier-length",
+ "-readability-implicit-bool-conversion",
+ "-readability-magic-numbers",
+ "-readability-use-anyofallof",
+ ],
+ tidy_checks_as_errors: [
+ "readability-*",
+ ],
+ tidy_flags: [
+ "--header-filter=^.*tools\\/rootcanal\\/(model|include|net|desktop)\\/(.(?!\\.pb\\.h))*$",
+ ],
+}
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
new file mode 100644
index 0000000..27e6ebf
--- /dev/null
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/bluetooth/BnBluetoothHciCallbacks.h>
+#include <aidl/android/hardware/bluetooth/IBluetoothHci.h>
+#include <aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.h>
+#include <aidl/android/hardware/bluetooth/Status.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <vector>
+
+using aidl::android::hardware::bluetooth::IBluetoothHci;
+using aidl::android::hardware::bluetooth::IBluetoothHciCallbacks;
+using aidl::android::hardware::bluetooth::Status;
+using ndk::ScopedAStatus;
+using ndk::SpAIBinder;
+
+// Bluetooth Core Specification 3.0 + HS
+static constexpr uint8_t kHciMinimumHciVersion = 5;
+// Bluetooth Core Specification 3.0 + HS
+static constexpr uint8_t kHciMinimumLmpVersion = 5;
+
+static constexpr std::chrono::milliseconds kWaitForInitTimeout(2000);
+static constexpr std::chrono::milliseconds kWaitForHciEventTimeout(2000);
+static constexpr std::chrono::milliseconds kInterfaceCloseDelayMs(200);
+
+static constexpr uint8_t kCommandHciShouldBeUnknown[] = {
+ 0xff, 0x3B, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+static constexpr uint8_t kCommandHciReadLocalVersionInformation[] = {0x01, 0x10,
+ 0x00};
+static constexpr uint8_t kCommandHciReadBufferSize[] = {0x05, 0x10, 0x00};
+static constexpr uint8_t kCommandHciReset[] = {0x03, 0x0c, 0x00};
+static constexpr uint8_t kHciStatusSuccess = 0x00;
+static constexpr uint8_t kHciStatusUnknownHciCommand = 0x01;
+
+static constexpr uint8_t kEventCommandComplete = 0x0e;
+static constexpr uint8_t kEventCommandStatus = 0x0f;
+static constexpr uint8_t kEventNumberOfCompletedPackets = 0x13;
+
+static constexpr size_t kEventCodeByte = 0;
+static constexpr size_t kEventCommandStatusStatusByte = 2;
+static constexpr size_t kEventCommandStatusOpcodeLsByte = 4; // Bytes 4 and 5
+static constexpr size_t kEventCommandCompleteOpcodeLsByte = 3; // Bytes 3 and 4
+static constexpr size_t kEventCommandCompleteStatusByte = 5;
+static constexpr size_t kEventCommandCompleteFirstParamByte = 6;
+static constexpr size_t kEventLocalHciVersionByte =
+ kEventCommandCompleteFirstParamByte;
+static constexpr size_t kEventLocalLmpVersionByte =
+ kEventLocalHciVersionByte + 3;
+
+static constexpr size_t kEventNumberOfCompletedPacketsNumHandles = 2;
+
+// To discard Qualcomm ACL debugging
+static constexpr uint16_t kAclHandleQcaDebugMessage = 0xedc;
+
+class ThroughputLogger {
+ public:
+ ThroughputLogger(std::string task)
+ : task_(task), start_time_(std::chrono::steady_clock::now()) {}
+
+ ~ThroughputLogger() {
+ if (total_bytes_ == 0) {
+ return;
+ }
+ std::chrono::duration<double> duration =
+ std::chrono::steady_clock::now() - start_time_;
+ double s = duration.count();
+ if (s == 0) {
+ return;
+ }
+ double rate_kb = (static_cast<double>(total_bytes_) / s) / 1024;
+ ALOGD("%s %.1f KB/s (%zu bytes in %.3fs)", task_.c_str(), rate_kb,
+ total_bytes_, s);
+ }
+
+ void setTotalBytes(size_t total_bytes) { total_bytes_ = total_bytes; }
+
+ private:
+ size_t total_bytes_;
+ std::string task_;
+ std::chrono::steady_clock::time_point start_time_;
+};
+
+// The main test class for Bluetooth HAL.
+class BluetoothAidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ // currently test passthrough mode only
+ hci = IBluetoothHci::fromBinder(
+ SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+ ASSERT_NE(hci, nullptr);
+ ALOGI("%s: getService() for bluetooth hci is %s", __func__,
+ hci->isRemote() ? "remote" : "local");
+
+ // Lambda function
+ auto on_binder_death = [](void* /*cookie*/) { FAIL(); };
+
+ bluetooth_hci_death_recipient =
+ AIBinder_DeathRecipient_new(on_binder_death);
+ ASSERT_NE(bluetooth_hci_death_recipient, nullptr);
+ ASSERT_EQ(STATUS_OK,
+ AIBinder_linkToDeath(hci->asBinder().get(),
+ bluetooth_hci_death_recipient, 0));
+
+ hci_cb = ndk::SharedRefBase::make<BluetoothHciCallbacks>(*this);
+ ASSERT_NE(hci_cb, nullptr);
+
+ max_acl_data_packet_length = 0;
+ max_sco_data_packet_length = 0;
+ max_acl_data_packets = 0;
+ max_sco_data_packets = 0;
+
+ event_cb_count = 0;
+ acl_cb_count = 0;
+ sco_cb_count = 0;
+
+ ASSERT_TRUE(hci->initialize(hci_cb).isOk());
+ auto future = initialized_promise.get_future();
+ auto timeout_status = future.wait_for(kWaitForInitTimeout);
+ ASSERT_EQ(timeout_status, std::future_status::ready);
+ ASSERT_TRUE(future.get());
+ }
+
+ virtual void TearDown() override {
+ ALOGI("TearDown");
+ // Should not be checked in production code
+ ASSERT_TRUE(hci->close().isOk());
+ std::this_thread::sleep_for(kInterfaceCloseDelayMs);
+ handle_no_ops();
+ EXPECT_EQ(static_cast<size_t>(0), event_queue.size());
+ EXPECT_EQ(static_cast<size_t>(0), sco_queue.size());
+ EXPECT_EQ(static_cast<size_t>(0), acl_queue.size());
+ EXPECT_EQ(static_cast<size_t>(0), iso_queue.size());
+ }
+
+ void setBufferSizes();
+
+ // Helper functions to try to get a handle on verbosity
+ void handle_no_ops();
+ void wait_for_event(bool timeout_is_error);
+ void wait_for_command_complete_event(std::vector<uint8_t> cmd);
+ int wait_for_completed_packets_event(uint16_t handle);
+
+ // A simple test implementation of BluetoothHciCallbacks.
+ class BluetoothHciCallbacks
+ : public aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks {
+ BluetoothAidlTest& parent_;
+
+ public:
+ BluetoothHciCallbacks(BluetoothAidlTest& parent) : parent_(parent){};
+
+ virtual ~BluetoothHciCallbacks() = default;
+
+ ndk::ScopedAStatus initializationComplete(Status status) {
+ parent_.initialized_promise.set_value(status == Status::SUCCESS);
+ ALOGV("%s (status = %d)", __func__, static_cast<int>(status));
+ return ScopedAStatus::ok();
+ };
+
+ ndk::ScopedAStatus hciEventReceived(const std::vector<uint8_t>& event) {
+ parent_.event_cb_count++;
+ parent_.event_queue.push(event);
+ ALOGV("Event received (length = %d)", static_cast<int>(event.size()));
+ return ScopedAStatus::ok();
+ };
+
+ ndk::ScopedAStatus aclDataReceived(const std::vector<uint8_t>& data) {
+ parent_.acl_cb_count++;
+ parent_.acl_queue.push(data);
+ return ScopedAStatus::ok();
+ };
+
+ ndk::ScopedAStatus scoDataReceived(const std::vector<uint8_t>& data) {
+ parent_.sco_cb_count++;
+ parent_.sco_queue.push(data);
+ return ScopedAStatus::ok();
+ };
+
+ ndk::ScopedAStatus isoDataReceived(const std::vector<uint8_t>& data) {
+ parent_.iso_cb_count++;
+ parent_.iso_queue.push(data);
+ return ScopedAStatus::ok();
+ };
+ };
+
+ template <class T>
+ class WaitQueue {
+ public:
+ WaitQueue(){};
+
+ virtual ~WaitQueue() = default;
+
+ bool empty() const {
+ std::lock_guard<std::mutex> lock(m_);
+ return q_.empty();
+ };
+
+ size_t size() const {
+ std::lock_guard<std::mutex> lock(m_);
+ return q_.size();
+ };
+
+ void push(const T& v) {
+ std::lock_guard<std::mutex> lock(m_);
+ q_.push(v);
+ ready_.notify_one();
+ };
+
+ bool pop(T& v) {
+ std::lock_guard<std::mutex> lock(m_);
+ if (q_.empty()) {
+ return false;
+ }
+ v = std::move(q_.front());
+ q_.pop();
+ return true;
+ };
+
+ bool front(T& v) {
+ std::lock_guard<std::mutex> lock(m_);
+ if (q_.empty()) {
+ return false;
+ }
+ v = q_.front();
+ return true;
+ };
+
+ void wait() {
+ std::unique_lock<std::mutex> lock(m_);
+ while (q_.empty()) {
+ ready_.wait(lock);
+ }
+ };
+
+ bool waitWithTimeout(std::chrono::milliseconds timeout) {
+ std::unique_lock<std::mutex> lock(m_);
+ while (q_.empty()) {
+ if (ready_.wait_for(lock, timeout) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ bool tryPopWithTimeout(T& v, std::chrono::milliseconds timeout) {
+ std::unique_lock<std::mutex> lock(m_);
+ while (q_.empty()) {
+ if (ready_.wait_for(lock, timeout) == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ v = std::move(q_.front());
+ q_.pop();
+ return true;
+ };
+
+ private:
+ mutable std::mutex m_;
+ std::queue<T> q_;
+ std::condition_variable_any ready_;
+ };
+
+ std::shared_ptr<IBluetoothHci> hci;
+ std::shared_ptr<BluetoothHciCallbacks> hci_cb;
+ AIBinder_DeathRecipient* bluetooth_hci_death_recipient;
+ WaitQueue<std::vector<uint8_t>> event_queue;
+ WaitQueue<std::vector<uint8_t>> acl_queue;
+ WaitQueue<std::vector<uint8_t>> sco_queue;
+ WaitQueue<std::vector<uint8_t>> iso_queue;
+
+ std::promise<bool> initialized_promise;
+ int event_cb_count;
+ int sco_cb_count;
+ int acl_cb_count;
+ int iso_cb_count;
+
+ int max_acl_data_packet_length;
+ int max_sco_data_packet_length;
+ int max_acl_data_packets;
+ int max_sco_data_packets;
+};
+
+// Discard NO-OPs from the event queue.
+void BluetoothAidlTest::handle_no_ops() {
+ while (!event_queue.empty()) {
+ std::vector<uint8_t> event;
+ event_queue.front(event);
+ ASSERT_GE(event.size(),
+ static_cast<size_t>(kEventCommandCompleteStatusByte));
+ bool event_is_no_op =
+ (event[kEventCodeByte] == kEventCommandComplete) &&
+ (event[kEventCommandCompleteOpcodeLsByte] == 0x00) &&
+ (event[kEventCommandCompleteOpcodeLsByte + 1] == 0x00);
+ event_is_no_op |= (event[kEventCodeByte] == kEventCommandStatus) &&
+ (event[kEventCommandStatusOpcodeLsByte] == 0x00) &&
+ (event[kEventCommandStatusOpcodeLsByte + 1] == 0x00);
+ if (event_is_no_op) {
+ event_queue.pop(event);
+ } else {
+ break;
+ }
+ }
+ // Discard Qualcomm ACL debugging
+ while (!acl_queue.empty()) {
+ std::vector<uint8_t> acl_packet;
+ acl_queue.front(acl_packet);
+ uint16_t connection_handle = acl_packet[1] & 0xF;
+ connection_handle <<= 8;
+ connection_handle |= acl_packet[0];
+ bool packet_is_no_op = connection_handle == kAclHandleQcaDebugMessage;
+ if (packet_is_no_op) {
+ acl_queue.pop(acl_packet);
+ } else {
+ break;
+ }
+ }
+}
+
+// Receive an event, discarding NO-OPs.
+void BluetoothAidlTest::wait_for_event(bool timeout_is_error = true) {
+ if (timeout_is_error) {
+ ASSERT_TRUE(event_queue.waitWithTimeout(kWaitForHciEventTimeout));
+ } else {
+ event_queue.wait();
+ }
+ ASSERT_LT(static_cast<size_t>(0), event_queue.size());
+ if (event_queue.empty()) {
+ // waitWithTimeout timed out
+ return;
+ }
+ handle_no_ops();
+}
+
+// Wait until a command complete is received.
+void BluetoothAidlTest::wait_for_command_complete_event(
+ std::vector<uint8_t> cmd) {
+ wait_for_event();
+ std::vector<uint8_t> event;
+ ASSERT_TRUE(event_queue.pop(event));
+
+ ASSERT_GT(event.size(), static_cast<size_t>(kEventCommandCompleteStatusByte));
+ ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
+ ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
+ ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
+ ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+}
+
+// Send the command to read the controller's buffer sizes.
+void BluetoothAidlTest::setBufferSizes() {
+ std::vector<uint8_t> cmd{
+ kCommandHciReadBufferSize,
+ kCommandHciReadBufferSize + sizeof(kCommandHciReadBufferSize)};
+ hci->sendHciCommand(cmd);
+
+ wait_for_event();
+ if (event_queue.empty()) {
+ return;
+ }
+ std::vector<uint8_t> event;
+ ASSERT_TRUE(event_queue.pop(event));
+
+ ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
+ ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
+ ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
+ ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+
+ max_acl_data_packet_length =
+ event[kEventCommandCompleteStatusByte + 1] +
+ (event[kEventCommandCompleteStatusByte + 2] << 8);
+ max_sco_data_packet_length = event[kEventCommandCompleteStatusByte + 3];
+ max_acl_data_packets = event[kEventCommandCompleteStatusByte + 4] +
+ (event[kEventCommandCompleteStatusByte + 5] << 8);
+ max_sco_data_packets = event[kEventCommandCompleteStatusByte + 6] +
+ (event[kEventCommandCompleteStatusByte + 7] << 8);
+
+ ALOGD("%s: ACL max %d num %d SCO max %d num %d", __func__,
+ static_cast<int>(max_acl_data_packet_length),
+ static_cast<int>(max_acl_data_packets),
+ static_cast<int>(max_sco_data_packet_length),
+ static_cast<int>(max_sco_data_packets));
+}
+
+// Return the number of completed packets reported by the controller.
+int BluetoothAidlTest::wait_for_completed_packets_event(uint16_t handle) {
+ int packets_processed = 0;
+ wait_for_event(false);
+ if (event_queue.empty()) {
+ ALOGW("%s: waitForBluetoothCallback timed out.", __func__);
+ return packets_processed;
+ }
+ while (!event_queue.empty()) {
+ std::vector<uint8_t> event;
+ EXPECT_TRUE(event_queue.pop(event));
+
+ EXPECT_EQ(kEventNumberOfCompletedPackets, event[kEventCodeByte]);
+ EXPECT_EQ(1, event[kEventNumberOfCompletedPacketsNumHandles]);
+
+ uint16_t event_handle = event[3] + (event[4] << 8);
+ EXPECT_EQ(handle, event_handle);
+
+ packets_processed += event[5] + (event[6] << 8);
+ }
+ return packets_processed;
+}
+
+// Empty test: Initialize()/Close() are called in SetUp()/TearDown().
+TEST_P(BluetoothAidlTest, InitializeAndClose) {}
+
+// Send an HCI Reset with sendHciCommand and wait for a command complete event.
+TEST_P(BluetoothAidlTest, HciReset) {
+ std::vector<uint8_t> reset{kCommandHciReset,
+ kCommandHciReset + sizeof(kCommandHciReset)};
+ hci->sendHciCommand(reset);
+
+ wait_for_command_complete_event(reset);
+}
+
+// Read and check the HCI version of the controller.
+TEST_P(BluetoothAidlTest, HciVersionTest) {
+ std::vector<uint8_t> cmd{kCommandHciReadLocalVersionInformation,
+ kCommandHciReadLocalVersionInformation +
+ sizeof(kCommandHciReadLocalVersionInformation)};
+ hci->sendHciCommand(cmd);
+
+ wait_for_event();
+ if (event_queue.empty()) {
+ return;
+ }
+
+ std::vector<uint8_t> event;
+ ASSERT_TRUE(event_queue.pop(event));
+ ASSERT_GT(event.size(), static_cast<size_t>(kEventLocalLmpVersionByte));
+
+ ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]);
+ ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
+ ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
+ ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]);
+
+ ASSERT_LE(kHciMinimumHciVersion, event[kEventLocalHciVersionByte]);
+ ASSERT_LE(kHciMinimumLmpVersion, event[kEventLocalLmpVersionByte]);
+}
+
+// Send an unknown HCI command and wait for the error message.
+TEST_P(BluetoothAidlTest, HciUnknownCommand) {
+ std::vector<uint8_t> cmd{
+ kCommandHciShouldBeUnknown,
+ kCommandHciShouldBeUnknown + sizeof(kCommandHciShouldBeUnknown)};
+ hci->sendHciCommand(cmd);
+
+ wait_for_event();
+ if (event_queue.empty()) {
+ return;
+ }
+
+ std::vector<uint8_t> event;
+ ASSERT_TRUE(event_queue.pop(event));
+
+ ASSERT_GT(event.size(), static_cast<size_t>(kEventCommandCompleteStatusByte));
+ if (event[kEventCodeByte] == kEventCommandComplete) {
+ ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]);
+ ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]);
+ ASSERT_EQ(kHciStatusUnknownHciCommand,
+ event[kEventCommandCompleteStatusByte]);
+ } else {
+ ASSERT_EQ(kEventCommandStatus, event[kEventCodeByte]);
+ ASSERT_EQ(cmd[0], event[kEventCommandStatusOpcodeLsByte]);
+ ASSERT_EQ(cmd[1], event[kEventCommandStatusOpcodeLsByte + 1]);
+ ASSERT_EQ(kHciStatusUnknownHciCommand,
+ event[kEventCommandStatusStatusByte]);
+ }
+}
+
+// Set all bits in the event mask
+TEST_P(BluetoothAidlTest, SetEventMask) {
+ std::vector<uint8_t> set_event_mask{
+ 0x01, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff};
+ hci->sendHciCommand({set_event_mask});
+ wait_for_command_complete_event(set_event_mask);
+}
+
+// Set all bits in the LE event mask
+TEST_P(BluetoothAidlTest, SetLeEventMask) {
+ std::vector<uint8_t> set_event_mask{
+ 0x20, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff};
+ hci->sendHciCommand({set_event_mask});
+ wait_for_command_complete_event(set_event_mask);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothAidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(
+ IBluetoothHci::descriptor)),
+ android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ABinderProcess_startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.xml b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.xml
new file mode 100644
index 0000000..3a42ae6
--- /dev/null
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<configuration description="Runs VtsHalBluetoothTargetTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
+ <option name="run-command" value="cmd bluetooth_manager disable" />
+ <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
+ <option name="teardown-command" value="cmd bluetooth_manager enable" />
+ <option name="teardown-command" value="cmd bluetooth_manager wait-for-state:STATE_ON" />
+ <option name="teardown-command" value="settings put global ble_scan_always_enabled 1" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="VtsHalBluetoothTargetTest->/data/local/tmp/VtsHalBluetoothTargetTest" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="VtsHalBluetoothTargetTest" />
+ </test>
+</configuration>
diff --git a/camera/provider/aidl/vts/camera_aidl_test.h b/camera/provider/aidl/vts/camera_aidl_test.h
index d828cee..3741a64 100644
--- a/camera/provider/aidl/vts/camera_aidl_test.h
+++ b/camera/provider/aidl/vts/camera_aidl_test.h
@@ -17,7 +17,10 @@
#ifndef HARDWARE_INTERFACES_CAMERA_PROVIDER_AIDL_VTS_CAMERA_AIDL_TEST_H_
#define HARDWARE_INTERFACES_CAMERA_PROVIDER_AIDL_VTS_CAMERA_AIDL_TEST_H_
+// TODO: LOG_TAG should not be in header
+#ifndef LOG_TAG
#define LOG_TAG "camera_aidl_hal_test"
+#endif
#include <string>
#include <unordered_map>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 41ad0ec..16237bd 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -212,14 +212,6 @@
<regex-instance>[^/]+/[0-9]+</regex-instance>
</interface>
</hal>
- <hal format="hidl" optional="true">
- <name>android.hardware.cas</name>
- <version>1.1-2</version>
- <interface>
- <name>IMediaCasService</name>
- <instance>default</instance>
- </interface>
- </hal>
<hal format="aidl" optional="true">
<name>android.hardware.cas</name>
<interface>
@@ -692,6 +684,14 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.tetheroffload</name>
+ <version>1</version>
+ <interface>
+ <name>IOffload</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="false">
<name>android.hardware.thermal</name>
<version>1</version>
@@ -702,6 +702,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.tv.hdmi.cec</name>
+ <version>1</version>
<interface>
<name>IHdmiCec</name>
<instance>default</instance>
@@ -709,6 +710,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.tv.hdmi.earc</name>
+ <version>1</version>
<interface>
<name>IEArc</name>
<instance>default</instance>
@@ -716,6 +718,7 @@
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.tv.hdmi.connection</name>
+ <version>1</version>
<interface>
<name>IHdmiConnection</name>
<instance>default</instance>
@@ -761,6 +764,13 @@
</interface>
</hal>
<hal format="aidl" optional="true">
+ <name>android.hardware.usb.gadget</name>
+ <interface>
+ <name>IUsbGadget</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
diff --git a/drm/aidl/vts/drm_hal_common.cpp b/drm/aidl/vts/drm_hal_common.cpp
index 7de8167..f5ef0e7 100644
--- a/drm/aidl/vts/drm_hal_common.cpp
+++ b/drm/aidl/vts/drm_hal_common.cpp
@@ -187,6 +187,12 @@
auto svc = GetParamService();
const string drmInstance = HalFullName(kDrmIface, svc);
+ if (!vendorModule) {
+ ASSERT_NE(drmInstance, HalFullName(kDrmIface, "widevine")) << "Widevine requires vendor module.";
+ ASSERT_NE(drmInstance, HalFullName(kDrmIface, "clearkey")) << "Clearkey requires vendor module.";
+ GTEST_SKIP() << "No vendor module installed";
+ }
+
if (drmInstance.find("IDrmFactory") != std::string::npos) {
drmFactory = IDrmFactory::fromBinder(
::ndk::SpAIBinder(AServiceManager_waitForService(drmInstance.c_str())));
@@ -195,12 +201,6 @@
cryptoPlugin = createCryptoPlugin();
}
- if (!vendorModule) {
- ASSERT_NE(drmInstance, "widevine") << "Widevine requires vendor module.";
- ASSERT_NE(drmInstance, "clearkey") << "Clearkey requires vendor module.";
- GTEST_SKIP() << "No vendor module installed";
- }
-
ASSERT_EQ(HalBaseName(drmInstance), vendorModule->getServiceName());
contentConfigurations = vendorModule->getContentConfigurations();
diff --git a/drm/aidl/vts/drm_hal_test.cpp b/drm/aidl/vts/drm_hal_test.cpp
index 14b3acf..847a4dc 100644
--- a/drm/aidl/vts/drm_hal_test.cpp
+++ b/drm/aidl/vts/drm_hal_test.cpp
@@ -198,6 +198,18 @@
EXPECT_NE(keySetId, keySetId2.keySetId);
}
+ for (auto level : {kHwSecureAll, kSwSecureCrypto}) {
+ Status err = Status::OK;
+ auto sid = openSession(level, &err);
+ if (err == Status::OK) {
+ closeSession(sid);
+ } else if (err == Status::ERROR_DRM_CANNOT_HANDLE) {
+ continue;
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_NOT_PROVISIONED, err);
+ provision();
+ }
+ }
ret = drmPlugin->removeOfflineLicense({keySetId});
EXPECT_TXN(ret);
EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
diff --git a/gatekeeper/aidl/android/hardware/gatekeeper/IGatekeeper.aidl b/gatekeeper/aidl/android/hardware/gatekeeper/IGatekeeper.aidl
index 927293e..215c6e6 100644
--- a/gatekeeper/aidl/android/hardware/gatekeeper/IGatekeeper.aidl
+++ b/gatekeeper/aidl/android/hardware/gatekeeper/IGatekeeper.aidl
@@ -30,7 +30,7 @@
const int STATUS_REENROLL = 1;
/* operation is successful */
const int STATUS_OK = 0;
- /* operation is successful. */
+ /* operation failed. */
const int ERROR_GENERAL_FAILURE = -1;
/* operation should be retried after timeout. */
const int ERROR_RETRY_TIMEOUT = -2;
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h
index 02f6212..f1d61f8 100644
--- a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerCommandEngine.h
@@ -73,9 +73,7 @@
}
bool executeSetLayerPerFrameMetadataBlobs(uint16_t length) {
- // must have at least one metadata blob
- // of at least size 1 in queue (i.e {/*numBlobs=*/1, key, size, blob})
- if (length < 4) {
+ if (length == 0) {
return false;
}
diff --git a/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h b/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h
index 0f6d146..0de88cd 100644
--- a/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h
+++ b/graphics/mapper/stable-c/include/android/hardware/graphics/mapper/IMapper.h
@@ -241,7 +241,9 @@
* acceptable.
*
* 1D buffers (width = size in bytes, height = 1, pixel_format = BLOB) must
- * "lock in place". The buffers must be directly accessible via mapping.
+ * "lock in place" and behave similar to shared memory. That is, multiple threads or processes
+ * may lock the buffer for reading & writing and the results must follow the device's memory
+ * model.
*
* The client must not modify the content of the buffer outside of
* @p accessRegion, and the device need not guarantee that content outside
diff --git a/health/aidl/Android.bp b/health/aidl/Android.bp
index a4d4ace..64d83f3 100644
--- a/health/aidl/Android.bp
+++ b/health/aidl/Android.bp
@@ -46,8 +46,8 @@
}
-cc_library {
- name: "android.hardware.health-translate-ndk",
+cc_defaults {
+ name: "android.hardware.health-translate-ndk_defaults",
vendor_available: true,
recovery_available: true,
host_supported: true,
@@ -55,7 +55,6 @@
shared_libs: [
"libbinder_ndk",
"libhidlbase",
- "android.hardware.health-V1-ndk",
"android.hardware.health@2.0",
"android.hardware.health@2.1",
],
@@ -71,6 +70,23 @@
},
}
+cc_library {
+ name: "android.hardware.health-translate-ndk",
+ defaults: ["android.hardware.health-translate-ndk_defaults"],
+ shared_libs: [
+ "android.hardware.health-V1-ndk",
+ ],
+}
+
+// TODO(b/251425963): remove when android.hardware.health is upgraded to V2.
+cc_library {
+ name: "android.hardware.health-translate-V1-ndk",
+ defaults: ["android.hardware.health-translate-ndk_defaults"],
+ shared_libs: [
+ "android.hardware.health-V1-ndk",
+ ],
+}
+
java_library {
name: "android.hardware.health-translate-java",
srcs: ["android/hardware/health/Translate.java"],
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index fb5048a..728cc91 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -4866,6 +4866,11 @@
if (vsr_api_level < 33) {
GTEST_SKIP() << "Applies only to VSR API level 33, this device is: " << vsr_api_level;
}
+ char soc_model[PROPERTY_VALUE_MAX] = {};
+ property_get("ro.soc.model", soc_model, "");
+ if (!strcmp(soc_model, "SM8550")) {
+ GTEST_SKIP() << "Skip QTI SM8550 chipset, the SOC model of this device is: " << soc_model;
+ }
FAIL() << "VSR 13+ requires KeyMint version 2";
}
diff --git a/radio/aidl/vts/radio_data_test.cpp b/radio/aidl/vts/radio_data_test.cpp
index f38a958..1cc6a36 100644
--- a/radio/aidl/vts/radio_data_test.cpp
+++ b/radio/aidl/vts/radio_data_test.cpp
@@ -143,9 +143,20 @@
TrafficDescriptor trafficDescriptor;
OsAppId osAppId;
- std::string osAppIdString("osAppId");
- std::vector<unsigned char> osAppIdVec(osAppIdString.begin(), osAppIdString.end());
- osAppId.osAppId = osAppIdVec;
+ osAppId.osAppId = {static_cast<unsigned char>(-105), static_cast<unsigned char>(-92),
+ static_cast<unsigned char>(-104), static_cast<unsigned char>(-29),
+ static_cast<unsigned char>(-4), static_cast<unsigned char>(-110),
+ static_cast<unsigned char>(92), static_cast<unsigned char>(-108),
+ static_cast<unsigned char>(-119), static_cast<unsigned char>(-122),
+ static_cast<unsigned char>(3), static_cast<unsigned char>(51),
+ static_cast<unsigned char>(-48), static_cast<unsigned char>(110),
+ static_cast<unsigned char>(78), static_cast<unsigned char>(71),
+ static_cast<unsigned char>(10), static_cast<unsigned char>(69),
+ static_cast<unsigned char>(78), static_cast<unsigned char>(84),
+ static_cast<unsigned char>(69), static_cast<unsigned char>(82),
+ static_cast<unsigned char>(80), static_cast<unsigned char>(82),
+ static_cast<unsigned char>(73), static_cast<unsigned char>(83),
+ static_cast<unsigned char>(69)};
trafficDescriptor.osAppId = osAppId;
DataProfileInfo dataProfileInfo;
diff --git a/scripts/anapic_hidl2aidl_review.sh b/scripts/anapic_hidl2aidl_review.sh
new file mode 100755
index 0000000..330ae32
--- /dev/null
+++ b/scripts/anapic_hidl2aidl_review.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+if [[ $# -ne 1 ]]; then
+ echo "Usage: $0 INTERFACE_NAME"
+ echo "- INTERFACE_NAME fully qualified HIDL interface name with version"
+ echo "example of creating the diffs for android.hardware.boot@1.2"
+ echo "$ ./anapic_hidl2aidl_review.sh android.hardware.boot@1.2"
+ exit 1
+fi
+
+# for pathmod
+source ${ANDROID_BUILD_TOP}/build/make/envsetup.sh
+
+set -ex
+type hidl2aidl 2>/dev/null || m hidl2aidl
+
+INTERFACE_NAME_NO_VER=${1%@*}
+pushd $(pathmod $INTERFACE_NAME_NO_VER)
+rm -rf android
+hidl2aidl -o . "$1"
+rm -rf conversion.log translate include
+git add -A
+git commit -am "convert $1" --no-edit
+git revert HEAD --no-edit
+git commit --amend --no-edit
+repo upload . --no-verify
+popd
diff --git a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
index 7c5a704..b9ce9d1 100644
--- a/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
+++ b/secure_element/aidl/android/hardware/secure_element/ISecureElement.aidl
@@ -113,7 +113,8 @@
* Reset the Secure Element.
*
* HAL should trigger reset to the secure element. It could hardware power cycle or
- * a soft reset depends on the hardware design.
+ * a soft reset depends on the hardware design. All channels opened are
+ * closed by this operation.
* HAL service must send onStateChange() with connected equal to true
* after resetting and all the re-initialization has been successfully completed.
*/
diff --git a/secure_element/aidl/default/main.cpp b/secure_element/aidl/default/main.cpp
index 9b5a8fc..6149eae 100644
--- a/secure_element/aidl/default/main.cpp
+++ b/secure_element/aidl/default/main.cpp
@@ -418,29 +418,37 @@
test_applet});
}
- ScopedAStatus init(const std::shared_ptr<ISecureElementCallback>& clientCallback) override {
- LOG(INFO) << __func__ << " callback: " << clientCallback.get();
- if (!clientCallback) {
+ ScopedAStatus init(const std::shared_ptr<ISecureElementCallback>& client_callback) override {
+ LOG(INFO) << __func__ << " callback: " << client_callback.get();
+ if (client_callback == nullptr) {
return ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
}
- client_callback_ = clientCallback;
+ for (auto& channel : channels_) {
+ channel = Channel();
+ }
+ client_callback_ = client_callback;
client_callback_->onStateChange(true, "init");
return ScopedAStatus::ok();
}
ScopedAStatus getAtr(std::vector<uint8_t>* aidl_return) override {
LOG(INFO) << __func__;
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
*aidl_return = atr_;
return ScopedAStatus::ok();
}
ScopedAStatus reset() override {
LOG(INFO) << __func__;
- CHECK(client_callback_ != nullptr) << " init not invoked";
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
client_callback_->onStateChange(false, "reset");
client_callback_->onStateChange(true, "reset");
// All channels are closed after reset.
- for (auto channel : channels_) {
+ for (auto& channel : channels_) {
channel = Channel();
}
return ScopedAStatus::ok();
@@ -448,6 +456,9 @@
ScopedAStatus isCardPresent(bool* aidl_return) override {
LOG(INFO) << __func__;
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
*aidl_return = true;
return ScopedAStatus::ok();
}
@@ -456,6 +467,9 @@
std::vector<uint8_t>* aidl_return) override {
LOG(INFO) << __func__ << " aid: " << HexString(aid.data(), aid.size()) << " (" << aid.size()
<< ") p2 " << p2;
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
std::vector<uint8_t> select_response;
std::shared_ptr<se::Applet> applet = nullptr;
@@ -508,6 +522,10 @@
LOG(INFO) << __func__ << " aid: " << HexString(aid.data(), aid.size()) << " (" << aid.size()
<< ") p2 " << p2;
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
size_t channel_number = 1;
std::vector<uint8_t> select_response;
std::shared_ptr<se::Applet> applet = nullptr;
@@ -562,6 +580,10 @@
ScopedAStatus closeChannel(int8_t channel_number) override {
LOG(INFO) << __func__ << " channel number: " << static_cast<int>(channel_number);
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
// The selected basic or logical channel is not opened.
if (channel_number >= channels_.size() || !channels_[channel_number].opened) {
return ScopedAStatus::ok();
@@ -580,6 +602,9 @@
std::vector<uint8_t>* aidl_return) override {
LOG(INFO) << __func__ << " data: " << HexString(data.data(), data.size()) << " ("
<< data.size() << ")";
+ if (client_callback_ == nullptr) {
+ return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
se::Apdu apdu(data);
uint8_t channel_number = apdu.get_channel_number();
@@ -648,7 +673,7 @@
// Channel 0 is the basic channel, channels 1-19 are the logical channels.
std::array<Channel, 20> channels_{};
- std::shared_ptr<ISecureElementCallback> client_callback_;
+ std::shared_ptr<ISecureElementCallback> client_callback_{nullptr};
// Secure element abstraction.
diff --git a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
index a85a8bc..c265579 100644
--- a/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
+++ b/secure_element/aidl/vts/VtsHalSecureElementTargetTest.cpp
@@ -18,6 +18,7 @@
#include <aidl/Vintf.h>
#include <aidl/android/hardware/secure_element/BnSecureElementCallback.h>
#include <aidl/android/hardware/secure_element/ISecureElement.h>
+#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gmock/gmock.h>
@@ -44,10 +45,29 @@
EXPECT_TRUE(status_impl.isOk()) << status_impl.getDescription(); \
} while (false)
-static const std::vector<uint8_t> kDataApdu = {0x00, 0x08, 0x00, 0x00, 0x00};
-static const std::vector<uint8_t> kAndroidTestAid = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41,
- 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64,
- 0x43, 0x54, 0x53, 0x31};
+#define EXPECT_ERR(status) \
+ do { \
+ auto status_impl = (status); \
+ EXPECT_FALSE(status_impl.isOk()) << status_impl.getDescription(); \
+ } while (false)
+
+// APDU defined in CTS tests.
+// The applet selected with kSelectableAid will return 256 bytes of data
+// in response.
+static const std::vector<uint8_t> kDataApdu = {
+ 0x00, 0x08, 0x00, 0x00, 0x00,
+};
+
+// Selectable test AID defined in CTS tests.
+static const std::vector<uint8_t> kSelectableAid = {
+ 0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31,
+};
+// Non-selectable test AID defined in CTS tests.
+static const std::vector<uint8_t> kNonSelectableAid = {
+ 0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64,
+ 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0xFF,
+};
class MySecureElementCallback : public BnSecureElementCallback {
public:
@@ -75,91 +95,173 @@
class SecureElementAidl : public ::testing::TestWithParam<std::string> {
public:
- virtual void SetUp() override {
+ void SetUp() override {
SpAIBinder binder = SpAIBinder(AServiceManager_waitForService(GetParam().c_str()));
- se = ISecureElement::fromBinder(binder);
- ASSERT_NE(se, nullptr);
- cb = SharedRefBase::make<MySecureElementCallback>();
- EXPECT_OK(se->init(cb));
+ secure_element_ = ISecureElement::fromBinder(binder);
+ ASSERT_NE(secure_element_, nullptr);
- cb->expectCallbackHistory({true});
+ secure_element_callback_ = SharedRefBase::make<MySecureElementCallback>();
+ ASSERT_NE(secure_element_callback_, nullptr);
+
+ EXPECT_OK(secure_element_->init(secure_element_callback_));
+ secure_element_callback_->expectCallbackHistory({true});
}
- std::shared_ptr<ISecureElement> se;
- std::shared_ptr<MySecureElementCallback> cb;
+ void TearDown() override {
+ secure_element_ = nullptr;
+ secure_element_callback_ = nullptr;
+ }
+
+ // Call transmit with kDataApdu and the selected channel number.
+ // Return the response sstatus code.
+ uint16_t transmit(uint8_t channel_number) {
+ std::vector<uint8_t> apdu = kDataApdu;
+ std::vector<uint8_t> response;
+
+ // Edit the channel number into the CLA header byte.
+ if (channel_number < 4) {
+ apdu[0] |= channel_number;
+ } else {
+ apdu[0] |= (channel_number - 4) | 0x40;
+ }
+
+ EXPECT_OK(secure_element_->transmit(apdu, &response));
+ EXPECT_GE(response.size(), 2u);
+ uint16_t status =
+ (response[response.size() - 2] << 8) | (response[response.size() - 1] << 0);
+
+ // When the command is successful the response
+ // must contain 256 bytes of data.
+ if (status == 0x9000) {
+ EXPECT_EQ(response.size(), 258);
+ }
+
+ return status;
+ }
+
+ std::shared_ptr<ISecureElement> secure_element_;
+ std::shared_ptr<MySecureElementCallback> secure_element_callback_;
};
+TEST_P(SecureElementAidl, init) {
+ // init(nullptr) shall fail.
+ EXPECT_ERR(secure_element_->init(nullptr));
+
+ // init with a valid callback pointer shall succeed.
+ EXPECT_OK(secure_element_->init(secure_element_callback_));
+ secure_element_callback_->expectCallbackHistory({true, true});
+}
+
+TEST_P(SecureElementAidl, reset) {
+ std::vector<uint8_t> basic_channel_response;
+ LogicalChannelResponse logical_channel_response;
+
+ // reset called after init shall succeed.
+ EXPECT_OK(secure_element_->openBasicChannel(kSelectableAid, 0x00, &basic_channel_response));
+ EXPECT_OK(secure_element_->openLogicalChannel(kSelectableAid, 0x00, &logical_channel_response));
+
+ EXPECT_OK(secure_element_->reset());
+ secure_element_callback_->expectCallbackHistory({true, false, true});
+
+ // All opened channels must be closed.
+ EXPECT_NE(transmit(0), 0x9000);
+ EXPECT_NE(transmit(logical_channel_response.channelNumber), 0x9000);
+}
+
TEST_P(SecureElementAidl, isCardPresent) {
bool res = false;
- EXPECT_OK(se->isCardPresent(&res));
+
+ // isCardPresent called after init shall succeed.
+ EXPECT_OK(secure_element_->isCardPresent(&res));
EXPECT_TRUE(res);
}
-TEST_P(SecureElementAidl, transmit) {
- LogicalChannelResponse response;
- EXPECT_OK(se->openLogicalChannel(kAndroidTestAid, 0x00, &response));
-
- EXPECT_GE(response.selectResponse.size(), 2u);
- EXPECT_GE(response.channelNumber, 1);
-
- std::vector<uint8_t> command = kDataApdu;
- command[0] |= response.channelNumber;
-
- std::vector<uint8_t> transmitResponse;
- EXPECT_OK(se->transmit(command, &transmitResponse));
-
- EXPECT_LE(transmitResponse.size(), 3);
- EXPECT_GE(transmitResponse.size(), 2);
- EXPECT_EQ(transmitResponse[transmitResponse.size() - 1], 0x00);
- EXPECT_EQ(transmitResponse[transmitResponse.size() - 2], 0x90);
-
- EXPECT_OK(se->closeChannel(response.channelNumber));
-}
-
-TEST_P(SecureElementAidl, openBasicChannel) {
- std::vector<uint8_t> response;
- auto status = se->openBasicChannel(kAndroidTestAid, 0x00, &response);
-
- if (!status.isOk()) {
- EXPECT_EQ(status.getServiceSpecificError(), ISecureElement::CHANNEL_NOT_AVAILABLE)
- << status.getDescription();
- return;
- }
-
- EXPECT_GE(response.size(), 2u);
- EXPECT_OK(se->closeChannel(0));
-}
-
TEST_P(SecureElementAidl, getAtr) {
std::vector<uint8_t> atr;
- EXPECT_OK(se->getAtr(&atr));
- if (atr.size() == 0) {
- return;
- }
+
+ // getAtr called after init shall succeed.
+ // The ATR has size between 0 and 32 bytes.
+ EXPECT_OK(secure_element_->getAtr(&atr));
EXPECT_LE(atr.size(), 32u);
- EXPECT_GE(atr.size(), 1u);
}
-TEST_P(SecureElementAidl, openCloseLogicalChannel) {
+TEST_P(SecureElementAidl, openBasicChannel) {
+ std::vector<uint8_t> response;
+
+ // openBasicChannel called with an invalid AID shall fail.
+ EXPECT_ERR(secure_element_->openBasicChannel(kNonSelectableAid, 0x00, &response));
+
+ // openBasicChannel called after init shall succeed.
+ // The response size must be larger than 2 bytes as it includes the
+ // status code.
+ EXPECT_OK(secure_element_->openBasicChannel(kSelectableAid, 0x00, &response));
+ EXPECT_GE(response.size(), 2u);
+
+ // tramsmit called on the basic channel should succeed.
+ EXPECT_EQ(transmit(0), 0x9000);
+
+ // openBasicChannel called a second time shall fail.
+ // The basic channel can only be opened once.
+ EXPECT_ERR(secure_element_->openBasicChannel(kSelectableAid, 0x00, &response));
+
+ // openBasicChannel called after closing the basic channel shall succeed.
+ EXPECT_OK(secure_element_->closeChannel(0));
+ EXPECT_OK(secure_element_->openBasicChannel(kSelectableAid, 0x00, &response));
+}
+
+TEST_P(SecureElementAidl, openLogicalChannel) {
LogicalChannelResponse response;
- EXPECT_OK(se->openLogicalChannel(kAndroidTestAid, 0x00, &response));
+
+ // openLogicalChannel called with an invalid AID shall fail.
+ EXPECT_ERR(secure_element_->openLogicalChannel(kNonSelectableAid, 0x00, &response));
+
+ // openLogicalChannel called after init shall succeed.
+ // The response size must be larger than 2 bytes as it includes the
+ // status code. The channel number must be in the range 1-19.
+ EXPECT_OK(secure_element_->openLogicalChannel(kSelectableAid, 0x00, &response));
EXPECT_GE(response.selectResponse.size(), 2u);
- EXPECT_GE(response.channelNumber, 1);
- EXPECT_OK(se->closeChannel(response.channelNumber));
+ EXPECT_GE(response.channelNumber, 1u);
+ EXPECT_LE(response.channelNumber, 19u);
+
+ // tramsmit called on the logical channel should succeed.
+ EXPECT_EQ(transmit(response.channelNumber), 0x9000);
}
-TEST_P(SecureElementAidl, openInvalidAid) {
- LogicalChannelResponse response;
- auto status = se->openLogicalChannel({0x42}, 0x00, &response);
- EXPECT_EQ(status.getServiceSpecificError(), ISecureElement::NO_SUCH_ELEMENT_ERROR)
- << status.getDescription();
+TEST_P(SecureElementAidl, closeChannel) {
+ std::vector<uint8_t> basic_channel_response;
+ LogicalChannelResponse logical_channel_response;
+
+ // closeChannel called on non-existing basic or logical channel is a no-op
+ // and shall succeed.
+ EXPECT_OK(secure_element_->closeChannel(0));
+ EXPECT_OK(secure_element_->closeChannel(1));
+
+ // closeChannel called on basic channel closes the basic channel.
+ EXPECT_OK(secure_element_->openBasicChannel(kSelectableAid, 0x00, &basic_channel_response));
+ EXPECT_OK(secure_element_->closeChannel(0));
+
+ // tramsmit called on the basic channel should fail.
+ EXPECT_NE(transmit(0), 0x9000);
+
+ // closeChannel called on logical channel closes the logical channel.
+ EXPECT_OK(secure_element_->openLogicalChannel(kSelectableAid, 0x00, &logical_channel_response));
+ EXPECT_OK(secure_element_->closeChannel(logical_channel_response.channelNumber));
+
+ // tramsmit called on the basic channel should fail.
+ EXPECT_NE(transmit(logical_channel_response.channelNumber), 0x9000);
}
-TEST_P(SecureElementAidl, Reset) {
- cb->expectCallbackHistory({true});
- EXPECT_OK(se->reset());
- cb->expectCallbackHistory({true, false, true});
+TEST_P(SecureElementAidl, transmit) {
+ std::vector<uint8_t> response;
+
+ // transmit called after init shall succeed.
+ // Note: no channel is opened for this test and the transmit
+ // response will have the status SW_LOGICAL_CHANNEL_NOT_SUPPORTED.
+ // The transmit response shall be larger than 2 bytes as it includes the
+ // status code.
+ EXPECT_OK(secure_element_->transmit(kDataApdu, &response));
+ EXPECT_GE(response.size(), 2u);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementAidl);
diff --git a/security/OWNERS b/security/OWNERS
index fbaf854..619139b 100644
--- a/security/OWNERS
+++ b/security/OWNERS
@@ -6,9 +6,11 @@
#
# This will get them auto-assigned to the on-call triage engineer, ensuring quickest response.
+ascull@google.com
drysdale@google.com
eranm@google.com
hasinitg@google.com
jbires@google.com
+sethmo@google.com
swillden@google.com
zeuthen@google.com
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index 837fc81..d401247 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -885,9 +885,9 @@
/**
* Tag::ATTESTATION_ID_SECOND_IMEI provides an additional IMEI of one of the radios on the
- * device to attested key generation/import operations. This field MUST be accompanied by
- * the Tag::ATTESTATION_ID_IMEI tag. It would only be used to convery a second IMEI the device
- * has, after Tag::ATTESTATION_ID_SECOND_IMEI has been used to convery the first IMEI.
+ * device to attested key generation/import operations. It should be used to convey an
+ * IMEI different to the one conveyed by the Tag::ATTESTATION_ID_IMEI tag. Like all other
+ * ID attestation flags, it may be included independently of other tags.
*
* If the device does not support ID attestation (or destroyAttestationIds() was previously
* called and the device can no longer attest its IDs), any key attestation request that
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index 88badc7..58b0645 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -31,8 +31,10 @@
"VtsHalTargetTestDefaults",
],
shared_libs: [
+ "libbinder",
"libbinder_ndk",
"libcrypto",
+ "packagemanager_aidl-cpp",
],
static_libs: [
"android.hardware.security.rkp-V3-ndk",
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 6c012fa..e9cbe10 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <android/content/pm/IPackageManagerNative.h>
#include <cppbor_parse.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
@@ -2048,6 +2049,29 @@
}
}
+// Check whether the given named feature is available.
+bool check_feature(const std::string& name) {
+ ::android::sp<::android::IServiceManager> sm(::android::defaultServiceManager());
+ ::android::sp<::android::IBinder> binder(sm->getService(::android::String16("package_native")));
+ if (binder == nullptr) {
+ GTEST_LOG_(ERROR) << "getService package_native failed";
+ return false;
+ }
+ ::android::sp<::android::content::pm::IPackageManagerNative> packageMgr =
+ ::android::interface_cast<::android::content::pm::IPackageManagerNative>(binder);
+ if (packageMgr == nullptr) {
+ GTEST_LOG_(ERROR) << "Cannot find package manager";
+ return false;
+ }
+ bool hasFeature = false;
+ auto status = packageMgr->hasSystemFeature(::android::String16(name.c_str()), 0, &hasFeature);
+ if (!status.isOk()) {
+ GTEST_LOG_(ERROR) << "hasSystemFeature('" << name << "') failed: " << status;
+ return false;
+ }
+ return hasFeature;
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 908eeab..fae9459 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -402,6 +402,7 @@
vector<uint8_t>* payload_value);
void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
void device_id_attestation_vsr_check(const ErrorCode& result);
+bool check_feature(const std::string& name);
AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index efd6fc7..1a8695b 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -66,6 +66,9 @@
static_libs: [
"android.hardware.security.rkp-V3-ndk",
],
+ whole_static_libs: [
+ "libhwtrust_cxx",
+ ],
shared_libs: [
"libbase",
"libbinder_ndk",
@@ -84,6 +87,7 @@
"android.hardware.security.rkp-V3-ndk",
"libgmock",
"libgtest_main",
+ "libkeymint_remote_prov_support",
],
defaults: [
"keymint_use_latest_hal_aidl_ndk_shared",
@@ -95,6 +99,5 @@
"libcrypto",
"libjsoncpp",
"libkeymaster_portable",
- "libkeymint_remote_prov_support",
],
}
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
index 1b94c62..79189a1 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -108,15 +108,6 @@
bytevec pubKey;
};
-/**
- * Validates the provided CBOR-encoded BCC, returning a vector of BccEntryData
- * structs containing the BCC entry contents. If an entry contains no firmware
- * digest, the corresponding BccEntryData.firmwareDigest will have length zero
- * (there's no way to distinguish between an empty and missing firmware digest,
- * which seems fine).
- */
-ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc);
-
struct JsonOutput {
static JsonOutput Ok(std::string json) { return {std::move(json), ""}; }
static JsonOutput Error(std::string error) { return {"", std::move(error)}; }
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 7e164fd..9620b6a 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -24,6 +24,7 @@
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
#include <android-base/properties.h>
#include <cppbor.h>
+#include <hwtrust/hwtrust.h>
#include <json/json.h>
#include <keymaster/km_openssl/ec_key.h>
#include <keymaster/km_openssl/ecdsa_operation.h>
@@ -289,134 +290,22 @@
return chain.encode();
}
-ErrMsgOr<bytevec> validatePayloadAndFetchPubKey(const cppbor::Map* payload) {
- const auto& issuer = payload->get(kBccPayloadIssuer);
- if (!issuer || !issuer->asTstr()) return "Issuer is not present or not a tstr.";
- const auto& subject = payload->get(kBccPayloadSubject);
- if (!subject || !subject->asTstr()) return "Subject is not present or not a tstr.";
- const auto& keyUsage = payload->get(kBccPayloadKeyUsage);
- if (!keyUsage || !keyUsage->asBstr()) return "Key usage is not present or not a bstr.";
- const auto& serializedKey = payload->get(kBccPayloadSubjPubKey);
- if (!serializedKey || !serializedKey->asBstr()) return "Key is not present or not a bstr.";
- return serializedKey->asBstr()->value();
-}
-
-ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
- const bytevec& signingCoseKey, const bytevec& aad) {
- if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
- return "Invalid COSE_Sign1";
- }
-
- const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
- const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
- const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
- const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
-
- if (!protectedParams || !unprotectedParams || !payload || !signature) {
- return "Invalid COSE_Sign1";
- }
-
- auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams);
- if (!parsedProtParams) {
- return errMsg + " when parsing protected params.";
- }
- if (!parsedProtParams->asMap()) {
- return "Protected params must be a map";
- }
-
- auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
- if (!algorithm || !algorithm->asInt() ||
- (algorithm->asInt()->value() != EDDSA && algorithm->asInt()->value() != ES256)) {
- return "Unsupported signature algorithm";
- }
-
- auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload);
- if (!parsedPayload) return payloadErrMsg + " when parsing key";
- if (!parsedPayload->asMap()) return "CWT must be a map";
- auto serializedKey = validatePayloadAndFetchPubKey(parsedPayload->asMap());
- if (!serializedKey) {
- return "CWT validation failed: " + serializedKey.moveMessage();
- }
-
- bool selfSigned = signingCoseKey.empty();
- bytevec signatureInput =
- cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
-
- if (algorithm->asInt()->value() == EDDSA) {
- auto key = CoseKey::parseEd25519(selfSigned ? *serializedKey : signingCoseKey);
-
- if (!key) return "Bad signing key: " + key.moveMessage();
-
- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
- key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
- return "Signature verification failed";
- }
- } else { // P256
- auto key = CoseKey::parseP256(selfSigned ? *serializedKey : signingCoseKey);
- if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() ||
- key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) {
- return "Bad signing key: " + key.moveMessage();
- }
- auto publicKey = key->getEcPublicKey();
- if (!publicKey) return publicKey.moveMessage();
-
- auto ecdsaDerSignature = ecdsaCoseSignatureToDer(signature->value());
- if (!ecdsaDerSignature) return ecdsaDerSignature.moveMessage();
-
- // convert public key to uncompressed form.
- publicKey->insert(publicKey->begin(), 0x04);
-
- if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), *ecdsaDerSignature)) {
- return "Signature verification failed";
- }
- }
-
- return serializedKey.moveValue();
-}
-
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
- if (!bcc || bcc->size() == 0) return "Invalid BCC";
-
+ auto encodedBcc = bcc->encode();
+ auto chain = hwtrust::DiceChain::verify(encodedBcc);
+ if (!chain.ok()) return chain.error().message();
+ auto keys = chain->cose_public_keys();
+ if (!keys.ok()) return keys.error().message();
std::vector<BccEntryData> result;
-
- const auto& devicePubKey = bcc->get(0);
- if (!devicePubKey->asMap()) return "Invalid device public key at the 1st entry in the BCC";
-
- bytevec prevKey;
-
- for (size_t i = 1; i < bcc->size(); ++i) {
- const cppbor::Array* entry = bcc->get(i)->asArray();
- if (!entry || entry->size() != kCoseSign1EntryCount) {
- return "Invalid BCC entry " + std::to_string(i) + ": " + prettyPrint(entry);
- }
- auto payload = verifyAndParseCoseSign1Cwt(entry, std::move(prevKey), bytevec{} /* AAD */);
- if (!payload) {
- return "Failed to verify entry " + std::to_string(i) + ": " + payload.moveMessage();
- }
-
- auto& certProtParms = entry->get(kCoseSign1ProtectedParams);
- if (!certProtParms || !certProtParms->asBstr()) return "Invalid prot params";
- auto [parsedProtParms, _, errMsg] = cppbor::parse(certProtParms->asBstr()->value());
- if (!parsedProtParms || !parsedProtParms->asMap()) return "Invalid prot params";
-
- result.push_back(BccEntryData{*payload});
-
- // This entry's public key is the signing key for the next entry.
- prevKey = payload.moveValue();
- if (i == 1) {
- auto [parsedRootKey, _, errMsg] = cppbor::parse(prevKey);
- if (!parsedRootKey || !parsedRootKey->asMap()) return "Invalid payload entry in BCC.";
- if (*parsedRootKey != *devicePubKey) {
- return "Device public key doesn't match BCC root.";
- }
- }
+ for (auto& key : *keys) {
+ result.push_back({std::move(key)});
}
-
return result;
}
JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name, const cppbor::Array& csr) {
const std::string kFingerprintProp = "ro.build.fingerprint";
+ const std::string kSerialNoProp = "ro.serialno";
if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
return JsonOutput::Error("Unable to read build fingerprint");
@@ -441,6 +330,7 @@
Json::Value json(Json::objectValue);
json["name"] = instance_name;
json["build_fingerprint"] = ::android::base::GetProperty(kFingerprintProp, /*default=*/"");
+ json["serialno"] = ::android::base::GetProperty(kSerialNoProp, /*default=*/"");
json["csr"] = base64.data(); // Boring writes a NUL-terminated c-string
Json::StreamWriterBuilder factory;
@@ -683,9 +573,6 @@
if (!bccContents) {
return bccContents.message() + "\n" + prettyPrint(bcc.get());
}
- if (bccContents->size() == 0U) {
- return "The BCC is empty. It must contain at least one entry.";
- }
auto deviceInfoResult =
parseAndValidateDeviceInfo(deviceInfo.deviceInfo, provisionable, isFactory);
@@ -977,9 +864,6 @@
if (!diceContents) {
return diceContents.message() + "\n" + prettyPrint(diceCertChain);
}
- if (diceContents->size() == 0U) {
- return "The DICE chain is empty. It must contain at least one entry.";
- }
auto& udsPub = diceContents->back().pubKey;
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp
index 0250cd6..eaaba45 100644
--- a/security/keymint/support/remote_prov_utils_test.cpp
+++ b/security/keymint/support/remote_prov_utils_test.cpp
@@ -191,7 +191,8 @@
std::string expected = R"({"build_fingerprint":")" +
::android::base::GetProperty("ro.build.fingerprint", /*default=*/"") +
- R"(","csr":"gQE=","name":"test"})";
+ R"(","csr":"gQE=","name":"test","serialno":")" +
+ ::android::base::GetProperty("ro.serialno", /*default=*/"") + R"("})";
ASSERT_EQ(json, expected);
}
diff --git a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index ff710f1..75990da 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -335,7 +335,7 @@
* UdsCerts,
* DiceCertChain,
* SignedData<[
- * challenge: bstr .size (32..64), ; Provided by the method parameters
+ * challenge: bstr .size (16..64), ; Provided by the method parameters
* bstr .cbor T,
* ]>,
* ]
diff --git a/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
index 3f699bc..bfe8417 100644
--- a/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -134,7 +134,7 @@
* ]
*
* SignedMacAad = [
- * challenge : bstr .size (32..64), ; Size between 32 - 64
+ * challenge : bstr .size (16..64), ; Size between 16 - 64
* ; bytes inclusive
* VerifiedDeviceInfo,
* tag: bstr ; This is the tag from COSE_Mac0 of
diff --git a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index bbda56d..573f10b 100644
--- a/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/rkp/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -404,6 +404,7 @@
protected:
void SetUp() override {
CertificateRequestTestBase::SetUp();
+ ASSERT_FALSE(HasFatalFailure());
if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
bytevec keysToSignMac;
@@ -689,6 +690,7 @@
class CertificateRequestV2Test : public CertificateRequestTestBase {
void SetUp() override {
CertificateRequestTestBase::SetUp();
+ ASSERT_FALSE(HasFatalFailure());
if (rpcHardwareInfo.versionNumber < VERSION_WITHOUT_TEST_MODE) {
GTEST_SKIP() << "This test case only applies to RKP v3 and above. "
diff --git a/tetheroffload/aidl/Android.bp b/tetheroffload/aidl/Android.bp
new file mode 100644
index 0000000..1d80586
--- /dev/null
+++ b/tetheroffload/aidl/Android.bp
@@ -0,0 +1,27 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.hardware.tetheroffload",
+ vendor_available: true,
+ srcs: ["android/hardware/tetheroffload/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ apex_available: [
+ "com.android.tethering",
+ ],
+ min_sdk_version: "30",
+ enabled: true,
+ },
+ ndk: {
+ apps_enabled: false,
+ },
+ },
+ frozen: false,
+}
diff --git a/tetheroffload/aidl/TEST_MAPPING b/tetheroffload/aidl/TEST_MAPPING
new file mode 100644
index 0000000..c6d4c07
--- /dev/null
+++ b/tetheroffload/aidl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit" : [
+ {
+ "name": "VtsHalTetheroffloadTargetTest"
+ }
+ ]
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ForwardedStats.aidl
similarity index 89%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ForwardedStats.aidl
index 336f9b5..493a698 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ForwardedStats.aidl
@@ -31,12 +31,9 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.tetheroffload;
+@VintfStability
+parcelable ForwardedStats {
+ long rxBytes;
+ long txBytes;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IOffload.aidl
similarity index 69%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IOffload.aidl
index 336f9b5..9a58b1f 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IOffload.aidl
@@ -31,12 +31,16 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.tetheroffload;
+@VintfStability
+interface IOffload {
+ void initOffload(in ParcelFileDescriptor fd1, in ParcelFileDescriptor fd2, in android.hardware.tetheroffload.ITetheringOffloadCallback cb);
+ void stopOffload();
+ void setLocalPrefixes(in String[] prefixes);
+ android.hardware.tetheroffload.ForwardedStats getForwardedStats(in String upstream);
+ void setDataWarningAndLimit(in String upstream, in long warningBytes, in long limitBytes);
+ void setUpstreamParameters(in String iface, in String v4Addr, in String v4Gw, in String[] v6Gws);
+ void addDownstream(in String iface, in String prefix);
+ void removeDownstream(in String iface, in String prefix);
+ const int ERROR_CODE_UNUSED = 0;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IPv4AddrPortPair.aidl
similarity index 89%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IPv4AddrPortPair.aidl
index 336f9b5..2b42f0c 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IPv4AddrPortPair.aidl
@@ -31,12 +31,9 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.tetheroffload;
+@VintfStability
+parcelable IPv4AddrPortPair {
+ String addr;
+ int port;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl
similarity index 85%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl
index 336f9b5..4eb7d04 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl
@@ -31,12 +31,9 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.tetheroffload;
+@VintfStability
+interface ITetheringOffloadCallback {
+ oneway void onEvent(in android.hardware.tetheroffload.OffloadCallbackEvent event);
+ oneway void updateTimeout(in android.hardware.tetheroffload.NatTimeoutUpdate params);
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NatTimeoutUpdate.aidl
similarity index 86%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NatTimeoutUpdate.aidl
index 336f9b5..9eddaa2 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NatTimeoutUpdate.aidl
@@ -31,12 +31,10 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.tetheroffload;
+@VintfStability
+parcelable NatTimeoutUpdate {
+ android.hardware.tetheroffload.IPv4AddrPortPair src;
+ android.hardware.tetheroffload.IPv4AddrPortPair dst;
+ android.hardware.tetheroffload.NetworkProtocol proto;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NetworkProtocol.aidl
similarity index 91%
rename from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
rename to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NetworkProtocol.aidl
index 336f9b5..52bd2a6 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NetworkProtocol.aidl
@@ -31,12 +31,9 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
+package android.hardware.tetheroffload;
@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+enum NetworkProtocol {
+ TCP = 6,
+ UDP = 17,
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/OffloadCallbackEvent.aidl
similarity index 86%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/OffloadCallbackEvent.aidl
index 336f9b5..026e18e 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/OffloadCallbackEvent.aidl
@@ -31,12 +31,13 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
+package android.hardware.tetheroffload;
@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+enum OffloadCallbackEvent {
+ OFFLOAD_STARTED = 1,
+ OFFLOAD_STOPPED_ERROR = 2,
+ OFFLOAD_STOPPED_UNSUPPORTED = 3,
+ OFFLOAD_SUPPORT_AVAILABLE = 4,
+ OFFLOAD_STOPPED_LIMIT_REACHED = 5,
+ OFFLOAD_WARNING_REACHED = 6,
}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/ForwardedStats.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/ForwardedStats.aidl
new file mode 100644
index 0000000..d2fe49b
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/ForwardedStats.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+@VintfStability
+parcelable ForwardedStats {
+ /**
+ * Tx/Rx forwarded bytes
+ */
+ long rxBytes;
+ long txBytes;
+}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/IOffload.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/IOffload.aidl
new file mode 100644
index 0000000..30b2c8d
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/IOffload.aidl
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+import android.hardware.tetheroffload.ForwardedStats;
+import android.hardware.tetheroffload.ITetheringOffloadCallback;
+
+/**
+ * Interface used to control tethering offload.
+ */
+@VintfStability
+interface IOffload {
+ /**
+ * Error code for all {@code ServiceSpecificException}s thrown by this interface.
+ */
+ const int ERROR_CODE_UNUSED = 0;
+
+ /**
+ * Indicates intent to start offload for tethering in immediate future.
+ *
+ * This API must be called exactly once the first time that Tethering is requested by
+ * the user.
+ *
+ * If this API is called multiple times without first calling stopOffload, then the subsequent
+ * calls must fail without changing the state of the server.
+ *
+ * If for some reason, the hardware is currently unable to support offload, this call must fail.
+ *
+ * @param fd1 A file descriptor bound to the following netlink groups
+ * (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+ * @param fd2 A file descriptor bound to the following netlink groups
+ * (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+ * @param cb Assuming success, this callback must provide unsolicited updates of offload status.
+ * It is assumed to be valid until stopOffload is called.
+ *
+ * @throws:
+ * - EX_ILLEGAL_ARGUMENT if any file descriptors are invalid.
+ * - EX_ILLEGAL_STATE if this method previously succeeded and stopOffload() was not
+ * later called.
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ *
+ * Remarks: Initializing offload does not imply that any upstreams or downstreams have yet been,
+ * or even will be, chosen. This API is symmetrical with stopOffload.
+ */
+ void initOffload(in ParcelFileDescriptor fd1, in ParcelFileDescriptor fd2,
+ in ITetheringOffloadCallback cb);
+
+ /**
+ * Indicate desire to tear down all tethering offload.
+ *
+ * Called after tethering is no longer requested by the user. Any remaining offload must
+ * be subsequently torn down by the management process. Upon success, the callback registered
+ * in initOffload must be released, and offload must be stopped.
+ *
+ * @throws:
+ * - EX_ILLEGAL_STATE if initOffload() was not called, or if stopOffload() was already
+ * called.
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ *
+ * Remarks: Statistics must be reset by this API.
+ */
+ void stopOffload();
+
+ /**
+ * Instruct management process not to forward traffic destined to or from the specified
+ * prefixes.
+ *
+ * This API may only be called after initOffload and before stopOffload.
+ *
+ * @param prefixes List containing fully specified prefixes. For e.g. 192.168.1.0/24
+ * or 2001:4860:684::/64
+ *
+ * @throws:
+ * - EX_ILLEGAL_ARGUMENT if the IP prefixes are invalid.
+ * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method
+ * is called after stopOffload().
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ *
+ * Remarks: This list overrides any previously specified list
+ */
+ void setLocalPrefixes(in String[] prefixes);
+
+ /**
+ * Query offloaded traffic statistics forwarded to an upstream address.
+ *
+ * Return statistics that have transpired since the last query. This would include
+ * statistics from all offloaded downstream tether interfaces that have been forwarded to this
+ * upstream interface. After returning the statistics, the counters are reset to zero.
+ *
+ * Only offloaded statistics must be returned by this API, software stats must not be
+ * returned.
+ *
+ * @param upstream Upstream interface on which traffic exited/entered
+ *
+ * @return ForwardedStats depicting the received and transmitted bytes
+ *
+ * @throws:
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ */
+ ForwardedStats getForwardedStats(in String upstream);
+
+ /**
+ * Instruct hardware to send callbacks, and possibly stop offload, after certain number of bytes
+ * have been transferred in either direction on this upstream interface.
+ *
+ * The specified quota bytes must be applied to all traffic on the given upstream interface.
+ * This includes hardware forwarded traffic, software forwarded traffic, and AP-originated
+ * traffic. IPv4 and IPv6 traffic both count towards the same quota. IP headers are included
+ * in the byte count quota, but, link-layer headers are not.
+ *
+ * This API may only be called while offload is occurring on this upstream. The hardware
+ * management process MUST NOT store the values when offload is not started and apply them
+ * once offload is started. This is because the quota values would likely become stale over
+ * time and would not reflect any new traffic that has occurred.
+ *
+ * The specified quota bytes MUST replace any previous quotas set by
+ * {@code setDataWarningAndLimit} specified on the same interface. It may be interpreted as
+ * "tell me when either <warningBytes> or <limitBytes> bytes have been transferred
+ * (in either direction), and stop offload when <limitBytes> bytes have been transferred,
+ * starting now and counting from zero on <upstream>."
+ *
+ * Once the {@code warningBytes} is reached, the callback registered in initOffload must be
+ * called with {@code OFFLOAD_WARNING_REACHED} to indicate this event. Once the event fires
+ * for this upstream, no further {@code OFFLOAD_WARNING_REACHED} event will be fired for this
+ * upstream unless this method is called again with the same interface. Note that there is
+ * no need to call initOffload again to resume offload if stopOffload was not called by the
+ * client.
+ *
+ * Similarly, Once the {@code limitBytes} is reached, the callback registered in initOffload
+ * must be called with {@code OFFLOAD_STOPPED_LIMIT_REACHED} to indicate this event. Once
+ * the event fires for this upstream, no further {@code OFFLOAD_STOPPED_LIMIT_REACHED}
+ * event will be fired for this upstream unless this method is called again with the same
+ * interface. However, unlike {@code warningBytes}, when {@code limitBytes} is reached,
+ * all offload must be stopped. If offload is desired again, the hardware management
+ * process must be completely reprogrammed by calling setUpstreamParameters and
+ * addDownstream again.
+ *
+ * Note that {@code warningBytes} must always be less than or equal to {@code limitBytes},
+ * when {@code warningBytes} is reached, {@code limitBytes} may still valid unless this method
+ * is called again with the same interface.
+ *
+ * @param upstream Upstream interface name that quota must apply to.
+ * @param warningBytes The quota of warning, defined as the number of bytes, starting from
+ * zero and counting from now.
+ * @param limitBytes The quota of limit, defined as the number of bytes, starting from zero
+ * and counting from now.
+ *
+ * @throws:
+ * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid upstream
+ * or negative number of bytes).
+ * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method
+ * is called after stopOffload().
+ * - EX_UNSUPPORTED_OPERATION if it is not supported.
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ */
+ void setDataWarningAndLimit(in String upstream, in long warningBytes, in long limitBytes);
+
+ /**
+ * Instruct hardware to start forwarding traffic to the specified upstream.
+ *
+ * When iface, v4Addr, and v4Gw are all non-null, the management process may begin forwarding
+ * any currently configured or future configured IPv4 downstreams to this upstream interface.
+ *
+ * If any of the previously three mentioned parameters are null, then any current IPv4 offload
+ * must be stopped.
+ *
+ * When iface and v6Gws are both non-null, and in the case of v6Gws, are not empty, the
+ * management process may begin forwarding any currently configured or future configured IPv6
+ * downstreams to this upstream interface.
+ *
+ * If either of the two above parameters are null, or no V6 Gateways are provided, then IPv6
+ * offload must be stopped.
+ *
+ * This API may only be called after initOffload and before stopOffload.
+ *
+ * @param iface Upstream interface name. Note that only one is needed because IPv4 and IPv6
+ * interfaces cannot be different (only known that this can occur during software
+ * xlat, which cannot be offloaded through hardware anyways). If the iface is
+ * null, offload must be stopped.
+ * @param v4Addr The local IPv4 address assigned to the provided upstream interface, i.e. the
+ * IPv4 address the packets are NATed to. For e.g. 192.168.0.12.
+ * @param v4Gw The IPv4 address of the IPv4 gateway on the upstream interface.
+ * For e.g. 192.168.1.1
+ * @param v6Gws A list of IPv6 addresses (for e.g. fe80::97be:9de7:b24b:9194) for possible IPv6
+ * gateways on the upstream interface.
+ *
+ * @throws:
+ * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid upstream
+ * or IP addresses).
+ * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method
+ * is called after stopOffload().
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ *
+ * Remarks: This overrides any previously configured parameters.
+ */
+ void setUpstreamParameters(
+ in String iface, in String v4Addr, in String v4Gw, in String[] v6Gws);
+
+ /**
+ * Configure a downstream interface and prefix in the hardware management process that may be
+ * forwarded.
+ *
+ * The prefix may be an IPv4 or an IPv6 prefix to signify which family can be offloaded from
+ * the specified tether interface. The list of IPv4 and IPv6 downstreams that are configured
+ * may differ.
+ *
+ * If the given protocol, as determined by the prefix, has an upstream set,
+ * the hardware may begin forwarding traffic between the upstream and any devices on the
+ * downstream interface that have IP addresses within the specified prefix. Other traffic from
+ * the same downstream interfaces is unaffected and must be forwarded if and only if it was
+ * already being forwarded.
+ *
+ * If no upstream is currently configured, then these downstream interface and prefixes must be
+ * preserved so that offload may begin in the future when an upstream is set.
+ *
+ * This API does not replace any previously configured downstreams and any such downstreams must
+ * be explicitly removed by calling removeDownstream.
+ *
+ * This API may only be called after initOffload and before stopOffload.
+ *
+ * @param iface Downstream interface
+ * @param prefix Downstream prefix depicting addresses that may be offloaded.
+ * For e.g. 192.168.1.0/24 or 2001:4860:684::/64)
+ *
+ * @throws:
+ * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid downstream
+ * or IP prefix).
+ * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method
+ * is called after stopOffload().
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ *
+ * Remarks: The hardware management process may fail this call in a normal situation. This can
+ * happen because the hardware cannot support the current number of prefixes, the
+ * hardware cannot support concurrent offload on multiple interfaces, the hardware
+ * cannot currently support offload on the tether interface for some reason, or any
+ * other dynamic configuration issues which may occur. In this case,
+ * traffic must remain unaffected and must be forwarded if and only if it was already
+ * being forwarded.
+ */
+ void addDownstream(in String iface, in String prefix);
+
+ /**
+ * Remove a downstream prefix that may be forwarded from the hardware management process.
+ *
+ * The prefix may be an IPv4 or an IPv6 prefix. If it was not previously configured using
+ * addDownstream, then this must be a no-op.
+ *
+ * This API may only be called after initOffload and before stopOffload.
+ *
+ * @param iface Downstream interface
+ * @param prefix Downstream prefix depicting address that must no longer be offloaded
+ * For e.g. 192.168.1.0/24 or 2001:4860:684::/64)
+ *
+ * @throws:
+ * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid downstream
+ * or IP prefix).
+ * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method
+ * is called after stopOffload().
+ * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the
+ * error.
+ */
+ void removeDownstream(in String iface, in String prefix);
+}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/IPv4AddrPortPair.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/IPv4AddrPortPair.aidl
new file mode 100644
index 0000000..16d7d85
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/IPv4AddrPortPair.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+@VintfStability
+parcelable IPv4AddrPortPair {
+ /**
+ * IPv4 Address and Port
+ */
+ String addr;
+ int port;
+}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl
new file mode 100644
index 0000000..5ee819b
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+import android.hardware.tetheroffload.NatTimeoutUpdate;
+import android.hardware.tetheroffload.OffloadCallbackEvent;
+
+/**
+ * Callback providing information about status of hardware management process
+ * as well as providing a way to keep offloaded connections from timing out.
+ */
+@VintfStability
+oneway interface ITetheringOffloadCallback {
+ /**
+ * Called when an asynchronous event is generated by the hardware
+ * management process.
+ */
+ void onEvent(in OffloadCallbackEvent event);
+
+ /**
+ * Provide a way for the management process to request that a connections
+ * timeout be updated in kernel.
+ *
+ * This is necessary to ensure that offloaded traffic is not cleaned up
+ * by the kernel connection tracking module for IPv4.
+ */
+ void updateTimeout(in NatTimeoutUpdate params);
+}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/NatTimeoutUpdate.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/NatTimeoutUpdate.aidl
new file mode 100644
index 0000000..50805ef
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/NatTimeoutUpdate.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+import android.hardware.tetheroffload.IPv4AddrPortPair;
+import android.hardware.tetheroffload.NetworkProtocol;
+
+@VintfStability
+parcelable NatTimeoutUpdate {
+ IPv4AddrPortPair src;
+ IPv4AddrPortPair dst;
+ NetworkProtocol proto;
+}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/NetworkProtocol.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/NetworkProtocol.aidl
new file mode 100644
index 0000000..cc4f7ac
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/NetworkProtocol.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+@VintfStability
+@Backing(type="int")
+enum NetworkProtocol {
+ TCP = 6,
+ UDP = 17,
+}
diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/OffloadCallbackEvent.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/OffloadCallbackEvent.aidl
new file mode 100644
index 0000000..a95f674
--- /dev/null
+++ b/tetheroffload/aidl/android/hardware/tetheroffload/OffloadCallbackEvent.aidl
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.tetheroffload;
+
+@VintfStability
+@Backing(type="int")
+enum OffloadCallbackEvent {
+ /**
+ * Indicate that a working configuration has been programmed and the
+ * hardware management process has begun forwarding traffic.
+ */
+ OFFLOAD_STARTED = 1,
+ /**
+ * Indicate that an error has occurred which has disrupted hardware
+ * acceleration. Software routing may still be attempted; however,
+ * statistics may be temporarily unavailable. Statistics may be recovered
+ * after OFFLOAD_SUPPORT_AVAILABLE event is fired.
+ */
+ OFFLOAD_STOPPED_ERROR = 2,
+ /**
+ * Indicate that the device has moved to a RAT on which hardware
+ * acceleration is not supported. Subsequent calls to setUpstreamParameters
+ * and add/removeDownstream will likely fail and cannot be presumed to be
+ * saved inside of the hardware management process. Upon receiving
+ * OFFLOAD_SUPPORT_AVAILABLE, the client may reprogram the hardware
+ * management process to begin offload again.
+ */
+ OFFLOAD_STOPPED_UNSUPPORTED = 3,
+ /**
+ * Indicate that the hardware management process is willing and able to
+ * provide support for hardware acceleration at this time. If applicable,
+ * the client may query for statistics. If offload is desired, the client
+ * must reprogram the hardware management process.
+ */
+ OFFLOAD_SUPPORT_AVAILABLE = 4,
+ /**
+ * Hardware acceleration is no longer in effect and must be reprogrammed
+ * in order to resume. This event is fired when the limit, applied in
+ * setDataLimit, has expired. It is recommended that the client query for
+ * statistics immediately after receiving this event.
+ */
+ OFFLOAD_STOPPED_LIMIT_REACHED = 5,
+ /**
+ * This event is fired when the quota, applied in setDataWarning, has expired. It is
+ * recommended that the client query for statistics immediately after receiving this event.
+ * Any offloaded traffic will continue to be offloaded until offload is stopped or
+ * OFFLOAD_STOPPED_LIMIT_REACHED is sent.
+ */
+ OFFLOAD_WARNING_REACHED = 6,
+}
diff --git a/tetheroffload/aidl/default/Android.bp b/tetheroffload/aidl/default/Android.bp
new file mode 100644
index 0000000..8f0739c
--- /dev/null
+++ b/tetheroffload/aidl/default/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+ name: "android.hardware.tetheroffload-service.example",
+ relative_install_path: "hw",
+ init_rc: ["tetheroffload-example.rc"],
+ vintf_fragments: ["tetheroffload-example.xml"],
+ vendor: true,
+ shared_libs: [
+ "android.hardware.tetheroffload-V1-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "libutils",
+ ],
+ srcs: [
+ "main.cpp",
+ "Offload.cpp",
+ ],
+}
diff --git a/tetheroffload/aidl/default/Offload.cpp b/tetheroffload/aidl/default/Offload.cpp
new file mode 100644
index 0000000..8aa6916
--- /dev/null
+++ b/tetheroffload/aidl/default/Offload.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <numeric>
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <netdb.h>
+
+#include "Offload.h"
+
+namespace aidl::android::hardware::tetheroffload::impl::example {
+
+using ::android::base::Join;
+
+ndk::ScopedAStatus Offload::addDownstream(const std::string& in_iface,
+ const std::string& in_prefix) {
+ LOG(VERBOSE) << __func__ << " Interface: " << in_iface << ", Prefix: " << in_prefix;
+ if (!isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized");
+ }
+ if (!isValidInterface(in_iface)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid interface name");
+ }
+ if (!isValidIpPrefix(in_prefix)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid IP prefix");
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::getForwardedStats(const std::string& in_upstream,
+ ForwardedStats* _aidl_return) {
+ LOG(VERBOSE) << __func__ << " Upstream: " << in_upstream;
+ ForwardedStats stats;
+ stats.rxBytes = 0;
+ stats.txBytes = 0;
+ *_aidl_return = std::move(stats);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::initOffload(const ndk::ScopedFileDescriptor& in_fd1,
+ const ndk::ScopedFileDescriptor& in_fd2,
+ const std::shared_ptr<ITetheringOffloadCallback>& in_cb) {
+ LOG(VERBOSE) << __func__ << " FileDescriptor1: " << std::to_string(in_fd1.get())
+ << ", FileDescriptor2: " << std::to_string(in_fd2.get())
+ << ", ITetheringOffloadCallback: " << in_cb;
+ if (isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL already initialized");
+ }
+ int fd1 = in_fd1.get();
+ int fd2 = in_fd2.get();
+ if (fd1 < 0 || fd2 < 0) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid file descriptors");
+ }
+ mFd1 = ndk::ScopedFileDescriptor(dup(fd1));
+ mFd2 = ndk::ScopedFileDescriptor(dup(fd2));
+ mInitialized = true;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::removeDownstream(const std::string& in_iface,
+ const std::string& in_prefix) {
+ LOG(VERBOSE) << __func__ << " Interface: " << in_iface << ", Prefix: " << in_prefix;
+ if (!isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized");
+ }
+ if (!isValidIpPrefix(in_prefix)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid IP prefix");
+ }
+ if (!isValidInterface(in_iface)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid interface name");
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::setDataWarningAndLimit(const std::string& in_upstream,
+ int64_t in_warningBytes, int64_t in_limitBytes) {
+ LOG(VERBOSE) << __func__ << " Upstream: " << in_upstream
+ << ", WarningBytes: " << in_warningBytes << ", LimitBytes: " << in_limitBytes;
+ if (!isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized");
+ }
+ if (!isValidInterface(in_upstream)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid interface name");
+ }
+ if (in_warningBytes < 0 || in_limitBytes < 0) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Threshold must be non-negative");
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::setLocalPrefixes(const std::vector<std::string>& in_prefixes) {
+ LOG(VERBOSE) << __func__ << " Prefixes: " << Join(in_prefixes, ',');
+ if (!isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized");
+ }
+ if (in_prefixes.empty()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "No IP prefix");
+ }
+ for (std::string prefix : in_prefixes) {
+ if (!isValidIpPrefix(prefix)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid IP prefix");
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::setUpstreamParameters(const std::string& in_iface,
+ const std::string& in_v4Addr,
+ const std::string& in_v4Gw,
+ const std::vector<std::string>& in_v6Gws) {
+ LOG(VERBOSE) << __func__ << " Interface: " << in_iface << ", IPv4Address: " << in_v4Addr
+ << ", IPv4Gateway: " << in_v4Gw << ", IPv6Gateways: " << Join(in_v6Gws, ',');
+ if (!isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized");
+ }
+ if (!isValidInterface(in_iface)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid interface name");
+ }
+ if (in_v4Addr.empty() && in_v4Gw.empty() && in_v6Gws.empty()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "No upstream IP address");
+ }
+ if (!in_v4Addr.empty() && !in_v4Gw.empty()) {
+ if (!isValidIpv4Address(in_v4Addr) || !isValidIpv4Address(in_v4Gw)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid IP address");
+ }
+ }
+ for (std::string ip : in_v6Gws) {
+ if (!isValidIpv6Address(ip)) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
+ "Invalid IP address");
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Offload::stopOffload() {
+ LOG(VERBOSE) << __func__;
+ if (!isInitialized()) {
+ return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
+ EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized");
+ }
+ mInitialized = false;
+ return ndk::ScopedAStatus::ok();
+};
+
+bool Offload::isInitialized() {
+ return (mInitialized == true);
+}
+
+bool Offload::isValidInterface(const std::string& iface) {
+ return !iface.empty() && iface != "invalid";
+}
+
+bool Offload::isValidIpv4Address(const std::string& repr) {
+ return validateIpAddressOrPrefix(repr, AF_INET, false);
+}
+
+bool Offload::isValidIpv4Prefix(const std::string& repr) {
+ return validateIpAddressOrPrefix(repr, AF_INET, true);
+}
+
+bool Offload::isValidIpv6Address(const std::string& repr) {
+ return validateIpAddressOrPrefix(repr, AF_INET6, false);
+}
+
+bool Offload::isValidIpv6Prefix(const std::string& repr) {
+ return validateIpAddressOrPrefix(repr, AF_INET6, true);
+}
+
+bool Offload::isValidIpAddress(const std::string& repr) {
+ return isValidIpv4Address(repr) || isValidIpv6Address(repr);
+}
+
+bool Offload::isValidIpPrefix(const std::string& repr) {
+ return isValidIpv4Prefix(repr) || isValidIpv6Prefix(repr);
+}
+
+// Refer to libnetdutils's IPAddress and IPPrefix classes.
+// Can't use them directly because libnetdutils is not "vendor_available".
+bool Offload::validateIpAddressOrPrefix(const std::string& repr, const int expectedFamily,
+ const bool isPrefix) {
+ const addrinfo hints = {
+ .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV,
+ };
+ addrinfo* res;
+ size_t index = repr.find('/');
+ if (isPrefix && index == std::string::npos) return false;
+
+ // Parse the IP address.
+ const std::string ipAddress = isPrefix ? repr.substr(0, index) : repr;
+ const int ret = getaddrinfo(ipAddress.c_str(), nullptr, &hints, &res);
+ if (ret != 0) return false;
+
+ // Check the address family.
+ int family = res[0].ai_family;
+ freeaddrinfo(res);
+ if (family != expectedFamily) return false;
+ if (!isPrefix) return true;
+
+ // Parse the prefix length.
+ const char* prefixString = repr.c_str() + index + 1;
+ if (!isdigit(*prefixString)) return false;
+ char* endptr;
+ unsigned long prefixlen = strtoul(prefixString, &endptr, 10);
+ if (*endptr != '\0') return false;
+
+ uint8_t maxlen = (family == AF_INET) ? 32 : 128;
+ if (prefixlen > maxlen) return false;
+
+ return true;
+}
+
+} // namespace aidl::android::hardware::tetheroffload::impl::example
diff --git a/tetheroffload/aidl/default/Offload.h b/tetheroffload/aidl/default/Offload.h
new file mode 100644
index 0000000..a1f6bce
--- /dev/null
+++ b/tetheroffload/aidl/default/Offload.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/tetheroffload/BnOffload.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace tetheroffload {
+namespace impl {
+namespace example {
+
+using aidl::android::hardware::tetheroffload::ForwardedStats;
+using aidl::android::hardware::tetheroffload::ITetheringOffloadCallback;
+
+class Offload : public BnOffload {
+ public:
+ ndk::ScopedAStatus addDownstream(const std::string& in_iface,
+ const std::string& in_prefix) override;
+ ndk::ScopedAStatus getForwardedStats(const std::string& in_upstream,
+ ForwardedStats* _aidl_return) override;
+ ndk::ScopedAStatus initOffload(
+ const ndk::ScopedFileDescriptor& in_fd1, const ndk::ScopedFileDescriptor& in_fd2,
+ const std::shared_ptr<ITetheringOffloadCallback>& in_cb) override;
+ ndk::ScopedAStatus removeDownstream(const std::string& in_iface,
+ const std::string& in_prefix) override;
+ ndk::ScopedAStatus setDataWarningAndLimit(const std::string& in_upstream,
+ int64_t in_warningBytes,
+ int64_t in_limitBytes) override;
+ ndk::ScopedAStatus setLocalPrefixes(const std::vector<std::string>& in_prefixes) override;
+ ndk::ScopedAStatus setUpstreamParameters(const std::string& in_iface,
+ const std::string& in_v4Addr,
+ const std::string& in_v4Gw,
+ const std::vector<std::string>& in_v6Gws) override;
+ ndk::ScopedAStatus stopOffload() override;
+
+ private:
+ bool isInitialized();
+ bool isValidInterface(const std::string& iface);
+ bool isValidIpv4Address(const std::string& repr);
+ bool isValidIpv4Prefix(const std::string& repr);
+ bool isValidIpv6Address(const std::string& repr);
+ bool isValidIpv6Prefix(const std::string& repr);
+ bool isValidIpAddress(const std::string& repr);
+ bool isValidIpPrefix(const std::string& repr);
+ bool validateIpAddressOrPrefix(const std::string& repr, const int expectedFamily,
+ const bool isPrefix);
+
+ bool mInitialized = false;
+ ndk::ScopedFileDescriptor mFd1;
+ ndk::ScopedFileDescriptor mFd2;
+};
+
+} // namespace example
+} // namespace impl
+} // namespace tetheroffload
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/tetheroffload/aidl/default/main.cpp b/tetheroffload/aidl/default/main.cpp
new file mode 100644
index 0000000..6633630
--- /dev/null
+++ b/tetheroffload/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Offload.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::tetheroffload::impl::example::Offload;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<Offload> offload = ndk::SharedRefBase::make<Offload>();
+
+ binder_status_t status = AServiceManager_addService(
+ offload->asBinder().get(), Offload::makeServiceName("default").c_str());
+ CHECK_EQ(status, STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/tetheroffload/aidl/default/tetheroffload-example.rc b/tetheroffload/aidl/default/tetheroffload-example.rc
new file mode 100644
index 0000000..46cda61
--- /dev/null
+++ b/tetheroffload/aidl/default/tetheroffload-example.rc
@@ -0,0 +1,4 @@
+service vendor.tetheroffload-example /vendor/bin/hw/android.hardware.tetheroffload-service.example
+ class hal
+ user nobody
+ group nobody
diff --git a/tetheroffload/aidl/default/tetheroffload-example.xml b/tetheroffload/aidl/default/tetheroffload-example.xml
new file mode 100644
index 0000000..9fe83f6
--- /dev/null
+++ b/tetheroffload/aidl/default/tetheroffload-example.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.tetheroffload</name>
+ <version>1</version>
+ <fqname>IOffload/default</fqname>
+ </hal>
+</manifest>
diff --git a/tetheroffload/aidl/vts/functional/Android.bp b/tetheroffload/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..74edab0
--- /dev/null
+++ b/tetheroffload/aidl/vts/functional/Android.bp
@@ -0,0 +1,25 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "VtsHalTetheroffloadTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalTetheroffloadTargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.tetheroffload-V1-ndk",
+ "libgmock_ndk",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/tetheroffload/aidl/vts/functional/VtsHalTetheroffloadTargetTest.cpp b/tetheroffload/aidl/vts/functional/VtsHalTetheroffloadTargetTest.cpp
new file mode 100644
index 0000000..fc8abbd
--- /dev/null
+++ b/tetheroffload/aidl/vts/functional/VtsHalTetheroffloadTargetTest.cpp
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "tetheroffload_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/tetheroffload/BnOffload.h>
+#include <aidl/android/hardware/tetheroffload/BnTetheringOffloadCallback.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <log/log.h>
+#include <net/if.h>
+#include <sys/socket.h>
+
+namespace aidl::android::hardware::tetheroffload {
+
+namespace {
+
+using ::android::base::unique_fd;
+using android::hardware::tetheroffload::ForwardedStats;
+using android::hardware::tetheroffload::IOffload;
+using android::hardware::tetheroffload::NatTimeoutUpdate;
+using android::hardware::tetheroffload::OffloadCallbackEvent;
+using ::testing::AnyOf;
+using ::testing::Eq;
+
+const std::string TEST_IFACE = "rmnet_data0";
+const unsigned kFd1Groups = NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY;
+const unsigned kFd2Groups = NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY;
+
+enum class ExpectBoolean {
+ Ignored = -1,
+ False = 0,
+ True = 1,
+};
+
+inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) {
+ return reinterpret_cast<const sockaddr*>(nladdr);
+}
+
+int netlinkSocket(int protocol, unsigned groups) {
+ unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, protocol));
+ if (s.get() < 0) {
+ return -errno;
+ }
+
+ const struct sockaddr_nl bind_addr = {
+ .nl_family = AF_NETLINK,
+ .nl_pad = 0,
+ .nl_pid = 0,
+ .nl_groups = groups,
+ };
+ if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
+ return -errno;
+ }
+
+ const struct sockaddr_nl kernel_addr = {
+ .nl_family = AF_NETLINK,
+ .nl_pad = 0,
+ .nl_pid = 0,
+ .nl_groups = groups,
+ };
+ if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
+ return -errno;
+ }
+
+ return s.release();
+}
+
+int netlinkSocket(unsigned groups) {
+ return netlinkSocket(NETLINK_NETFILTER, groups);
+}
+
+// Check whether the specified interface is up.
+bool interfaceIsUp(const std::string name) {
+ struct ifreq ifr = {};
+ strlcpy(ifr.ifr_name, name.c_str(), sizeof(ifr.ifr_name));
+ int sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1) return false;
+ int ret = ioctl(sock, SIOCGIFFLAGS, &ifr, sizeof(ifr));
+ close(sock);
+ return (ret == 0) && (ifr.ifr_flags & IFF_UP);
+}
+
+// Callback class for both events and NAT timeout updates.
+class TetheringOffloadCallback : public BnTetheringOffloadCallback {
+ public:
+ ndk::ScopedAStatus onEvent(OffloadCallbackEvent in_event) override {
+ auto lock = std::lock_guard{mMutex};
+ mOnEventInvoked = true;
+ mLastEvent = in_event;
+ mNotifyCv.notify_all();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ndk::ScopedAStatus updateTimeout(const NatTimeoutUpdate& in_params) override {
+ auto lock = std::lock_guard{mMutex};
+ mOnUpdateTimeoutInvoked = true;
+ mNatTimeout = in_params;
+ mNotifyCv.notify_all();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mNotifyCv;
+ OffloadCallbackEvent mLastEvent;
+ NatTimeoutUpdate mNatTimeout;
+ bool mOnEventInvoked = false;
+ bool mOnUpdateTimeoutInvoked = false;
+};
+
+// The common base class for tetheroffload AIDL HAL tests.
+class TetheroffloadAidlTestBase : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override { getService(); }
+ virtual void TearDown() override {
+ // For good measure, the teardown should try stopOffload() once more, since
+ // different HAL call test cycles might enter this function. Also the
+ // return code cannot be actually expected for all cases, hence ignore it.
+ stopOffload(ExpectBoolean::Ignored);
+ };
+
+ protected:
+ void getService() {
+ AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
+ ASSERT_NE(binder, nullptr);
+ mOffload = IOffload::fromBinder(ndk::SpAIBinder(binder));
+ }
+
+ void initOffload(const bool expectedResult) {
+ unique_fd ufd1(netlinkSocket(kFd1Groups));
+ if (ufd1.get() < 0) {
+ ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno));
+ FAIL();
+ }
+ ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(ufd1.release());
+
+ unique_fd ufd2(netlinkSocket(kFd2Groups));
+ if (ufd2.get() < 0) {
+ ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno));
+ FAIL();
+ }
+ ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(ufd2.release());
+
+ mTetheringOffloadCallback = ndk::SharedRefBase::make<TetheringOffloadCallback>();
+ ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback";
+
+ ASSERT_EQ(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(),
+ expectedResult ? EX_NONE : EX_ILLEGAL_STATE);
+ }
+
+ void stopOffload(const ExpectBoolean expectedResult) {
+ ndk::ScopedAStatus status = mOffload->stopOffload();
+ if (expectedResult == ExpectBoolean::Ignored) return;
+ ASSERT_EQ(status.getExceptionCode(),
+ expectedResult == ExpectBoolean::True ? EX_NONE : EX_ILLEGAL_STATE);
+ }
+
+ std::shared_ptr<IOffload> mOffload;
+ std::shared_ptr<TetheringOffloadCallback> mTetheringOffloadCallback;
+};
+
+// The test class for tetheroffload before initialization.
+class TetheroffloadAidlPreInitTest : public TetheroffloadAidlTestBase {
+ public:
+ virtual void SetUp() override { getService(); }
+};
+
+// The main test class for tetheroffload AIDL HAL.
+class TetheroffloadAidlGeneralTest : public TetheroffloadAidlTestBase {
+ public:
+ virtual void SetUp() override {
+ getService();
+ initOffload(true);
+ }
+};
+
+// Passing invalid file descriptor to initOffload() should return an error.
+// Check that this occurs when both FDs are empty.
+TEST_P(TetheroffloadAidlPreInitTest, TestInitOffloadInvalidFdsReturnsError) {
+ ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(-1);
+ ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(-1);
+ mTetheringOffloadCallback = ndk::SharedRefBase::make<TetheringOffloadCallback>();
+ ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback";
+ EXPECT_THAT(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(),
+ AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_TRANSACTION_FAILED)));
+}
+
+// Passing invalid file descriptor to initOffload() should return an error.
+// Check that this occurs when FD1 is empty.
+TEST_P(TetheroffloadAidlPreInitTest, TestInitOffloadInvalidFd1ReturnsError) {
+ ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(-1);
+ unique_fd ufd2(netlinkSocket(kFd2Groups));
+ if (ufd2.get() < 0) {
+ ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno));
+ FAIL();
+ }
+ ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(ufd2.release());
+ mTetheringOffloadCallback = ndk::SharedRefBase::make<TetheringOffloadCallback>();
+ ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback";
+ EXPECT_THAT(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(),
+ AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_TRANSACTION_FAILED)));
+}
+
+// Passing invalid file descriptor to initOffload() should return an error.
+// Check that this occurs when FD2 is empty.
+TEST_P(TetheroffloadAidlPreInitTest, TestInitOffloadInvalidFd2ReturnsError) {
+ unique_fd ufd1(netlinkSocket(kFd1Groups));
+ if (ufd1.get() < 0) {
+ ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno));
+ FAIL();
+ }
+ ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(ufd1.release());
+ ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(-1);
+ mTetheringOffloadCallback = ndk::SharedRefBase::make<TetheringOffloadCallback>();
+ ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback";
+ EXPECT_THAT(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(),
+ AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_TRANSACTION_FAILED)));
+}
+
+// Call initOffload() multiple times. Check that non-first initOffload() calls return error.
+TEST_P(TetheroffloadAidlPreInitTest, AdditionalInitsWithoutStopReturnError) {
+ initOffload(true);
+ initOffload(false);
+ initOffload(false);
+ initOffload(false);
+}
+
+// Check that calling stopOffload() without first having called initOffload() returns error.
+TEST_P(TetheroffloadAidlPreInitTest, MultipleStopsWithoutInitReturnError) {
+ stopOffload(ExpectBoolean::False);
+ stopOffload(ExpectBoolean::False);
+ stopOffload(ExpectBoolean::False);
+}
+
+// Check that calling stopOffload() after a complete init/stop cycle returns error.
+TEST_P(TetheroffloadAidlPreInitTest, AdditionalStopsWithInitReturnError) {
+ initOffload(true);
+ // Call setUpstreamParameters() so that "offload" can be reasonably said
+ // to be both requested and operational.
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("192.0.0.2");
+ const std::string v4Gw("192.0.0.1");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:2")};
+ EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk());
+ if (!interfaceIsUp(TEST_IFACE)) {
+ return;
+ }
+ SCOPED_TRACE("Expecting stopOffload to succeed");
+ stopOffload(ExpectBoolean::True); // balance out initOffload(true)
+ SCOPED_TRACE("Expecting stopOffload to fail the first time");
+ stopOffload(ExpectBoolean::False);
+ SCOPED_TRACE("Expecting stopOffload to fail the second time");
+ stopOffload(ExpectBoolean::False);
+}
+
+// Check that calling setLocalPrefixes() without first having called initOffload() returns error.
+TEST_P(TetheroffloadAidlPreInitTest, SetLocalPrefixesWithoutInitReturnsError) {
+ const std::vector<std::string> prefixes{std::string("2001:db8::/64")};
+ EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_STATE);
+}
+
+// Check that calling getForwardedStats() without first having called initOffload()
+// returns zero bytes statistics.
+TEST_P(TetheroffloadAidlPreInitTest, GetForwardedStatsWithoutInitReturnsZeroValues) {
+ const std::string upstream(TEST_IFACE);
+ ForwardedStats stats;
+ EXPECT_TRUE(mOffload->getForwardedStats(upstream, &stats).isOk());
+ EXPECT_EQ(stats.rxBytes, 0ULL);
+ EXPECT_EQ(stats.txBytes, 0ULL);
+}
+
+// Check that calling setDataWarningAndLimit() without first having called initOffload() returns
+// error.
+TEST_P(TetheroffloadAidlPreInitTest, SetDataWarningAndLimitWithoutInitReturnsError) {
+ const std::string upstream(TEST_IFACE);
+ const int64_t warning = 5000LL;
+ const int64_t limit = 5000LL;
+ EXPECT_EQ(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(),
+ EX_ILLEGAL_STATE);
+}
+
+// Check that calling setUpstreamParameters() without first having called initOffload()
+// returns error.
+TEST_P(TetheroffloadAidlPreInitTest, SetUpstreamParametersWithoutInitReturnsError) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("192.0.2.0/24");
+ const std::string v4Gw("192.0.2.1");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1")};
+ EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(),
+ EX_ILLEGAL_STATE);
+}
+
+// Check that calling addDownstream() with an IPv4 prefix without first having called
+// initOffload() returns error.
+TEST_P(TetheroffloadAidlPreInitTest, AddIPv4DownstreamWithoutInitReturnsError) {
+ const std::string iface(TEST_IFACE);
+ const std::string prefix("192.0.2.0/24");
+ EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE);
+}
+
+// Check that calling addDownstream() with an IPv6 prefix without first having called
+// initOffload() returns error.
+TEST_P(TetheroffloadAidlPreInitTest, AddIPv6DownstreamWithoutInitReturnsError) {
+ const std::string iface(TEST_IFACE);
+ const std::string prefix("2001:db8::/64");
+ EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE);
+}
+
+// Check that calling removeDownstream() with an IPv4 prefix without first having called
+// initOffload() returns error.
+TEST_P(TetheroffloadAidlPreInitTest, RemoveIPv4DownstreamWithoutInitReturnsError) {
+ const std::string iface(TEST_IFACE);
+ const std::string prefix("192.0.2.0/24");
+ EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE);
+}
+
+// Check that calling removeDownstream() with an IPv6 prefix without first having called
+// initOffload() returns error.
+TEST_P(TetheroffloadAidlPreInitTest, RemoveIPv6DownstreamWithoutInitReturnsError) {
+ const std::string iface(TEST_IFACE);
+ const std::string prefix("2001:db8::/64");
+ EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE);
+}
+
+/*
+ * Tests for IOffload::setLocalPrefixes().
+ */
+
+// Test setLocalPrefixes() rejects an IPv4 address.
+TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesIPv4AddressFails) {
+ const std::vector<std::string> prefixes{std::string("192.0.2.1")};
+ EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+// Test setLocalPrefixes() rejects an IPv6 address.
+TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesIPv6AddressFails) {
+ const std::vector<std::string> prefixes{std::string("fe80::1")};
+ EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+// Test setLocalPrefixes() accepts both IPv4 and IPv6 prefixes.
+TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesIPv4v6PrefixesOk) {
+ const std::vector<std::string> prefixes{std::string("192.0.2.0/24"), std::string("fe80::/64")};
+ EXPECT_TRUE(mOffload->setLocalPrefixes(prefixes).isOk());
+}
+
+// Test that setLocalPrefixes() fails given empty input. There is always
+// a non-empty set of local prefixes; when all networking interfaces are down
+// we still apply {127.0.0.0/8, ::1/128, fe80::/64} here.
+TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesEmptyFails) {
+ const std::vector<std::string> prefixes{};
+ EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+// Test setLocalPrefixes() fails on incorrectly formed input strings.
+TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesInvalidFails) {
+ const std::vector<std::string> prefixes{std::string("192.0.2.0/24"), std::string("invalid")};
+ EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+/*
+ * Tests for IOffload::getForwardedStats().
+ */
+
+// Test that getForwardedStats() for a non-existent upstream yields zero bytes statistics.
+TEST_P(TetheroffloadAidlGeneralTest, GetForwardedStatsInvalidUpstreamIface) {
+ const std::string upstream("invalid");
+ ForwardedStats stats;
+ EXPECT_TRUE(mOffload->getForwardedStats(upstream, &stats).isOk());
+ EXPECT_EQ(stats.rxBytes, 0ULL);
+ EXPECT_EQ(stats.txBytes, 0ULL);
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, GetForwardedStatsDummyIface) {
+ const std::string upstream(TEST_IFACE);
+ ForwardedStats stats;
+ EXPECT_TRUE(mOffload->getForwardedStats(upstream, &stats).isOk());
+ EXPECT_EQ(stats.rxBytes, 0ULL);
+ EXPECT_EQ(stats.txBytes, 0ULL);
+}
+
+/*
+ * Tests for IOffload::setDataWarningAndLimit().
+ */
+
+// Test that setDataWarningAndLimit() for an empty interface name fails.
+TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitEmptyUpstreamIfaceFails) {
+ const std::string upstream("");
+ const int64_t warning = 12345LL;
+ const int64_t limit = 67890LL;
+ EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(),
+ AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_UNSUPPORTED_OPERATION)));
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitNonZeroOk) {
+ const std::string upstream(TEST_IFACE);
+ const int64_t warning = 4000LL;
+ const int64_t limit = 5000LL;
+ EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(),
+ AnyOf(Eq(EX_NONE), Eq(EX_UNSUPPORTED_OPERATION)));
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitZeroOk) {
+ const std::string upstream(TEST_IFACE);
+ const int64_t warning = 0LL;
+ const int64_t limit = 0LL;
+ EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(),
+ AnyOf(Eq(EX_NONE), Eq(EX_UNSUPPORTED_OPERATION)));
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitUnlimitedWarningOk) {
+ const std::string upstream(TEST_IFACE);
+ const int64_t warning = LLONG_MAX;
+ const int64_t limit = 5000LL;
+ EXPECT_TRUE(mOffload->setDataWarningAndLimit(upstream, warning, limit).isOk());
+}
+
+// Test that setDataWarningAndLimit() with negative thresholds fails.
+TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitNegativeFails) {
+ const std::string upstream(TEST_IFACE);
+ const int64_t warning = -1LL;
+ const int64_t limit = -1LL;
+ EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(),
+ AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_UNSUPPORTED_OPERATION)));
+}
+
+/*
+ * Tests for IOffload::setUpstreamParameters().
+ */
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersIPv6OnlyOk) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("");
+ const std::string v4Gw("");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:2")};
+ EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk());
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersAlternateIPv6OnlyOk) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("");
+ const std::string v4Gw("");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:3")};
+ EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk());
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersIPv4OnlyOk) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("192.0.2.2");
+ const std::string v4Gw("192.0.2.1");
+ const std::vector<std::string> v6Gws{};
+ EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk());
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersIPv4v6Ok) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("192.0.2.2");
+ const std::string v4Gw("192.0.2.1");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:2")};
+ EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk());
+}
+
+// Test that setUpstreamParameters() fails when all parameters are empty.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersEmptyFails) {
+ const std::string iface("");
+ const std::string v4Addr("");
+ const std::string v4Gw("");
+ const std::vector<std::string> v6Gws{};
+ EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+}
+
+// Test that setUpstreamParameters() fails when given empty or non-existent interface names.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersBogusIfaceFails) {
+ const std::string v4Addr("192.0.2.2");
+ const std::string v4Gw("192.0.2.1");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1")};
+ for (const auto& bogus : {"", "invalid"}) {
+ SCOPED_TRACE(testing::Message() << "upstream: " << bogus);
+ const std::string iface(bogus);
+ EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+// Test that setUpstreamParameters() fails when given unparseable IPv4 addresses.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersInvalidIPv4AddrFails) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Gw("192.0.2.1");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1")};
+ for (const auto& bogus : {"invalid", "192.0.2"}) {
+ SCOPED_TRACE(testing::Message() << "v4addr: " << bogus);
+ const std::string v4Addr(bogus);
+ EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+// Test that setUpstreamParameters() fails when given unparseable IPv4 gateways.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersInvalidIPv4GatewayFails) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("192.0.2.2");
+ const std::vector<std::string> v6Gws{std::string("fe80::db8:1")};
+ for (const auto& bogus : {"invalid", "192.0.2"}) {
+ SCOPED_TRACE(testing::Message() << "v4gateway: " << bogus);
+ const std::string v4Gw(bogus);
+ EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+// Test that setUpstreamParameters() fails when given unparseable IPv6 gateways.
+TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersBadIPv6GatewaysFail) {
+ const std::string iface(TEST_IFACE);
+ const std::string v4Addr("192.0.2.2");
+ const std::string v4Gw("192.0.2.1");
+ for (const auto& bogus : {"", "invalid", "fe80::bogus", "192.0.2.66"}) {
+ SCOPED_TRACE(testing::Message() << "v6gateway: " << bogus);
+ const std::vector<std::string> v6Gws{std::string("fe80::1"), std::string(bogus)};
+ EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+/*
+ * Tests for IOffload::addDownstream().
+ */
+
+// Test addDownstream() works given an IPv4 prefix.
+TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamIPv4) {
+ const std::string iface("dummy0");
+ const std::string prefix("192.0.2.0/24");
+ EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk());
+}
+
+// Test addDownstream() works given an IPv6 prefix.
+TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamIPv6) {
+ const std::string iface("dummy0");
+ const std::string prefix("2001:db8::/64");
+ EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk());
+}
+
+// Test addDownstream() fails given all empty parameters.
+TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamEmptyFails) {
+ const std::string iface("");
+ const std::string prefix("");
+ EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+// Test addDownstream() fails given empty or non-existent interface names.
+TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamInvalidIfaceFails) {
+ const std::string prefix("192.0.2.0/24");
+ for (const auto& bogus : {"", "invalid"}) {
+ SCOPED_TRACE(testing::Message() << "iface: " << bogus);
+ const std::string iface(bogus);
+ EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+// Test addDownstream() fails given unparseable prefix arguments.
+TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamBogusPrefixFails) {
+ const std::string iface("dummy0");
+ for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) {
+ SCOPED_TRACE(testing::Message() << "prefix: " << bogus);
+ const std::string prefix(bogus);
+ EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+/*
+ * Tests for IOffload::removeDownstream().
+ */
+
+// Test removeDownstream() works given an IPv4 prefix.
+TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamIPv4) {
+ const std::string iface("dummy0");
+ const std::string prefix("192.0.2.0/24");
+ // First add the downstream, otherwise removeDownstream logic can reasonably
+ // return error for downstreams not previously added.
+ EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk());
+ EXPECT_TRUE(mOffload->removeDownstream(iface, prefix).isOk());
+}
+
+// Test removeDownstream() works given an IPv6 prefix.
+TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamIPv6) {
+ const std::string iface("dummy0");
+ const std::string prefix("2001:db8::/64");
+ // First add the downstream, otherwise removeDownstream logic can reasonably
+ // return error for downstreams not previously added.
+ EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk());
+ EXPECT_TRUE(mOffload->removeDownstream(iface, prefix).isOk());
+}
+
+// Test removeDownstream() fails given all empty parameters.
+TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamEmptyFails) {
+ const std::string iface("");
+ const std::string prefix("");
+ EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+}
+
+// Test removeDownstream() fails given empty or non-existent interface names.
+TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamBogusIfaceFails) {
+ const std::string prefix("192.0.2.0/24");
+ for (const auto& bogus : {"", "invalid"}) {
+ SCOPED_TRACE(testing::Message() << "iface: " << bogus);
+ const std::string iface(bogus);
+ EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+// Test removeDownstream() fails given unparseable prefix arguments.
+TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamBogusPrefixFails) {
+ const std::string iface("dummy0");
+ for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) {
+ SCOPED_TRACE(testing::Message() << "prefix: " << bogus);
+ const std::string prefix(bogus);
+ EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(),
+ EX_ILLEGAL_ARGUMENT);
+ }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TetheroffloadAidlTestBase);
+INSTANTIATE_TEST_SUITE_P(
+ IOffload, TetheroffloadAidlTestBase,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IOffload::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TetheroffloadAidlPreInitTest);
+INSTANTIATE_TEST_SUITE_P(
+ IOffload, TetheroffloadAidlPreInitTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IOffload::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TetheroffloadAidlGeneralTest);
+INSTANTIATE_TEST_SUITE_P(
+ IOffload, TetheroffloadAidlGeneralTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IOffload::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+} // namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
+
+} // namespace aidl::android::hardware::tetheroffload
diff --git a/tv/hdmi/earc/aidl/default/EArcMock.cpp b/tv/hdmi/earc/aidl/default/EArcMock.cpp
index 3578999..99a845e 100644
--- a/tv/hdmi/earc/aidl/default/EArcMock.cpp
+++ b/tv/hdmi/earc/aidl/default/EArcMock.cpp
@@ -85,7 +85,7 @@
return ScopedAStatus::ok();
}
-ScopedAStatus EArcMock::reportCapabilities(const std::vector<uint8_t> capabilities,
+ScopedAStatus EArcMock::reportCapabilities(const std::vector<uint8_t>& capabilities,
int32_t portId) {
if (mCallback != nullptr) {
mCallback->onCapabilitiesReported(capabilities, portId);
diff --git a/tv/hdmi/earc/aidl/default/EArcMock.h b/tv/hdmi/earc/aidl/default/EArcMock.h
index fc4c828..8af9706 100644
--- a/tv/hdmi/earc/aidl/default/EArcMock.h
+++ b/tv/hdmi/earc/aidl/default/EArcMock.h
@@ -43,7 +43,7 @@
::ndk::ScopedAStatus getState(int32_t in_portId, IEArcStatus* _aidl_return) override;
::ndk::ScopedAStatus getLastReportedAudioCapabilities(
int32_t in_portId, std::vector<uint8_t>* _aidl_return) override;
- ::ndk::ScopedAStatus reportCapabilities(const std::vector<uint8_t> capabilities,
+ ::ndk::ScopedAStatus reportCapabilities(const std::vector<uint8_t>& capabilities,
int32_t portId);
::ndk::ScopedAStatus changeState(const IEArcStatus status, int32_t portId);
diff --git a/usb/gadget/aidl/Android.bp b/usb/gadget/aidl/Android.bp
new file mode 100644
index 0000000..cb8560a
--- /dev/null
+++ b/usb/gadget/aidl/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.usb.gadget",
+ vendor_available: true,
+ srcs: ["android/hardware/usb/gadget/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ },
+}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/GadgetFunction.aidl
similarity index 84%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/GadgetFunction.aidl
index 336f9b5..c3f26d5 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/GadgetFunction.aidl
@@ -31,12 +31,16 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.usb.gadget;
+@VintfStability
+parcelable GadgetFunction {
+ const long NONE = 0;
+ const long ADB = 1;
+ const long ACCESSORY = 2;
+ const long MTP = 4;
+ const long MIDI = 8;
+ const long PTP = 16;
+ const long RNDIS = 32;
+ const long AUDIO_SOURCE = 64;
+ const long NCM = 1024;
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadget.aidl
similarity index 77%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadget.aidl
index 336f9b5..ef45f8b 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadget.aidl
@@ -31,12 +31,11 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.usb.gadget;
+@VintfStability
+interface IUsbGadget {
+ oneway void setCurrentUsbFunctions(in long functions, in android.hardware.usb.gadget.IUsbGadgetCallback callback, in long timeoutMs, long transactionId);
+ oneway void getCurrentUsbFunctions(in android.hardware.usb.gadget.IUsbGadgetCallback callback, long transactionId);
+ oneway void getUsbSpeed(in android.hardware.usb.gadget.IUsbGadgetCallback callback, long transactionId);
+ oneway void reset();
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
similarity index 79%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
index 336f9b5..9de68de 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
@@ -31,12 +31,10 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
-@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+package android.hardware.usb.gadget;
+@VintfStability
+interface IUsbGadgetCallback {
+ oneway void setCurrentUsbFunctionsCb(in long functions, in android.hardware.usb.gadget.Status status, long transactionId);
+ oneway void getCurrentUsbFunctionsCb(in long functions, in android.hardware.usb.gadget.Status status, long transactionId);
+ oneway void getUsbSpeedCb(in android.hardware.usb.gadget.UsbSpeed speed, long transactionId);
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/Status.aidl
similarity index 90%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/Status.aidl
index 336f9b5..bdcf685 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/Status.aidl
@@ -31,12 +31,12 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
+package android.hardware.usb.gadget;
@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+enum Status {
+ SUCCESS = 0,
+ ERROR = 1,
+ FUNCTIONS_APPLIED = 2,
+ FUNCTIONS_NOT_APPLIED = 3,
+ CONFIGURATION_NOT_SUPPORTED = 4,
}
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/UsbSpeed.aidl
similarity index 89%
copy from audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
copy to usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/UsbSpeed.aidl
index 336f9b5..0f54ee5 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/AudioMode.aidl
+++ b/usb/gadget/aidl/aidl_api/android.hardware.usb.gadget/current/android/hardware/usb/gadget/UsbSpeed.aidl
@@ -31,12 +31,14 @@
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
-package android.hardware.audio.core;
+package android.hardware.usb.gadget;
@Backing(type="int") @VintfStability
-enum AudioMode {
- NORMAL = 0,
- RINGTONE = 1,
- IN_CALL = 2,
- IN_COMMUNICATION = 3,
- CALL_SCREEN = 4,
+enum UsbSpeed {
+ UNKNOWN = 0,
+ LOWSPEED = 1,
+ FULLSPEED = 2,
+ HIGHSPEED = 3,
+ SUPERSPEED = 4,
+ SUPERSPEED_10Gb = 5,
+ SUPERSPEED_20Gb = 6,
}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl
new file mode 100644
index 0000000..d82b427
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+@VintfStability
+parcelable GadgetFunction {
+ /**
+ * Removes all the functions and pulls down the gadget.
+ */
+ const long NONE = 0;
+ /**
+ * Android Debug Bridge function.
+ */
+ const long ADB = 1;
+ /**
+ * Android open accessory protocol function.
+ */
+ const long ACCESSORY = 1 << 1;
+ /**
+ * Media Transfer protocol function.
+ */
+ const long MTP = 1 << 2;
+ /**
+ * Peripheral mode USB Midi function.
+ */
+ const long MIDI = 1 << 3;
+ /**
+ * Picture transfer protocol function.
+ */
+ const long PTP = 1 << 4;
+ /**
+ * Tethering function.
+ */
+ const long RNDIS = 1 << 5;
+ /**
+ * AOAv2.0 - Audio Source function.
+ */
+ const long AUDIO_SOURCE = 1 << 6;
+ /**
+ * NCM - NCM function.
+ */
+ const long NCM = 1 << 10;
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadget.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadget.aidl
new file mode 100644
index 0000000..da644d6
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadget.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+import android.hardware.usb.gadget.GadgetFunction;
+import android.hardware.usb.gadget.IUsbGadgetCallback;
+
+@VintfStability
+oneway interface IUsbGadget {
+ /**
+ * This function is used to set the current USB gadget configuration.
+ * Usb gadget needs to be reset if an USB configuration is already.
+ *
+ * @param functions The GadgetFunction bitmap. See GadgetFunction for
+ * the value of each bit.
+ * @param callback IUsbGadgetCallback::setCurrentUsbFunctionsCb used to
+ * propagate back the status.
+ * @param timeoutMs The maximum time (in milliseconds) within which the
+ * IUsbGadgetCallback needs to be returned.
+ * @param transactionId ID to be used when invoking the callback.
+ *
+ */
+ void setCurrentUsbFunctions(in long functions, in IUsbGadgetCallback callback,
+ in long timeoutMs, long transactionId);
+
+ /**
+ * This function is used to query the USB functions included in the
+ * current USB configuration.
+ *
+ * @param callback IUsbGadgetCallback::getCurrentUsbFunctionsCb used to
+ * propagate the current functions list.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getCurrentUsbFunctions(in IUsbGadgetCallback callback, long transactionId);
+
+ /**
+ * The function is used to query current USB speed.
+ *
+ * @param callback IUsbGadgetCallback::getUsbSpeedCb used to propagate
+ * current USB speed.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getUsbSpeed(in IUsbGadgetCallback callback, long transactionId);
+
+ /**
+ * This function is used to reset USB gadget driver.
+ * Performs USB data connection reset. The connection will disconnect and
+ * reconnect.
+ */
+ void reset();
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadgetCallback.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
new file mode 100644
index 0000000..5a682d6
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/IUsbGadgetCallback.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+import android.hardware.usb.gadget.GadgetFunction;
+import android.hardware.usb.gadget.Status;
+import android.hardware.usb.gadget.UsbSpeed;
+
+@VintfStability
+oneway interface IUsbGadgetCallback {
+ /**
+ * Callback function used to propagate the status of configuration
+ * switch to the caller.
+ *
+ * @param functions list of functions defined by GadgetFunction
+ * included in the current USB gadget composition.
+ * @param status SUCCESS when the functions are applied.
+ * FUNCTIONS_NOT_SUPPORTED when the configuration is
+ * not supported.
+ * ERROR otherwise.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void setCurrentUsbFunctionsCb(in long functions, in Status status, long transactionId);
+
+ /**
+ * Callback function used to propagate the current USB gadget
+ * configuration.
+ * @param functions The GadgetFunction bitmap. See GadgetFunction for
+ * the value of each bit.
+ * @param status FUNCTIONS_APPLIED when list of functions have been
+ * applied.
+ * FUNCTIONS_NOT_APPLIED when the functions have not
+ * been applied.
+ * ERROR otherwise.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getCurrentUsbFunctionsCb(in long functions, in Status status, long transactionId);
+
+ /**
+ * Used to convey the current USB speed to the caller.
+ * Must be called either when USB state changes due to USB enumeration or
+ * when caller requested for USB speed through getUsbSpeed.
+ *
+ * @param speed USB Speed defined by UsbSpeed showed current USB
+ * connection speed.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void getUsbSpeedCb(in UsbSpeed speed, long transactionId);
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/Status.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/Status.aidl
new file mode 100644
index 0000000..8d8c3e3
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/Status.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+@VintfStability
+@Backing(type="int")
+enum Status {
+ SUCCESS = 0,
+ /**
+ * Error value when the HAL operation fails for reasons not listed here.
+ */
+ ERROR = 1,
+ /**
+ * USB configuration applied successfully.
+ */
+ FUNCTIONS_APPLIED = 2,
+ /**
+ * USB confgiuration failed to apply.
+ */
+ FUNCTIONS_NOT_APPLIED = 3,
+ /**
+ * USB configuration not supported.
+ */
+ CONFIGURATION_NOT_SUPPORTED = 4,
+}
diff --git a/usb/gadget/aidl/android/hardware/usb/gadget/UsbSpeed.aidl b/usb/gadget/aidl/android/hardware/usb/gadget/UsbSpeed.aidl
new file mode 100644
index 0000000..0492757
--- /dev/null
+++ b/usb/gadget/aidl/android/hardware/usb/gadget/UsbSpeed.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb.gadget;
+
+@VintfStability
+@Backing(type="int")
+enum UsbSpeed {
+ /**
+ * UNKNOWN - Not Connected or Unsupported Speed
+ */
+ UNKNOWN = 0,
+ /**
+ * USB Low Speed
+ */
+ LOWSPEED = 1,
+ /**
+ * USB Full Speed
+ */
+ FULLSPEED = 2,
+ /**
+ * USB High Speed
+ */
+ HIGHSPEED = 3,
+ /**
+ * USB Super Speed
+ */
+ SUPERSPEED = 4,
+ /**
+ * USB Super Speed 10Gbps
+ */
+ SUPERSPEED_10Gb = 5,
+ /**
+ * USB Super Speed 20Gbps
+ */
+ SUPERSPEED_20Gb = 6,
+}
diff --git a/usb/gadget/aidl/default/Android.bp b/usb/gadget/aidl/default/Android.bp
new file mode 100644
index 0000000..2ea0e12
--- /dev/null
+++ b/usb/gadget/aidl/default/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "hardware_interfaces_license",
+ ],
+}
+cc_binary {
+ name: "android.hardware.usb.gadget-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.usb.gadget-service.example.rc"],
+ vintf_fragments: [
+ "android.hardware.usb.gadget-service.example.xml",
+ ],
+ vendor: true,
+ srcs: ["service_gadget.cpp", "UsbGadget.cpp"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "libhardware",
+ "android.hardware.usb.gadget-V1-ndk",
+ "android.frameworks.stats-V1-ndk",
+ "libcutils",
+ "libbinder_ndk",
+ ],
+}
diff --git a/usb/gadget/aidl/default/UsbGadget.cpp b/usb/gadget/aidl/default/UsbGadget.cpp
new file mode 100644
index 0000000..72cf681
--- /dev/null
+++ b/usb/gadget/aidl/default/UsbGadget.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define LOG_TAG "android.hardware.usb.gadget.aidl-service"
+
+#include "UsbGadget.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <aidl/android/frameworks/stats/IStats.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+string enabledPath;
+constexpr char kHsi2cPath[] = "/sys/devices/platform/10d50000.hsi2c";
+constexpr char kI2CPath[] = "/sys/devices/platform/10d50000.hsi2c/i2c-";
+constexpr char kAccessoryLimitCurrent[] = "i2c-max77759tcpc/usb_limit_accessory_current";
+constexpr char kAccessoryLimitCurrentEnable[] = "i2c-max77759tcpc/usb_limit_accessory_enable";
+
+UsbGadget::UsbGadget() : mGadgetIrqPath("") {}
+
+Status UsbGadget::getUsbGadgetIrqPath() {
+ std::string irqs;
+ size_t read_pos = 0;
+ size_t found_pos = 0;
+
+ if (!ReadFileToString(kProcInterruptsPath, &irqs)) {
+ ALOGE("cannot read all interrupts");
+ return Status::ERROR;
+ }
+
+ while (true) {
+ found_pos = irqs.find_first_of("\n", read_pos);
+ if (found_pos == std::string::npos) {
+ ALOGI("the string of all interrupts is unexpected");
+ return Status::ERROR;
+ }
+
+ std::string single_irq = irqs.substr(read_pos, found_pos - read_pos);
+
+ if (single_irq.find("dwc3", 0) != std::string::npos) {
+ unsigned int dwc3_irq_number;
+ size_t dwc3_pos = single_irq.find_first_of(":");
+ if (!ParseUint(single_irq.substr(0, dwc3_pos), &dwc3_irq_number)) {
+ ALOGI("unknown IRQ strings");
+ return Status::ERROR;
+ }
+
+ mGadgetIrqPath = kProcIrqPath + single_irq.substr(0, dwc3_pos) + kSmpAffinityList;
+ break;
+ }
+
+ if (found_pos == irqs.npos) {
+ ALOGI("USB gadget doesn't start");
+ return Status::ERROR;
+ }
+
+ read_pos = found_pos + 1;
+ }
+
+ return Status::SUCCESS;
+}
+
+void currentFunctionsAppliedCallback(bool functionsApplied, void* payload) {
+ UsbGadget* gadget = (UsbGadget*)payload;
+ gadget->mCurrentUsbFunctionsApplied = functionsApplied;
+}
+
+ScopedAStatus UsbGadget::getCurrentUsbFunctions(const shared_ptr<IUsbGadgetCallback>& callback,
+ int64_t in_transactionId) {
+ 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<IUsbGadgetCallback>& 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() {
+ return Status::SUCCESS;
+}
+
+ScopedAStatus UsbGadget::reset() {
+ return ScopedAStatus::ok();
+}
+
+Status UsbGadget::setupFunctions(long functions, const shared_ptr<IUsbGadgetCallback>& callback,
+ uint64_t timeout, int64_t in_transactionId) {
+ bool ffsEnabled = false;
+ if (timeout == 0) {
+ ALOGI("timeout not setup");
+ }
+
+ if ((functions & GadgetFunction::ADB) != 0) {
+ ffsEnabled = true;
+ }
+
+ if ((functions & GadgetFunction::NCM) != 0) {
+ ALOGI("setCurrentUsbFunctions ncm");
+ }
+
+ // Pull up the gadget right away when there are no ffs functions.
+ if (!ffsEnabled) {
+ mCurrentUsbFunctionsApplied = true;
+ if (callback)
+ callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS, in_transactionId);
+ return Status::SUCCESS;
+ }
+
+ return Status::SUCCESS;
+}
+
+Status getI2cBusHelper(string* name) {
+ DIR* dp;
+
+ dp = opendir(kHsi2cPath);
+ if (dp != NULL) {
+ struct dirent* ep;
+
+ while ((ep = readdir(dp))) {
+ if (ep->d_type == DT_DIR) {
+ if (string::npos != string(ep->d_name).find("i2c-")) {
+ std::strtok(ep->d_name, "-");
+ *name = std::strtok(NULL, "-");
+ }
+ }
+ }
+ closedir(dp);
+ return Status::SUCCESS;
+ }
+
+ ALOGE("Failed to open %s", kHsi2cPath);
+ return Status::ERROR;
+}
+
+ScopedAStatus UsbGadget::setCurrentUsbFunctions(int64_t functions,
+ const shared_ptr<IUsbGadgetCallback>& callback,
+ int64_t timeoutMs, int64_t in_transactionId) {
+ std::unique_lock<std::mutex> lk(mLockSetCurrentFunction);
+ std::string current_usb_power_operation_mode, current_usb_type;
+ std::string usb_limit_sink_enable;
+
+ string accessoryCurrentLimitEnablePath, accessoryCurrentLimitPath, path;
+
+ mCurrentUsbFunctions = functions;
+ mCurrentUsbFunctionsApplied = false;
+
+ getI2cBusHelper(&path);
+ accessoryCurrentLimitPath = kI2CPath + path + "/" + kAccessoryLimitCurrent;
+ accessoryCurrentLimitEnablePath = kI2CPath + path + "/" + kAccessoryLimitCurrentEnable;
+
+ // Get the gadget IRQ number before tearDownGadget()
+ if (mGadgetIrqPath.empty()) getUsbGadgetIrqPath();
+
+ // 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 = setupFunctions(functions, callback, timeoutMs, in_transactionId);
+ if (status != Status::SUCCESS) {
+ goto error;
+ }
+
+ if (functions & GadgetFunction::NCM) {
+ if (!mGadgetIrqPath.empty()) {
+ if (!WriteStringToFile(BIG_CORE, mGadgetIrqPath))
+ ALOGI("Cannot move gadget IRQ to big core, path:%s", mGadgetIrqPath.c_str());
+ }
+ } else {
+ if (!mGadgetIrqPath.empty()) {
+ if (!WriteStringToFile(MEDIUM_CORE, mGadgetIrqPath))
+ ALOGI("Cannot move gadget IRQ to medium core, path:%s", mGadgetIrqPath.c_str());
+ }
+ }
+
+ if (ReadFileToString(CURRENT_USB_TYPE_PATH, ¤t_usb_type))
+ current_usb_type = Trim(current_usb_type);
+
+ if (ReadFileToString(CURRENT_USB_POWER_OPERATION_MODE_PATH, ¤t_usb_power_operation_mode))
+ current_usb_power_operation_mode = Trim(current_usb_power_operation_mode);
+
+ if (functions & GadgetFunction::ACCESSORY && current_usb_type == "Unknown SDP [CDP] DCP" &&
+ (current_usb_power_operation_mode == "default" ||
+ current_usb_power_operation_mode == "1.5A")) {
+ if (!WriteStringToFile("1300000", accessoryCurrentLimitPath)) {
+ ALOGI("Write 1.3A to limit current fail");
+ } else {
+ if (!WriteStringToFile("1", accessoryCurrentLimitEnablePath)) {
+ ALOGI("Enable limit current fail");
+ }
+ }
+ } else {
+ if (!WriteStringToFile("0", accessoryCurrentLimitEnablePath))
+ ALOGI("unvote accessory limit current failed");
+ }
+
+ 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
+} // namespace aidl
diff --git a/usb/gadget/aidl/default/UsbGadget.h b/usb/gadget/aidl/default/UsbGadget.h
new file mode 100644
index 0000000..5060194
--- /dev/null
+++ b/usb/gadget/aidl/default/UsbGadget.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/usb/gadget/BnUsbGadget.h>
+#include <aidl/android/hardware/usb/gadget/BnUsbGadgetCallback.h>
+#include <aidl/android/hardware/usb/gadget/GadgetFunction.h>
+#include <aidl/android/hardware/usb/gadget/IUsbGadget.h>
+#include <aidl/android/hardware/usb/gadget/IUsbGadgetCallback.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <sched.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <utils/Log.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace usb {
+namespace gadget {
+
+using ::aidl::android::hardware::usb::gadget::GadgetFunction;
+using ::aidl::android::hardware::usb::gadget::IUsbGadget;
+using ::aidl::android::hardware::usb::gadget::IUsbGadgetCallback;
+using ::aidl::android::hardware::usb::gadget::Status;
+using ::aidl::android::hardware::usb::gadget::UsbSpeed;
+using ::android::base::GetProperty;
+using ::android::base::ParseUint;
+using ::android::base::ReadFileToString;
+using ::android::base::SetProperty;
+using ::android::base::Trim;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFile;
+using ::ndk::ScopedAStatus;
+using ::std::shared_ptr;
+using ::std::string;
+
+constexpr char kGadgetName[] = "11110000.dwc3";
+constexpr char kProcInterruptsPath[] = "/proc/interrupts";
+constexpr char kProcIrqPath[] = "/proc/irq/";
+constexpr char kSmpAffinityList[] = "/smp_affinity_list";
+#ifndef UDC_PATH
+#define UDC_PATH "/sys/class/udc/11110000.dwc3/"
+#endif
+// static MonitorFfs monitorFfs(kGadgetName);
+
+#define SPEED_PATH UDC_PATH "current_speed"
+
+#define BIG_CORE "6"
+#define MEDIUM_CORE "4"
+
+#define POWER_SUPPLY_PATH "/sys/class/power_supply/usb/"
+#define USB_PORT0_PATH "/sys/class/typec/port0/"
+
+#define CURRENT_MAX_PATH POWER_SUPPLY_PATH "current_max"
+#define CURRENT_USB_TYPE_PATH POWER_SUPPLY_PATH "usb_type"
+#define CURRENT_USB_POWER_OPERATION_MODE_PATH USB_PORT0_PATH "power_operation_mode"
+
+struct UsbGadget : public BnUsbGadget {
+ UsbGadget();
+
+ // Makes sure that only one request is processed at a time.
+ std::mutex mLockSetCurrentFunction;
+ std::string mGadgetIrqPath;
+ long mCurrentUsbFunctions;
+ bool mCurrentUsbFunctionsApplied;
+ UsbSpeed mUsbSpeed;
+
+ ScopedAStatus setCurrentUsbFunctions(int64_t functions,
+ const shared_ptr<IUsbGadgetCallback>& callback,
+ int64_t timeoutMs, int64_t in_transactionId) override;
+
+ ScopedAStatus getCurrentUsbFunctions(const shared_ptr<IUsbGadgetCallback>& callback,
+ int64_t in_transactionId) override;
+
+ ScopedAStatus reset() override;
+
+ ScopedAStatus getUsbSpeed(const shared_ptr<IUsbGadgetCallback>& callback,
+ int64_t in_transactionId) override;
+
+ private:
+ Status tearDownGadget();
+ Status getUsbGadgetIrqPath();
+ Status setupFunctions(long functions, const shared_ptr<IUsbGadgetCallback>& callback,
+ uint64_t timeout, int64_t in_transactionId);
+};
+
+} // namespace gadget
+} // namespace usb
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.rc b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.rc
new file mode 100644
index 0000000..b2a8cc0
--- /dev/null
+++ b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.usb_gadget_default /vendor/bin/hw/android.hardware.usb.gadget-service.example
+ class hal
+ user system
+ group system
diff --git a/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.xml b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.xml
new file mode 100644
index 0000000..e7eebc3
--- /dev/null
+++ b/usb/gadget/aidl/default/android.hardware.usb.gadget-service.example.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.usb.gadget</name>
+ <version>1</version>
+ <interface>
+ <name>IUsbGadget</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/usb/gadget/aidl/default/service_gadget.cpp b/usb/gadget/aidl/default/service_gadget.cpp
new file mode 100644
index 0000000..88678ab
--- /dev/null
+++ b/usb/gadget/aidl/default/service_gadget.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include "UsbGadget.h"
+using ::aidl::android::hardware::usb::gadget::UsbGadget;
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<UsbGadget> usbgadget = ndk::SharedRefBase::make<UsbGadget>();
+ 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 -1;
+}