Merge "Add white point plumbing into command buffer"
diff --git a/bluetooth/audio/2.2/Android.bp b/bluetooth/audio/2.2/Android.bp
index 6449c08..8d52ce9 100644
--- a/bluetooth/audio/2.2/Android.bp
+++ b/bluetooth/audio/2.2/Android.bp
@@ -14,6 +14,7 @@
root: "android.hardware",
srcs: [
"types.hal",
+ "IBluetoothAudioPort.hal",
"IBluetoothAudioProvider.hal",
"IBluetoothAudioProvidersFactory.hal",
],
diff --git a/bluetooth/audio/2.2/IBluetoothAudioPort.hal b/bluetooth/audio/2.2/IBluetoothAudioPort.hal
new file mode 100644
index 0000000..344899c
--- /dev/null
+++ b/bluetooth/audio/2.2/IBluetoothAudioPort.hal
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.bluetooth.audio@2.2;
+
+import @2.0::IBluetoothAudioPort;
+import android.hardware.audio.common@5.0::SinkMetadata;
+
+interface IBluetoothAudioPort extends @2.0::IBluetoothAudioPort {
+ /**
+ * Called when the metadata of the stream's sink has been changed.
+ *
+ * @param sinkMetadata Description of the audio that is recorded by the
+ * clients.
+ */
+ updateSinkMetadata(SinkMetadata sinkMetadata);
+};
diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
index ad8c839..bc16b01 100644
--- a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
+++ b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
@@ -17,7 +17,7 @@
package android.hardware.bluetooth.audio@2.2;
import @2.1::IBluetoothAudioProvider;
-import @2.0::IBluetoothAudioPort;
+import @2.2::IBluetoothAudioPort;
import @2.0::Status;
/**
diff --git a/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp
index 126bc9e..2a6d93a 100644
--- a/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp
@@ -54,7 +54,7 @@
}
Return<void> A2dpOffloadAudioProvider::startSession(
- const sp<IBluetoothAudioPort>& hostIf,
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
diff --git a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp
index 0d918e1..ba31d39 100644
--- a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp
@@ -70,7 +70,7 @@
}
Return<void> A2dpSoftwareAudioProvider::startSession(
- const sp<IBluetoothAudioPort>& hostIf,
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
diff --git a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h
index 3d4f0cc..ac3aece 100644
--- a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h
+++ b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h
@@ -40,7 +40,7 @@
bool isValid(const V2_1::SessionType& sessionType) override;
bool isValid(const V2_0::SessionType& sessionType) override;
- Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
diff --git a/bluetooth/audio/2.2/default/AudioPort_2_0_to_2_2_Wrapper.h b/bluetooth/audio/2.2/default/AudioPort_2_0_to_2_2_Wrapper.h
new file mode 100644
index 0000000..c5613fb
--- /dev/null
+++ b/bluetooth/audio/2.2/default/AudioPort_2_0_to_2_2_Wrapper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.2/types.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::V5_0::SinkMetadata;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_2::IBluetoothAudioPort;
+
+class AudioPort_2_0_to_2_2_Wrapper : public V2_2::IBluetoothAudioPort {
+ public:
+ AudioPort_2_0_to_2_2_Wrapper(const sp<V2_0::IBluetoothAudioPort>& port) {
+ this->port = port;
+ }
+
+ Return<void> startStream() override { return port->startStream(); }
+ Return<void> suspendStream() override { return port->suspendStream(); }
+ Return<void> stopStream() override { return port->stopStream(); }
+ Return<void> getPresentationPosition(
+ getPresentationPosition_cb _hidl_cb) override {
+ return port->getPresentationPosition(_hidl_cb);
+ }
+ Return<void> updateMetadata(const SourceMetadata& sourceMetadata) override {
+ return port->updateMetadata(sourceMetadata);
+ }
+ Return<void> updateSinkMetadata(const SinkMetadata&) override {
+ // DO NOTHING, 2.0 AudioPort doesn't support sink metadata updates
+ return Void();
+ }
+
+ sp<V2_0::IBluetoothAudioPort> port;
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
index 3655bc0..3c0ff42 100644
--- a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
@@ -20,6 +20,7 @@
#include <android-base/logging.h>
+#include "AudioPort_2_0_to_2_2_Wrapper.h"
#include "BluetoothAudioSessionReport_2_2.h"
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
@@ -51,7 +52,7 @@
audio_config_({}) {}
Return<void> BluetoothAudioProvider::startSession(
- const sp<IBluetoothAudioPort>& hostIf,
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
AudioConfiguration audioConfig_2_2;
@@ -67,11 +68,13 @@
audioConfig_2_2.codecConfig(audioConfig.codecConfig());
}
- return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+ sp<V2_2::IBluetoothAudioPort> hostIf_2_2 =
+ new AudioPort_2_0_to_2_2_Wrapper(hostIf);
+ return startSession_2_2(hostIf_2_2, audioConfig_2_2, _hidl_cb);
}
Return<void> BluetoothAudioProvider::startSession_2_1(
- const sp<IBluetoothAudioPort>& hostIf,
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
AudioConfiguration audioConfig_2_2;
if (audioConfig.getDiscriminator() ==
@@ -92,11 +95,13 @@
audioConfig_2_2.codecConfig(audioConfig.codecConfig());
}
- return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+ sp<V2_2::IBluetoothAudioPort> hostIf_2_2 =
+ new AudioPort_2_0_to_2_2_Wrapper(hostIf);
+ return startSession_2_2(hostIf_2_2, audioConfig_2_2, _hidl_cb);
}
Return<void> BluetoothAudioProvider::startSession_2_2(
- const sp<IBluetoothAudioPort>& hostIf,
+ const sp<V2_2::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
if (hostIf == nullptr) {
_hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
index b7581ba..0f1f3c6 100644
--- a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
@@ -26,7 +26,7 @@
namespace implementation {
using ::android::sp;
-using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_2::IBluetoothAudioPort;
using BluetoothAudioStatus =
::android::hardware::bluetooth::audio::V2_0::Status;
@@ -41,13 +41,13 @@
virtual bool isValid(const V2_1::SessionType& sessionType) = 0;
virtual bool isValid(const V2_0::SessionType& sessionType) = 0;
- Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
- Return<void> startSession_2_1(const sp<IBluetoothAudioPort>& hostIf,
+ Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_1::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
- Return<void> startSession_2_2(const sp<IBluetoothAudioPort>& hostIf,
+ Return<void> startSession_2_2(const sp<V2_2::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
Return<void> streamStarted(BluetoothAudioStatus status) override;
@@ -59,7 +59,7 @@
V2_1::SessionType session_type_;
AudioConfiguration audio_config_;
- sp<V2_0::IBluetoothAudioPort> stack_iface_;
+ sp<V2_2::IBluetoothAudioPort> stack_iface_;
virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
};
diff --git a/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp
index c79b910..9b3294f 100644
--- a/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp
@@ -66,7 +66,7 @@
}
Return<void> HearingAidAudioProvider::startSession(
- const sp<IBluetoothAudioPort>& hostIf,
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
diff --git a/bluetooth/audio/2.2/default/HearingAidAudioProvider.h b/bluetooth/audio/2.2/default/HearingAidAudioProvider.h
index 426c443..63290b5 100644
--- a/bluetooth/audio/2.2/default/HearingAidAudioProvider.h
+++ b/bluetooth/audio/2.2/default/HearingAidAudioProvider.h
@@ -40,7 +40,7 @@
bool isValid(const V2_1::SessionType& sessionType) override;
bool isValid(const V2_0::SessionType& sessionType) override;
- Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
const V2_0::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
diff --git a/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp b/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp
index af6ec99..9ec1776 100644
--- a/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp
@@ -20,6 +20,7 @@
#include <android-base/logging.h>
+#include "AudioPort_2_0_to_2_2_Wrapper.h"
#include "BluetoothAudioSessionReport_2_2.h"
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
@@ -83,11 +84,13 @@
.bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
.dataIntervalUs = 0});
- return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+ sp<V2_2::IBluetoothAudioPort> hostIf_2_2 =
+ new AudioPort_2_0_to_2_2_Wrapper(hostIf);
+ return startSession_2_2(hostIf_2_2, audioConfig_2_2, _hidl_cb);
}
Return<void> LeAudioAudioProvider::startSession_2_2(
- const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const sp<V2_2::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
diff --git a/bluetooth/audio/2.2/default/LeAudioAudioProvider.h b/bluetooth/audio/2.2/default/LeAudioAudioProvider.h
index 40c26e0..3de1724 100644
--- a/bluetooth/audio/2.2/default/LeAudioAudioProvider.h
+++ b/bluetooth/audio/2.2/default/LeAudioAudioProvider.h
@@ -45,7 +45,7 @@
const V2_1::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
- Return<void> startSession_2_2(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ Return<void> startSession_2_2(const sp<V2_2::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
diff --git a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp
index 7b70654..e3da267 100644
--- a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp
+++ b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp
@@ -20,6 +20,7 @@
#include <android-base/logging.h>
+#include "AudioPort_2_0_to_2_2_Wrapper.h"
#include "BluetoothAudioSessionReport_2_2.h"
#include "BluetoothAudioSupportedCodecsDB_2_1.h"
#include "BluetoothAudioSupportedCodecsDB_2_2.h"
@@ -91,11 +92,13 @@
.peerDelay = 0,
.lc3Config = audioConfig.leAudioCodecConfig().lc3Config};
- return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+ sp<V2_2::IBluetoothAudioPort> hostIf_2_2 =
+ new AudioPort_2_0_to_2_2_Wrapper(hostIf);
+ return startSession_2_2(hostIf_2_2, audioConfig_2_2, _hidl_cb);
}
Return<void> LeAudioOffloadAudioProvider::startSession_2_2(
- const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const sp<V2_2::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
/**
* Initialize the audio platform if audioConfiguration is supported.
diff --git a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h
index 5620295..fe58de5 100644
--- a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h
+++ b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h
@@ -38,7 +38,7 @@
const V2_1::AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
- Return<void> startSession_2_2(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ Return<void> startSession_2_2(const sp<V2_2::IBluetoothAudioPort>& hostIf,
const AudioConfiguration& audioConfig,
startSession_cb _hidl_cb) override;
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index 19d2d92..4f712bf 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -22,6 +22,7 @@
export_include_dirs: ["session/"],
header_libs: ["libhardware_headers"],
shared_libs: [
+ "android.hardware.audio.common@5.0",
"android.hardware.bluetooth.audio@2.0",
"android.hardware.bluetooth.audio@2.1",
"android.hardware.bluetooth.audio@2.2",
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h
index e20914e..b4ba8cf 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_2.h
@@ -132,6 +132,15 @@
}
}
+ static void UpdateSinkMetadata(const SessionType_2_1& session_type,
+ const struct sink_metadata* sink_metadata) {
+ std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+ BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->UpdateSinkMetadata(sink_metadata);
+ }
+ }
+
// The control API writes stream to FMQ
static size_t OutWritePcmData(const SessionType_2_1& session_type,
const void* buffer, size_t bytes) {
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
index 5a6b2e7..80df5d9 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
@@ -20,10 +20,16 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android/hardware/bluetooth/audio/2.2/IBluetoothAudioPort.h>
namespace android {
namespace bluetooth {
namespace audio {
+
+using ::android::hardware::audio::common::V5_0::AudioSource;
+using ::android::hardware::audio::common::V5_0::RecordTrackMetadata;
+using ::android::hardware::audio::common::V5_0::SinkMetadata;
+
using SessionType_2_1 =
::android::hardware::bluetooth::audio::V2_1::SessionType;
using SessionType_2_0 =
@@ -37,6 +43,9 @@
::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
BluetoothAudioSession_2_2::invalidOffloadAudioConfiguration = {};
+using IBluetoothAudioPort_2_2 =
+ ::android::hardware::bluetooth::audio::V2_2::IBluetoothAudioPort;
+
namespace {
bool is_2_0_session_type(
const ::android::hardware::bluetooth::audio::V2_1::SessionType&
@@ -84,6 +93,54 @@
return audio_session_2_1;
}
+void BluetoothAudioSession_2_2::UpdateSinkMetadata(
+ const struct sink_metadata* sink_metadata) {
+ std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+ if (!IsSessionReady()) {
+ LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+ << " has NO session";
+ return;
+ }
+
+ ssize_t track_count = sink_metadata->track_count;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+ << ", " << track_count << " track(s)";
+ if (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return;
+ }
+
+ struct record_track_metadata* track = sink_metadata->tracks;
+ SinkMetadata sinkMetadata;
+ RecordTrackMetadata* halMetadata;
+
+ sinkMetadata.tracks.resize(track_count);
+ halMetadata = sinkMetadata.tracks.data();
+ while (track_count && track) {
+ halMetadata->source = static_cast<AudioSource>(track->source);
+ halMetadata->gain = track->gain;
+ // halMetadata->destination leave unspecified
+ LOG(INFO) << __func__
+ << " - SessionType=" << toString(GetAudioSession()->session_type_)
+ << ", source=" << track->source
+ << ", dest_device=" << track->dest_device
+ << ", gain=" << track->gain
+ << ", dest_device_address=" << track->dest_device_address;
+ --track_count;
+ ++track;
+ ++halMetadata;
+ }
+
+ /* This is called just for 2.2 sessions, so it's safe to do this casting*/
+ IBluetoothAudioPort_2_2* stack_iface_2_2_ =
+ static_cast<IBluetoothAudioPort_2_2*>(audio_session->stack_iface_.get());
+ auto hal_retval = stack_iface_2_2_->updateSinkMetadata(sinkMetadata);
+ if (!hal_retval.isOk()) {
+ LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+ << toString(session_type_2_1_) << " failed";
+ }
+}
+
// The control function is for the bluetooth_audio module to get the current
// AudioConfiguration
const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
index 7213ede..d6ae3d7 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
@@ -74,6 +74,8 @@
const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
GetAudioConfig();
+ void UpdateSinkMetadata(const struct sink_metadata* sink_metadata);
+
static constexpr ::android::hardware::bluetooth::audio::V2_2::
AudioConfiguration& kInvalidSoftwareAudioConfiguration =
invalidSoftwareAudioConfiguration;
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index a781b35..9dc6fae 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -332,6 +332,13 @@
</interface>
</hal>
<hal format="aidl" optional="true">
+ <name>android.hardware.net.nlinterceptor</name>
+ <interface>
+ <name>IInterceptor</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="aidl" optional="true">
<name>android.hardware.oemlock</name>
<version>1</version>
<interface>
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 970d1cb..aac59db 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -804,6 +804,9 @@
* 3. Sets SUPL server host/port.
*/
TEST_P(GnssHalTest, TestAGnssExtension) {
+ if (aidl_gnss_hal_->getInterfaceVersion() == 1) {
+ return;
+ }
sp<IAGnss> iAGnss;
auto status = aidl_gnss_hal_->getExtensionAGnss(&iAGnss);
ASSERT_TRUE(status.isOk());
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 64550ef..73c3820 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -583,6 +583,7 @@
attest_key, &attested_key_blob, &attested_key_characteristics,
&attested_key_cert_chain));
+ ASSERT_GT(attested_key_cert_chain.size(), 0);
CheckedDeleteKey(&attested_key_blob);
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
@@ -612,6 +613,7 @@
attest_key, &attested_key_blob, &attested_key_characteristics,
&attested_key_cert_chain));
+ ASSERT_GT(attested_key_cert_chain.size(), 0);
CheckedDeleteKey(&attested_key_blob);
CheckedDeleteKey(&attest_key.keyBlob);
diff --git a/weaver/aidl/default/Weaver.cpp b/weaver/aidl/default/Weaver.cpp
index 56d9c4d..6b77924 100644
--- a/weaver/aidl/default/Weaver.cpp
+++ b/weaver/aidl/default/Weaver.cpp
@@ -15,30 +15,52 @@
*/
#include "Weaver.h"
+#include <array>
namespace aidl {
namespace android {
namespace hardware {
namespace weaver {
+struct Slotinfo {
+ int slot_id;
+ std::vector<uint8_t> key;
+ std::vector<uint8_t> value;
+};
+
+std::array<struct Slotinfo, 16> slot_array;
// Methods from ::android::hardware::weaver::IWeaver follow.
::ndk::ScopedAStatus Weaver::getConfig(WeaverConfig* out_config) {
- (void)out_config;
+ *out_config = {16, 16, 16};
return ::ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus Weaver::read(int32_t in_slotId, const std::vector<uint8_t>& in_key, WeaverReadResponse* out_response) {
- (void)in_slotId;
- (void)in_key;
- (void)out_response;
+
+ if (in_slotId > 15 || in_key.size() > 16) {
+ *out_response = {0, {}};
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(Weaver::STATUS_FAILED));
+ }
+
+ if (slot_array[in_slotId].key != in_key) {
+ *out_response = {0, {}};
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(Weaver::STATUS_INCORRECT_KEY));
+ }
+
+ *out_response = {0, slot_array[in_slotId].value};
+
return ::ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus Weaver::write(int32_t in_slotId, const std::vector<uint8_t>& in_key, const std::vector<uint8_t>& in_value) {
- (void)in_slotId;
- (void)in_key;
- (void)in_value;
+
+ if (in_slotId > 15 || in_key.size() > 16 || in_value.size() > 16)
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+
+ slot_array[in_slotId].key = in_key;
+ slot_array[in_slotId].value = in_value;
+
return ::ndk::ScopedAStatus::ok();
}
diff --git a/wifi/netlinkinterceptor/aidl/Android.bp b/wifi/netlinkinterceptor/aidl/Android.bp
new file mode 100644
index 0000000..924edee
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+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.net.nlinterceptor",
+ vendor_available: true,
+ srcs: ["android/hardware/net/nlinterceptor/*.aidl"],
+ stability: "vintf",
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
+}
diff --git a/wifi/netlinkinterceptor/aidl/aidl_api/NetlinkInterceptor/current/android/hardware/net/nlinterceptor/IInterceptor.aidl b/wifi/netlinkinterceptor/aidl/aidl_api/NetlinkInterceptor/current/android/hardware/net/nlinterceptor/IInterceptor.aidl
new file mode 100644
index 0000000..249b343
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/aidl_api/NetlinkInterceptor/current/android/hardware/net/nlinterceptor/IInterceptor.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.net.nlinterceptor;
+@VintfStability
+interface IInterceptor {
+ int createSocket(in int nlFamily, in int clientNlPid, in String clientName);
+ void closeSocket(in int nlFamily, in int interceptorNlPid);
+ void subscribeGroup(in int nlFamily, in int interceptorNlPid, in int nlGroup);
+ void unsubscribeGroup(in int nlFamily, in int interceptorNlPid, in int nlGroup);
+}
diff --git a/wifi/netlinkinterceptor/aidl/aidl_api/android.hardware.net.nlinterceptor/current/android/hardware/net/nlinterceptor/IInterceptor.aidl b/wifi/netlinkinterceptor/aidl/aidl_api/android.hardware.net.nlinterceptor/current/android/hardware/net/nlinterceptor/IInterceptor.aidl
new file mode 100644
index 0000000..3d0f955
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/aidl_api/android.hardware.net.nlinterceptor/current/android/hardware/net/nlinterceptor/IInterceptor.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.net.nlinterceptor;
+@VintfStability
+interface IInterceptor {
+ android.hardware.net.nlinterceptor.InterceptedSocket createSocket(in int nlFamily, in int clientNlPid, in String clientName);
+ void closeSocket(in android.hardware.net.nlinterceptor.InterceptedSocket handle);
+ void subscribeGroup(in android.hardware.net.nlinterceptor.InterceptedSocket handle, in int nlGroup);
+ void unsubscribeGroup(in android.hardware.net.nlinterceptor.InterceptedSocket handle, in int nlGroup);
+}
diff --git a/wifi/netlinkinterceptor/aidl/aidl_api/android.hardware.net.nlinterceptor/current/android/hardware/net/nlinterceptor/InterceptedSocket.aidl b/wifi/netlinkinterceptor/aidl/aidl_api/android.hardware.net.nlinterceptor/current/android/hardware/net/nlinterceptor/InterceptedSocket.aidl
new file mode 100644
index 0000000..b679be5
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/aidl_api/android.hardware.net.nlinterceptor/current/android/hardware/net/nlinterceptor/InterceptedSocket.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.net.nlinterceptor;
+@VintfStability
+parcelable InterceptedSocket {
+ int nlFamily;
+ int portId;
+}
diff --git a/wifi/netlinkinterceptor/aidl/android/hardware/net/nlinterceptor/IInterceptor.aidl b/wifi/netlinkinterceptor/aidl/android/hardware/net/nlinterceptor/IInterceptor.aidl
new file mode 100644
index 0000000..c222a1e
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/android/hardware/net/nlinterceptor/IInterceptor.aidl
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.net.nlinterceptor;
+
+import android.hardware.net.nlinterceptor.InterceptedSocket;
+
+/**
+ * Netlink Interceptor
+ *
+ * This HAL provides a way for Android services to route their Netlink traffic to a location other
+ * than the Kernel. One might want to do this for a variety of reasons:
+ * -> Route Netlink traffic to a different host.
+ * -> Route Netlink traffic to a different VM.
+ * -> Convert Netlink commands into proprietary vendor hardware commands.
+ *
+ * Some important notes regarding Netlink Interceptor.
+ * -> All int values are treated as unsigned.
+ * -> Users of Netlink Interceptor must close their sockets with closeSocket manually.
+ * -> PID != process ID. In this case, it is "port ID", a unique number assigned by the kernel to a
+ * given Netlink socket.
+ * -> Netlink PIDs are only unique per family. This means that for all NETLINK_GENERIC sockets,
+ * there can only be one socket with PID "1234". HOWEVER, there can ALSO be a Netlink socket
+ * using NETLINK_ROUTE which has a PID of "1234". Hence, in order to uniquely identify a Netlink
+ * socket, both the PID and Netlink Family are required.
+ */
+@VintfStability
+interface IInterceptor {
+ /**
+ * Creates a Netlink socket on both the HU and TCU, and a bi-directional gRPC stream to carry
+ * data between them. This must be closed by the caller with closeSocket().
+ *
+ * @param nlFamily - Netlink Family. Support for families other than NETLINK_GENERIC is still
+ * experimental.
+ * @param clientNlPid - Port ID of the caller's Netlink socket.
+ * @param clientName - Human readable name of the caller. Used for debugging.
+ *
+ * @return InterceptedSocket identifying the socket on the HU allocated for the caller.
+ */
+ InterceptedSocket createSocket(in int nlFamily, in int clientNlPid, in String clientName);
+
+ /**
+ * Closes a socket and gRPC stream given the socket's identifier. This must be invoked manually
+ * by the caller of createSocket().
+ *
+ * @param handle - unique identifier for a socket returned by createSocket.
+ */
+ void closeSocket(in InterceptedSocket handle);
+
+ /**
+ * Subscribes a socket on the TCU to a Netlink multicast group.
+ *
+ * @param handle - unique identifier for a socket returned by createSocket.
+ * @param nlGroup - A single Netlink multicast group that the caller wants to subscribe to.
+ */
+ void subscribeGroup(in InterceptedSocket handle, in int nlGroup);
+
+ /**
+ * Unsubscribes a socket on the TCU from a Netlink multicast group.
+ *
+ * @param handle - unique identifier for a socket returned by createSocket.
+ * @param nlGroup - A single Netlink multicast group that the caller wants to unsubscribe from.
+ */
+ void unsubscribeGroup(in InterceptedSocket handle, in int nlGroup);
+}
diff --git a/wifi/netlinkinterceptor/aidl/android/hardware/net/nlinterceptor/InterceptedSocket.aidl b/wifi/netlinkinterceptor/aidl/android/hardware/net/nlinterceptor/InterceptedSocket.aidl
new file mode 100644
index 0000000..d74a556
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/android/hardware/net/nlinterceptor/InterceptedSocket.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.net.nlinterceptor;
+
+/**
+ * Unique identifier for a Netlink socket.
+ */
+@VintfStability
+parcelable InterceptedSocket {
+ /**
+ * Netlink family of the identified socket
+ */
+ int nlFamily;
+
+ /**
+ * Netlink port ID of the identified socket.
+ */
+ int portId;
+}
diff --git a/wifi/netlinkinterceptor/aidl/default/Android.bp b/wifi/netlinkinterceptor/aidl/default/Android.bp
new file mode 100644
index 0000000..686ff19
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "android.hardware.net.nlinterceptor-service.default",
+ init_rc: ["nlinterceptor-default.rc"],
+ vintf_fragments: ["nlinterceptor-default.xml"],
+ vendor: true,
+ relative_install_path: "hw",
+ defaults: ["nlinterceptor@defaults"],
+ shared_libs: [
+ "android.hardware.net.nlinterceptor-V1-ndk",
+ "libbase",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "libnlinterceptor",
+ "libnl++",
+ ],
+ srcs: [
+ "InterceptorRelay.cpp",
+ "NetlinkInterceptor.cpp",
+ "service.cpp",
+ "util.cpp",
+ ],
+}
diff --git a/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp
new file mode 100644
index 0000000..ded9122
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InterceptorRelay.h"
+
+#include <android-base/logging.h>
+#include <libnl++/printer.h>
+#include <poll.h>
+
+#include <chrono>
+
+#include "util.h"
+
+namespace android::nlinterceptor {
+using namespace std::chrono_literals;
+
+static constexpr std::chrono::milliseconds kPollTimeout = 300ms;
+static constexpr bool kSuperVerbose = true;
+
+InterceptorRelay::InterceptorRelay(uint32_t nlFamily, uint32_t clientNlPid,
+ const std::string& clientName)
+ : mClientName(clientName),
+ mNlSocket(std::make_optional<nl::Socket>(nlFamily, 0, 0)),
+ mClientNlPid(clientNlPid) {}
+
+InterceptorRelay::~InterceptorRelay() {
+ mRunning = false;
+ if (mRelayThread.joinable()) mRelayThread.join();
+}
+
+uint32_t InterceptorRelay::getPid() {
+ auto pidMaybe = mNlSocket->getPid();
+ CHECK(pidMaybe.has_value()) << "Failed to get pid of nl::Socket!";
+ return *pidMaybe;
+}
+
+void InterceptorRelay::relayMessages() {
+ pollfd fds[] = {
+ mNlSocket->preparePoll(POLLIN),
+ };
+ while (mRunning) {
+ if (poll(fds, countof(fds), kPollTimeout.count()) < 0) {
+ PLOG(FATAL) << "poll failed";
+ return;
+ }
+ const auto nlsockEvents = fds[0].revents;
+
+ if (isSocketBad(nlsockEvents)) {
+ LOG(ERROR) << "Netlink socket is bad";
+ mRunning = false;
+ return;
+ }
+ if (!isSocketReadable(nlsockEvents)) continue;
+
+ const auto [msgMaybe, sa] = mNlSocket->receiveFrom();
+ if (!msgMaybe.has_value()) {
+ LOG(ERROR) << "Failed to receive Netlink data!";
+ mRunning = false;
+ return;
+ }
+ const auto msg = *msgMaybe;
+ if (!msg.firstOk()) {
+ LOG(ERROR) << "Netlink packet is malformed!";
+ // Test messages might be empty, this isn't fatal.
+ continue;
+ }
+ if constexpr (kSuperVerbose) {
+ LOG(VERBOSE) << "[" << mClientName
+ << "] nlMsg: " << nl::toString(msg, NETLINK_GENERIC);
+ }
+
+ uint32_t destinationPid = 0;
+ if (sa.nl_pid == 0) {
+ destinationPid = mClientNlPid;
+ }
+
+ if (!mNlSocket->send(msg, destinationPid)) {
+ LOG(ERROR) << "Failed to send Netlink message!";
+ mRunning = false;
+ return;
+ }
+ }
+ LOG(VERBOSE) << "[" << mClientName << "] Exiting relay thread!";
+}
+
+bool InterceptorRelay::start() {
+ if (mRunning) {
+ LOG(ERROR)
+ << "Can't relay messages: InterceptorRelay is already running!";
+ return false;
+ }
+ if (mRelayThread.joinable()) {
+ LOG(ERROR) << "relay thread is already running!";
+ return false;
+ }
+ if (!mNlSocket.has_value()) {
+ LOG(ERROR) << "Netlink socket not initialized!";
+ return false;
+ }
+
+ mRunning = true;
+ mRelayThread = std::thread(&InterceptorRelay::relayMessages, this);
+
+ LOG(VERBOSE) << "Relay threads initialized";
+ return true;
+}
+
+bool InterceptorRelay::subscribeGroup(uint32_t nlGroup) {
+ return mNlSocket->addMembership(nlGroup);
+}
+
+bool InterceptorRelay::unsubscribeGroup(uint32_t nlGroup) {
+ return mNlSocket->dropMembership(nlGroup);
+}
+
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h
new file mode 100644
index 0000000..0178c90
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/InterceptorRelay.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <libnl++/Socket.h>
+
+#include <mutex>
+#include <thread>
+
+namespace android::nlinterceptor {
+
+class InterceptorRelay {
+ public:
+ /**
+ * Wrapper around the netlink socket and thread which relays messages.
+ *
+ * \param nlFamily - netlink family to use for the netlink socket.
+ * \param clientNlPid - pid of the client netlink socket.
+ * \param clientName - name of the client to be used for debugging.
+ */
+ InterceptorRelay(uint32_t nlFamily, uint32_t clientNlPid,
+ const std::string& clientName);
+
+ /**
+ * Stops the relay thread if running and destroys itself.
+ */
+ ~InterceptorRelay();
+
+ /**
+ * Returns the PID of the internal Netlink socket.
+ *
+ * \return value of PID,
+ */
+ uint32_t getPid();
+
+ /**
+ * Spawns relay thread.
+ */
+ bool start();
+
+ /**
+ * Subscribes the internal socket to a single Netlink multicast group.
+ *
+ * \param nlGroup - Netlink group to subscribe to.
+ * \returns - true for success, false for failure.
+ */
+ bool subscribeGroup(uint32_t nlGroup);
+
+ /**
+ * Unsubscribes the internal socket from a single Netlink multicast group.
+ *
+ * \param nlGroup - Netlink group to unsubscribe from.
+ * \returns - true for success, false for failure.
+ */
+ bool unsubscribeGroup(uint32_t nlGroup);
+
+ private:
+ std::string mClientName; ///< Name of client (Wificond, for example).
+ std::optional<nl::Socket> mNlSocket;
+ const uint32_t mClientNlPid = 0; ///< pid of client NL socket.
+
+ /**
+ * If set to true, the relay thread should be running. Setting this to false
+ * stops the relay thread.
+ */
+ std::atomic_bool mRunning = false;
+
+ /**
+ * Reads incoming Netlink messages destined for mNlSocket. If from the
+ * kernel, the message is relayed to the client specified in the
+ * constructor. Otherwise, the message is relayed to the kernel. This will
+ * run as long as mRunning is set to true.
+ */
+ void relayMessages();
+
+ std::thread mRelayThread;
+};
+
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp
new file mode 100644
index 0000000..908ecf2
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NetlinkInterceptor.h"
+
+#include <android-base/logging.h>
+#include <libnl++/Socket.h>
+
+namespace android::nlinterceptor {
+
+ndk::ScopedAStatus NetlinkInterceptor::createSocket(
+ int32_t nlFamilyAidl, int32_t clientNlPidAidl,
+ const std::string& clientName, AidlInterceptedSocket* interceptedSocket) {
+ auto nlFamily = static_cast<uint32_t>(nlFamilyAidl);
+ auto clientNlPid = static_cast<uint32_t>(clientNlPidAidl);
+ uint32_t interceptorNlPid = 0;
+
+ std::unique_ptr<InterceptorRelay> interceptor =
+ std::make_unique<InterceptorRelay>(nlFamily, clientNlPid, clientName);
+
+ interceptorNlPid = interceptor->getPid();
+
+ if (interceptorNlPid == 0) {
+ LOG(ERROR) << "Failed to create a Netlink socket for " << clientName
+ << ", " << nlFamily << ":" << clientNlPid;
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ if (mClientMap.find({nlFamily, interceptorNlPid}) != mClientMap.end()) {
+ LOG(ERROR) << "A socket with pid " << interceptorNlPid
+ << " already exists!";
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ if (!interceptor->start()) {
+ LOG(ERROR) << "Failed to start interceptor thread!";
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ if (!mClientMap
+ .emplace(InterceptedSocket(nlFamily, interceptorNlPid),
+ std::move(interceptor))
+ .second) {
+ // If this happens, it is very bad.
+ LOG(FATAL) << "Failed to insert interceptor instance with pid "
+ << interceptorNlPid << " into map!";
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ interceptedSocket->nlFamily = nlFamily;
+ interceptedSocket->portId = interceptorNlPid;
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus NetlinkInterceptor::closeSocket(
+ const AidlInterceptedSocket& interceptedSocket) {
+ InterceptedSocket sock(interceptedSocket);
+
+ auto interceptorIt = mClientMap.find(sock);
+ if (interceptorIt == mClientMap.end()) {
+ LOG(ERROR) << "closeSocket Failed! No such socket " << sock;
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+ mClientMap.erase(interceptorIt);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus NetlinkInterceptor::subscribeGroup(
+ const AidlInterceptedSocket& interceptedSocket, int32_t nlGroupAidl) {
+ InterceptedSocket sock(interceptedSocket);
+ auto nlGroup = static_cast<uint32_t>(nlGroupAidl);
+
+ auto interceptorIt = mClientMap.find(sock);
+ if (interceptorIt == mClientMap.end()) {
+ LOG(ERROR) << "subscribeGroup failed! No such socket " << sock;
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ auto& interceptor = interceptorIt->second;
+ if (!interceptor->subscribeGroup(nlGroup)) {
+ LOG(ERROR) << "Failed to subscribe " << sock << " to " << nlGroup;
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus NetlinkInterceptor::unsubscribeGroup(
+ const AidlInterceptedSocket& interceptedSocket, int32_t nlGroupAidl) {
+ InterceptedSocket sock(interceptedSocket);
+ auto nlGroup = static_cast<uint32_t>(nlGroupAidl);
+
+ auto interceptorIt = mClientMap.find(sock);
+ if (interceptorIt == mClientMap.end()) {
+ LOG(ERROR) << "unsubscribeGroup failed! No such socket " << sock;
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+
+ auto& interceptor = interceptorIt->second;
+ if (!interceptor->unsubscribeGroup(nlGroup)) {
+ LOG(ERROR) << "Failed to unsubscribe " << sock << " from " << nlGroup;
+ return ndk::ScopedAStatus(AStatus_fromStatus(::android::UNKNOWN_ERROR));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h
new file mode 100644
index 0000000..8345654
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/NetlinkInterceptor.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/net/nlinterceptor/BnInterceptor.h>
+#include <libnlinterceptor/libnlinterceptor.h>
+
+#include <map>
+
+#include "InterceptorRelay.h"
+
+namespace android::nlinterceptor {
+
+class NetlinkInterceptor
+ : public ::aidl::android::hardware::net::nlinterceptor::BnInterceptor {
+ using ClientMap =
+ std::map<::android::nlinterceptor::InterceptedSocket,
+ std::unique_ptr<::android::nlinterceptor::InterceptorRelay>>;
+
+ using AidlInterceptedSocket =
+ ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket;
+
+ public:
+ ndk::ScopedAStatus createSocket(
+ int32_t nlFamily, int32_t clientNlPid, const std::string& clientName,
+ AidlInterceptedSocket* interceptedSocket) override;
+
+ ndk::ScopedAStatus closeSocket(
+ const AidlInterceptedSocket& interceptedSocket) override;
+
+ ndk::ScopedAStatus subscribeGroup(
+ const AidlInterceptedSocket& interceptedSocket,
+ int32_t nlGroup) override;
+
+ ndk::ScopedAStatus unsubscribeGroup(
+ const AidlInterceptedSocket& interceptedSocket,
+ int32_t nlGroup) override;
+
+ private:
+ ClientMap mClientMap;
+};
+
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/aidl/default/OWNERS b/wifi/netlinkinterceptor/aidl/default/OWNERS
new file mode 100644
index 0000000..b738dac
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/OWNERS
@@ -0,0 +1,2 @@
+chrisweir@google.com
+twasilczyk@google.com
diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc
new file mode 100644
index 0000000..353cb27
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.rc
@@ -0,0 +1,4 @@
+service nlinterceptor /vendor/bin/hw/android.hardware.net.nlinterceptor-service.default
+ class hal
+ user root
+ group system inet
diff --git a/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml
new file mode 100644
index 0000000..d7d257e
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/nlinterceptor-default.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.net.nlinterceptor</name>
+ <interface>
+ <name>IInterceptor</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/wifi/netlinkinterceptor/aidl/default/service.cpp b/wifi/netlinkinterceptor/aidl/default/service.cpp
new file mode 100644
index 0000000..2aec3a5
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/service.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "NetlinkInterceptor.h"
+
+namespace android::nlinterceptor {
+using namespace std::string_literals;
+
+static void service() {
+ base::SetDefaultTag("nlinterceptor");
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ LOG(DEBUG) << "Netlink Interceptor service starting...";
+
+ // TODO(202549296): Sometimes this causes an Address Sanitizer error.
+ auto interceptor = ndk::SharedRefBase::make<NetlinkInterceptor>();
+ const auto instance = NetlinkInterceptor::descriptor + "/default"s;
+ const auto status = AServiceManager_addService(
+ interceptor->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ LOG(FATAL) << "Netlink Interceptor has stopped";
+}
+
+} // namespace android::nlinterceptor
+
+int main() {
+ ::android::nlinterceptor::service();
+ return 0;
+}
diff --git a/wifi/netlinkinterceptor/aidl/default/util.cpp b/wifi/netlinkinterceptor/aidl/default/util.cpp
new file mode 100644
index 0000000..c734747
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/util.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+#include <poll.h>
+
+namespace android::nlinterceptor {
+
+bool isSocketReadable(const short revents) { return 0 != (revents & POLLIN); }
+
+bool isSocketBad(const short revents) {
+ return 0 != (revents & (POLLERR | POLLHUP | POLLNVAL));
+}
+
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/aidl/default/util.h b/wifi/netlinkinterceptor/aidl/default/util.h
new file mode 100644
index 0000000..9b8ec63
--- /dev/null
+++ b/wifi/netlinkinterceptor/aidl/default/util.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/macros.h>
+
+#include <functional>
+
+namespace android::nlinterceptor {
+
+/**
+ * Handy-dandy helper to get the size of a statically initialized array.
+ *
+ * \param N the array to get the size of.
+ * \return the size of the array.
+ */
+template <typename T, size_t N>
+size_t countof(T (&)[N]) {
+ return N;
+}
+
+/**
+ * Helper to check if socket is readable (POLLIN is set).
+ *
+ * \param revents pollfd.revents value to check.
+ * \return true if socket is ready to read.
+ */
+bool isSocketReadable(short revents);
+
+/**
+ * Helper to check if socket is bad (POLLERR, POLLHUP or POLLNVAL is set).
+ *
+ * \param revents pollfd.revents value to check.
+ * \return true if socket is bad.
+ */
+bool isSocketBad(short revents);
+
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/libnlinterceptor/Android.bp b/wifi/netlinkinterceptor/libnlinterceptor/Android.bp
new file mode 100644
index 0000000..a5e1766
--- /dev/null
+++ b/wifi/netlinkinterceptor/libnlinterceptor/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "nlinterceptor@defaults",
+ cpp_std: "experimental",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wsuggest-override",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+ sanitize: {
+ address: true,
+ undefined: true,
+ all_undefined: true,
+ fuzzer: true,
+ cfi: true,
+ integer_overflow: true,
+ scs: true,
+ },
+ strip: {
+ keep_symbols_and_debug_frame: true,
+ },
+}
+
+cc_library_static {
+ name: "libnlinterceptor",
+ defaults: ["nlinterceptor@defaults"],
+ vendor_available: true,
+ shared_libs: [
+ "android.hardware.net.nlinterceptor-V1-ndk",
+ "libbinder_ndk",
+ ],
+ srcs: [
+ "libnlinterceptor.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h b/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h
new file mode 100644
index 0000000..ac8653e
--- /dev/null
+++ b/wifi/netlinkinterceptor/libnlinterceptor/include/libnlinterceptor/libnlinterceptor.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+
+#include <aidl/android/hardware/net/nlinterceptor/InterceptedSocket.h>
+#include <android-base/unique_fd.h>
+#include <linux/netlink.h>
+
+#include <optional>
+#include <string>
+
+namespace android::nlinterceptor {
+
+/**
+ * Wrapper structure to uniquely identifies a socket that Netlink Interceptor
+ * has allocated for us.
+ */
+struct InterceptedSocket {
+ uint32_t nlFamily;
+ uint32_t portId;
+
+ InterceptedSocket(
+ ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket sock);
+ InterceptedSocket(uint32_t nlFamily, uint32_t portId);
+
+ bool operator<(const InterceptedSocket& other) const;
+ operator sockaddr_nl() const;
+ operator ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket()
+ const;
+};
+
+/**
+ * Output stream operator for InterceptedSocket
+ */
+std::ostream& operator<<(std::ostream& os, const InterceptedSocket& sock);
+
+/**
+ * Checks if an instance Netlink Interceptor exists.
+ *
+ * \return true if supported, false if not.
+ */
+bool isEnabled();
+
+/**
+ * Asks Netlink Interceptor to allocate a socket to which we can send Netlink
+ * traffic.
+ *
+ * \param clientSocket - File descriptor for the client's Netlink socket.
+ * \param clientName - Human readable name of the client application.
+ * \return Identifier for the socket created by Netlink Interceptor, nullopt on
+ * error.
+ */
+std::optional<InterceptedSocket> createSocket(base::borrowed_fd clientSocket,
+ const std::string& clientName);
+
+/**
+ * Asks Netlink Interceptor to close a socket that it created for us previously,
+ * if it exists.
+ *
+ * \param sock - Identifier for the socket created by Netlink Interceptor.
+ */
+void closeSocket(const InterceptedSocket& sock);
+
+/**
+ * Asks Netlink Interceptor to subscribe a socket that it created for us
+ * previously to a specified multicast group.
+ *
+ * \param sock - Identifier for the socket created by Netlink Interceptor.
+ * \param group - A single Netlink multicast group for which we would like to
+ * receive events.
+ * \return true for success, false if something went wrong.
+ */
+bool subscribe(const InterceptedSocket& sock, uint32_t group);
+
+/**
+ * Asks Netlink Interceptor to unsubscribe a socket that it created for us
+ * previously from a specified multicast group.
+ *
+ * \param sock - Identifier for the socket created by Netlink Interceptor.
+ * \param group - A single Netlink multicast group for which we no longer wish
+ * to receive events.
+ * \return true for success, false if something went wrong.
+ */
+bool unsubscribe(const InterceptedSocket& sock, uint32_t group);
+} // namespace android::nlinterceptor
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// C wrappers for libnlinterceptor
+struct android_nlinterceptor_InterceptedSocket {
+ uint32_t nlFamily;
+ uint32_t portId;
+};
+
+bool android_nlinterceptor_isEnabled();
+
+bool android_nlinterceptor_createSocket(
+ int clientSocketFd, const char* clientName,
+ struct android_nlinterceptor_InterceptedSocket* interceptedSocket);
+
+void android_nlinterceptor_closeSocket(
+ const struct android_nlinterceptor_InterceptedSocket* sock);
+
+bool android_nlinterceptor_subscribe(
+ const struct android_nlinterceptor_InterceptedSocket* sock, uint32_t group);
+
+bool android_nlinterceptor_unsubscribe(
+ const struct android_nlinterceptor_InterceptedSocket* sock, uint32_t group);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp b/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
new file mode 100644
index 0000000..575f900
--- /dev/null
+++ b/wifi/netlinkinterceptor/libnlinterceptor/libnlinterceptor.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/net/nlinterceptor/IInterceptor.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android/binder_manager.h>
+#include <libnlinterceptor/libnlinterceptor.h>
+#include <linux/netlink.h>
+
+#include <mutex>
+
+namespace android::nlinterceptor {
+using namespace std::string_literals;
+using namespace ::aidl::android::hardware::net::nlinterceptor;
+using base::borrowed_fd;
+using AidlInterceptedSocket =
+ ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket;
+
+static const auto kServiceName = IInterceptor::descriptor + "/default"s;
+
+InterceptedSocket::InterceptedSocket(
+ ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket sock)
+ : nlFamily(sock.nlFamily), portId(sock.portId) {}
+
+InterceptedSocket::InterceptedSocket(uint32_t nlFamily, uint32_t portId)
+ : nlFamily(nlFamily), portId(portId) {}
+
+std::ostream& operator<<(std::ostream& os, const InterceptedSocket& sock) {
+ return os << "family: " << sock.nlFamily << ", portId: " << sock.portId;
+}
+
+bool InterceptedSocket::operator<(const InterceptedSocket& other) const {
+ if (nlFamily != other.nlFamily) {
+ return nlFamily < other.nlFamily;
+ }
+ return portId < other.portId;
+}
+
+InterceptedSocket::operator sockaddr_nl() const {
+ return {
+ .nl_family = AF_NETLINK,
+ .nl_pad = 0,
+ .nl_pid = portId,
+ .nl_groups = 0,
+ };
+}
+
+InterceptedSocket::operator AidlInterceptedSocket() const {
+ return {
+ .nlFamily = static_cast<int32_t>(nlFamily),
+ .portId = static_cast<int32_t>(portId),
+ };
+}
+
+bool isEnabled() {
+ static std::mutex supportedMutex;
+ static std::optional<bool> interceptorSupported;
+ // Avoid querying service manager when we can cache the result.
+ if (interceptorSupported.has_value()) return *interceptorSupported;
+ std::lock_guard lock(supportedMutex);
+ if (interceptorSupported.has_value()) return *interceptorSupported;
+
+ if (!AServiceManager_isDeclared(kServiceName.c_str())) {
+ interceptorSupported = false;
+ return false;
+ }
+ interceptorSupported = true;
+ return true;
+}
+
+static IInterceptor& getInstance() {
+ static std::mutex instanceMutex;
+ static std::shared_ptr<IInterceptor> interceptorInstance;
+ CHECK(isEnabled()) << "Can't getInstance! Interceptor not supported!";
+ // Don't overwrite the pointer once we've acquired it.
+ if (interceptorInstance != nullptr) return *interceptorInstance;
+ std::lock_guard lock(instanceMutex);
+ if (interceptorInstance != nullptr) return *interceptorInstance;
+ interceptorInstance = IInterceptor::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(kServiceName.c_str())));
+ CHECK(interceptorInstance != nullptr)
+ << "Failed to get Netlink Interceptor service!";
+ return *interceptorInstance;
+}
+
+std::optional<InterceptedSocket> createSocket(borrowed_fd clientSocket,
+ const std::string& clientName) {
+ sockaddr_nl nladdr = {};
+ socklen_t nlsize = sizeof(nladdr);
+ if (getsockname(clientSocket.get(), reinterpret_cast<sockaddr*>(&nladdr),
+ &nlsize) < 0) {
+ PLOG(ERROR) << "Failed to get pid of fd passed by " << clientName;
+ return std::nullopt;
+ }
+
+ ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket
+ interceptedSocket;
+ auto aidlStatus = getInstance().createSocket(
+ nladdr.nl_family, nladdr.nl_pid, clientName, &interceptedSocket);
+ if (!aidlStatus.isOk()) {
+ return std::nullopt;
+ }
+
+ return InterceptedSocket{nladdr.nl_family,
+ uint32_t(interceptedSocket.portId)};
+}
+
+void closeSocket(const InterceptedSocket& sock) {
+ auto aidlStatus = getInstance().closeSocket(sock);
+ if (!aidlStatus.isOk()) {
+ LOG(ERROR) << "Failed to close socket with pid = " << sock.portId;
+ }
+}
+
+bool subscribe(const InterceptedSocket& sock, uint32_t group) {
+ auto aidlStatus = getInstance().subscribeGroup(sock, group);
+ return aidlStatus.isOk();
+}
+
+bool unsubscribe(const InterceptedSocket& sock, uint32_t group) {
+ auto aidlStatus = getInstance().unsubscribeGroup(sock, group);
+ return aidlStatus.isOk();
+}
+
+extern "C" bool android_nlinterceptor_isEnabled() { return isEnabled(); }
+
+extern "C" bool android_nlinterceptor_createSocket(
+ int clientSocketFd, const char* clientName,
+ android_nlinterceptor_InterceptedSocket* interceptedSocket) {
+ if (!clientName || clientSocketFd <= 0) return false;
+ const auto maybeSocket =
+ createSocket(borrowed_fd(clientSocketFd), clientName);
+ if (!maybeSocket) return false;
+ *interceptedSocket = {.nlFamily = maybeSocket->nlFamily,
+ .portId = maybeSocket->portId};
+ return true;
+}
+
+extern "C" void android_nlinterceptor_closeSocket(
+ const android_nlinterceptor_InterceptedSocket* sock) {
+ if (!sock) {
+ LOG(ERROR) << "Can't close socket identified by a null pointer!";
+ return;
+ }
+ closeSocket({sock->nlFamily, sock->portId});
+}
+
+extern "C" bool android_nlinterceptor_subscribe(
+ const android_nlinterceptor_InterceptedSocket* sock, uint32_t group) {
+ if (!sock) return false;
+ return subscribe({sock->nlFamily, sock->portId}, group);
+}
+
+extern "C" bool android_nlinterceptor_unsubscribe(
+ const android_nlinterceptor_InterceptedSocket* sock, uint32_t group) {
+ if (!sock) return false;
+ return unsubscribe({sock->nlFamily, sock->portId}, group);
+}
+
+} // namespace android::nlinterceptor
diff --git a/wifi/netlinkinterceptor/vts/OWNERS b/wifi/netlinkinterceptor/vts/OWNERS
new file mode 100644
index 0000000..b738dac
--- /dev/null
+++ b/wifi/netlinkinterceptor/vts/OWNERS
@@ -0,0 +1,2 @@
+chrisweir@google.com
+twasilczyk@google.com
diff --git a/wifi/netlinkinterceptor/vts/functional/Android.bp b/wifi/netlinkinterceptor/vts/functional/Android.bp
new file mode 100644
index 0000000..33284e8
--- /dev/null
+++ b/wifi/netlinkinterceptor/vts/functional/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+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: "VtsHalNetlinkInterceptorV1_0Test",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ cpp_std: "experimental",
+ srcs: [
+ "interceptor_aidl_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.net.nlinterceptor-V1-ndk",
+ "libbase",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "libgmock",
+ "android.hardware.automotive.can@libnetdevice",
+ "libnl++",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ disable_framework: true,
+}
diff --git a/wifi/netlinkinterceptor/vts/functional/interceptor_aidl_test.cpp b/wifi/netlinkinterceptor/vts/functional/interceptor_aidl_test.cpp
new file mode 100644
index 0000000..b26d8ec
--- /dev/null
+++ b/wifi/netlinkinterceptor/vts/functional/interceptor_aidl_test.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/net/nlinterceptor/IInterceptor.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <gtest/gtest.h>
+#include <libnetdevice/libnetdevice.h>
+#include <libnl++/MessageFactory.h>
+#include <libnl++/Socket.h>
+#include <libnl++/printer.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <chrono>
+#include <thread>
+
+using aidl::android::hardware::net::nlinterceptor::IInterceptor;
+using AidlInterceptedSocket =
+ ::aidl::android::hardware::net::nlinterceptor::InterceptedSocket;
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+class InterceptorAidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ android::base::SetDefaultTag("InterceptorAidlTest");
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ const auto instance = IInterceptor::descriptor + "/default"s;
+ mNlInterceptorService = IInterceptor::fromBinder(
+ ndk::SpAIBinder(AServiceManager_getService(instance.c_str())));
+
+ ASSERT_NE(mNlInterceptorService, nullptr);
+ mSocket = std::make_unique<android::nl::Socket>(NETLINK_ROUTE);
+ ASSERT_TRUE(mSocket->getPid().has_value());
+
+ // If the test broke last run, clean up our mess, don't worry about "no
+ // such device".
+ if (android::netdevice::del(mTestIfaceName)) {
+ LOG(WARNING) << "Test interface wasn't cleaned up on previous run!";
+ }
+ }
+
+ void multicastReceiver();
+
+ std::shared_ptr<IInterceptor> mNlInterceptorService;
+ std::unique_ptr<android::nl::Socket> mSocket;
+ bool mRunning;
+ bool mGotMulticast;
+ const std::string mTestIfaceName = "interceptorvts0";
+};
+
+TEST_P(InterceptorAidlTest, createSocketTest) {
+ // Ask IInterceptor for a socket.
+ AidlInterceptedSocket interceptedSocket;
+ auto aidlStatus = mNlInterceptorService->createSocket(
+ NETLINK_ROUTE, *(mSocket->getPid()), "createSocketTest",
+ &interceptedSocket);
+ ASSERT_TRUE(aidlStatus.isOk());
+ ASSERT_NE(interceptedSocket.portId, 0);
+ uint32_t interceptorPid = interceptedSocket.portId;
+
+ // Ask the kernel to tell us what interfaces are available.
+ android::nl::MessageFactory<rtgenmsg> req(RTM_GETLINK,
+ NLM_F_REQUEST | NLM_F_DUMP);
+ req->rtgen_family = AF_PACKET;
+ sockaddr_nl sa = {.nl_family = AF_NETLINK,
+ .nl_pad = 0,
+ .nl_pid = interceptorPid,
+ .nl_groups = 0};
+ EXPECT_TRUE(mSocket->send(req, sa));
+
+ // We'll likely get back several messages, as indicated by the MULTI flag.
+ unsigned received = 0;
+ for (const auto msg : *mSocket) {
+ ASSERT_NE(msg->nlmsg_type, NLMSG_ERROR);
+ ++received;
+ break;
+ if (msg->nlmsg_type == NLMSG_DONE) {
+ // TODO(202548749): NLMSG_DONE on NETLINK_ROUTE doesn't work?
+ break;
+ }
+ }
+ ASSERT_GE(received, 1);
+
+ // Close the socket and make sure it's stopped working.
+ aidlStatus = mNlInterceptorService->closeSocket(interceptedSocket);
+ EXPECT_TRUE(aidlStatus.isOk());
+ EXPECT_FALSE(mSocket->send(req, sa));
+}
+
+static bool isSocketReadable(const short revents) {
+ return 0 != (revents & POLLIN);
+}
+
+static bool isSocketBad(const short revents) {
+ return 0 != (revents & (POLLERR | POLLHUP | POLLNVAL));
+}
+
+void InterceptorAidlTest::multicastReceiver() {
+ pollfd fds[] = {
+ mSocket->preparePoll(POLLIN),
+ };
+ while (mRunning) {
+ if (poll(fds, 1, 300) < 0) {
+ PLOG(FATAL) << "poll failed";
+ return;
+ }
+ const auto nlsockEvents = fds[0].revents;
+ ASSERT_FALSE(isSocketBad(nlsockEvents));
+ if (!isSocketReadable(nlsockEvents)) continue;
+
+ const auto [msgMaybe, sa] = mSocket->receiveFrom();
+ ASSERT_TRUE(msgMaybe.has_value());
+ auto msg = *msgMaybe;
+
+ // Multicast messages have 0 for their pid and sequence number.
+ if (msg->nlmsg_pid == 0 && msg->nlmsg_seq == 0) {
+ mGotMulticast = true;
+ }
+ }
+}
+
+TEST_P(InterceptorAidlTest, subscribeGroupTest) {
+ // Ask IInterceptor for a socket.
+ AidlInterceptedSocket interceptedSocket;
+ auto aidlStatus = mNlInterceptorService->createSocket(
+ NETLINK_ROUTE, *(mSocket->getPid()), "subscribeGroupTest",
+ &interceptedSocket);
+ ASSERT_TRUE(aidlStatus.isOk());
+ ASSERT_TRUE(interceptedSocket.portId != 0);
+
+ // Listen for interface up/down events.
+ aidlStatus =
+ mNlInterceptorService->subscribeGroup(interceptedSocket, RTNLGRP_LINK);
+ ASSERT_TRUE(aidlStatus.isOk());
+
+ // Start a thread to receive a multicast
+ mRunning = true;
+ mGotMulticast = false;
+ std::thread successfulReceiver(&InterceptorAidlTest::multicastReceiver,
+ this);
+
+ // TODO(201695162): use futures with wait_for instead of a sleep_for().
+ std::this_thread::sleep_for(50ms);
+ // create a network interface and bring it up to trigger a multicast event.
+ ASSERT_TRUE(android::netdevice::add(mTestIfaceName, /*type=*/"dummy"));
+ ASSERT_TRUE(android::netdevice::up(mTestIfaceName));
+ std::this_thread::sleep_for(50ms);
+ EXPECT_TRUE(mGotMulticast);
+ mRunning = false;
+ successfulReceiver.join();
+
+ // Stop listening to interface up/down events.
+ aidlStatus = mNlInterceptorService->unsubscribeGroup(interceptedSocket,
+ RTNLGRP_LINK);
+ ASSERT_TRUE(aidlStatus.isOk());
+
+ // This time, we should hear nothing.
+ mGotMulticast = false;
+ mRunning = true;
+ std::thread unsuccessfulReceiver(&InterceptorAidlTest::multicastReceiver,
+ this);
+ std::this_thread::sleep_for(50ms);
+ ASSERT_TRUE(android::netdevice::down(mTestIfaceName));
+ ASSERT_TRUE(android::netdevice::del(mTestIfaceName));
+ std::this_thread::sleep_for(50ms);
+ EXPECT_FALSE(mGotMulticast);
+ mRunning = false;
+ unsuccessfulReceiver.join();
+
+ aidlStatus = mNlInterceptorService->closeSocket(interceptedSocket);
+ EXPECT_TRUE(aidlStatus.isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InterceptorAidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, InterceptorAidlTest,
+ testing::ValuesIn(android::getAidlHalInstanceNames(
+ IInterceptor::descriptor)),
+ android::PrintInstanceNameToString);