Merge changes from topic "iinterceptor"

* changes:
  Add VTS for Netlink Interceptor
  Add default implementation for Nlinterceptor
  Move interceptor AIDL definition
This commit is contained in:
Chris Weir 2021-12-09 23:30:19 +00:00 committed by Android (Google) Code Review
commit 8c42fdd095
24 changed files with 1466 additions and 0 deletions

View file

@ -331,6 +331,13 @@
<instance>default</instance>
</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>

View file

@ -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,
},
},
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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",
],
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,2 @@
chrisweir@google.com
twasilczyk@google.com

View file

@ -0,0 +1,4 @@
service nlinterceptor /vendor/bin/hw/android.hardware.net.nlinterceptor-service.default
class hal
user root
group system inet

View file

@ -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>

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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"],
}

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,2 @@
chrisweir@google.com
twasilczyk@google.com

View file

@ -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,
}

View file

@ -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);