Merge changes I17c82208,Ia925d8b5 into main
* changes:
Remove eBPF-based port listener
Integrate port forwarding with setting page
diff --git a/android/forwarder_host/src/forwarder_host.rs b/android/forwarder_host/src/forwarder_host.rs
index 26bae89..7496a02 100644
--- a/android/forwarder_host/src/forwarder_host.rs
+++ b/android/forwarder_host/src/forwarder_host.rs
@@ -28,7 +28,7 @@
use std::time::Duration;
use forwarder::forwarder::ForwarderSession;
-use jni::objects::{JObject, JValue};
+use jni::objects::{JIntArray, JObject, JValue};
use jni::sys::jint;
use jni::JNIEnv;
use log::{debug, error, info, warn};
@@ -45,11 +45,16 @@
static SHUTDOWN_EVT: LazyLock<EventFd> =
LazyLock::new(|| EventFd::new().expect("Could not create shutdown eventfd"));
+static UPDATE_EVT: LazyLock<EventFd> =
+ LazyLock::new(|| EventFd::new().expect("Could not create update eventfd"));
+
+static UPDATE_QUEUE: LazyLock<Arc<Mutex<VecDeque<u16>>>> =
+ LazyLock::new(|| Arc::new(Mutex::new(VecDeque::new())));
+
#[remain::sorted]
#[derive(Debug)]
enum Error {
BindVsock(io::Error),
- EventFdNew(nix::Error),
IncorrectCid(u32),
LaunchForwarderGuest(jni::errors::Error),
NoListenerForPort(u16),
@@ -62,7 +67,6 @@
TcpAccept(io::Error),
TcpListenerPort(io::Error),
UpdateEventRead(nix::Error),
- UpdateEventWrite(nix::Error),
VsockAccept(io::Error),
VsockAcceptTimeout,
VsockListenerPort(io::Error),
@@ -78,7 +82,6 @@
#[remain::sorted]
match self {
BindVsock(e) => write!(f, "failed to bind vsock: {}", e),
- EventFdNew(e) => write!(f, "failed to create eventfd: {}", e),
IncorrectCid(cid) => write!(f, "chunnel connection from unexpected cid {}", cid),
LaunchForwarderGuest(e) => write!(f, "failed to launch forwarder_guest {}", e),
NoListenerForPort(port) => write!(f, "could not find listener for port: {}", port),
@@ -93,7 +96,6 @@
write!(f, "failed to read local sockaddr for tcp listener: {}", e)
}
UpdateEventRead(e) => write!(f, "failed to read update eventfd: {}", e),
- UpdateEventWrite(e) => write!(f, "failed to write update eventfd: {}", e),
VsockAccept(e) => write!(f, "failed to accept vsock: {}", e),
VsockAcceptTimeout => write!(f, "timed out waiting for vsock connection"),
VsockListenerPort(e) => write!(f, "failed to get vsock listener port: {}", e),
@@ -101,12 +103,6 @@
}
}
-/// A TCP forwarding target. Uniquely identifies a listening port in a given container.
-struct TcpForwardTarget {
- pub port: u16,
- pub vsock_cid: u32,
-}
-
/// A tag that uniquely identifies a particular forwarding session. This has arbitrarily been
/// chosen as the fd of the local (TCP) socket.
type SessionTag = u32;
@@ -127,7 +123,6 @@
struct PortListeners {
tcp4_listener: TcpListener,
tcp6_listener: TcpListener,
- forward_target: TcpForwardTarget,
}
/// SocketFamily specifies whether a socket uses IPv4 or IPv6.
@@ -140,25 +135,18 @@
struct ForwarderSessions<'a> {
listening_ports: BTreeMap<u16, PortListeners>,
tcp4_forwarders: HashMap<SessionTag, ForwarderSession>,
- update_evt: EventFd,
- update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+ cid: u32,
jni_env: JNIEnv<'a>,
jni_cb: JObject<'a>,
}
impl<'a> ForwarderSessions<'a> {
/// Creates a new instance of ForwarderSessions.
- fn new(
- update_evt: EventFd,
- update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
- jni_env: JNIEnv<'a>,
- jni_cb: JObject<'a>,
- ) -> Result<Self> {
+ fn new(cid: i32, jni_env: JNIEnv<'a>, jni_cb: JObject<'a>) -> Result<Self> {
Ok(ForwarderSessions {
listening_ports: BTreeMap::new(),
tcp4_forwarders: HashMap::new(),
- update_evt,
- update_queue,
+ cid: cid as u32,
jni_env,
jni_cb,
})
@@ -167,12 +155,11 @@
/// Adds or removes listeners based on the latest listening ports from the D-Bus thread.
fn process_update_queue(&mut self, poll_ctx: &PollContext<Token>) -> Result<()> {
// Unwrap of LockResult is customary.
- let mut update_queue = self.update_queue.lock().unwrap();
+ let mut update_queue = UPDATE_QUEUE.lock().unwrap();
let mut active_ports: BTreeSet<u16> = BTreeSet::new();
// Add any new listeners first.
- while let Some(target) = update_queue.pop_front() {
- let port = target.port;
+ while let Some(port) = update_queue.pop_front() {
// Ignore privileged ports.
if port < 1024 {
continue;
@@ -201,7 +188,7 @@
poll_ctx
.add(&tcp6_listener, Token::Ipv6Listener(port))
.map_err(Error::PollContextAdd)?;
- o.insert(PortListeners { tcp4_listener, tcp6_listener, forward_target: target });
+ o.insert(PortListeners { tcp4_listener, tcp6_listener });
}
active_ports.insert(port);
}
@@ -218,7 +205,7 @@
}
// Consume the eventfd.
- self.update_evt.read().map_err(Error::UpdateEventRead)?;
+ UPDATE_EVT.read().map_err(Error::UpdateEventRead)?;
Ok(())
}
@@ -240,12 +227,8 @@
// This session should be dropped if any of the PollContext setup fails. Since the only
// extant fds for the underlying sockets will be closed, they will be unregistered from
// epoll set automatically.
- let session = create_forwarder_session(
- listener,
- &port_listeners.forward_target,
- &mut self.jni_env,
- &self.jni_cb,
- )?;
+ let session =
+ create_forwarder_session(listener, self.cid, &mut self.jni_env, &self.jni_cb)?;
let tag = session.local_stream().as_raw_fd() as u32;
@@ -293,7 +276,7 @@
fn run(&mut self) -> Result<()> {
let poll_ctx: PollContext<Token> = PollContext::new().map_err(Error::PollContextNew)?;
- poll_ctx.add(&self.update_evt, Token::UpdatePorts).map_err(Error::PollContextAdd)?;
+ poll_ctx.add(&*UPDATE_EVT, Token::UpdatePorts).map_err(Error::PollContextAdd)?;
poll_ctx.add(&*SHUTDOWN_EVT, Token::Shutdown).map_err(Error::PollContextAdd)?;
loop {
@@ -340,7 +323,7 @@
/// Creates a forwarder session from a `listener` that has a pending connection to accept.
fn create_forwarder_session(
listener: &TcpListener,
- target: &TcpForwardTarget,
+ cid: u32,
jni_env: &mut JNIEnv,
jni_cb: &JObject,
) -> Result<ForwarderSession> {
@@ -376,7 +359,7 @@
Some(_) => {
let (vsock_stream, sockaddr) = vsock_listener.accept().map_err(Error::VsockAccept)?;
- if sockaddr.cid() != target.vsock_cid {
+ if sockaddr.cid() != cid {
Err(Error::IncorrectCid(sockaddr.cid()))
} else {
Ok(ForwarderSession::new(tcp_stream.into(), vsock_stream.into()))
@@ -386,33 +369,10 @@
}
}
-fn update_listening_ports(
- update_queue: &Arc<Mutex<VecDeque<TcpForwardTarget>>>,
- update_evt: &EventFd,
- cid: i32,
-) -> Result<()> {
- let mut update_queue = update_queue.lock().unwrap();
-
- // TODO(b/340126051): Bring listening ports from the guest.
- update_queue.push_back(TcpForwardTarget {
- port: 12345, /* Example value for testing */
- vsock_cid: cid as u32,
- });
-
- update_evt.write(1).map_err(Error::UpdateEventWrite)?;
- Ok(())
-}
-
// TODO(b/340126051): Host can receive opened ports from the guest.
fn run_forwarder_host(cid: i32, jni_env: JNIEnv, jni_cb: JObject) -> Result<()> {
debug!("Starting forwarder_host");
- let update_evt = EventFd::new().map_err(Error::EventFdNew)?;
- let update_queue = Arc::new(Mutex::new(VecDeque::new()));
-
- // TODO(b/340126051): Instead of one-time execution, bring port info with separated thread.
- update_listening_ports(&update_queue, &update_evt, cid)?;
-
- let mut sessions = ForwarderSessions::new(update_evt, update_queue, jni_env, jni_cb)?;
+ let mut sessions = ForwarderSessions::new(cid, jni_env, jni_cb)?;
sessions.run()
}
@@ -442,3 +402,22 @@
) {
SHUTDOWN_EVT.write(1).expect("Failed to write shutdown event FD");
}
+
+/// JNI function for updating listening ports.
+#[no_mangle]
+pub extern "C" fn Java_com_android_virtualization_vmlauncher_DebianServiceImpl_updateListeningPorts(
+ env: JNIEnv,
+ _class: JObject,
+ ports: JIntArray,
+) {
+ let length = env.get_array_length(&ports).expect("Failed to get length of port array");
+ let mut buf = vec![0; length as usize];
+ env.get_int_array_region(ports, 0, &mut buf).expect("Failed to get port array");
+
+ let mut update_queue = UPDATE_QUEUE.lock().unwrap();
+ update_queue.clear();
+ for port in buf {
+ update_queue.push_back(port.try_into().expect("Failed to add port into update queue"));
+ }
+ UPDATE_EVT.write(1).expect("failed to write update eventfd");
+}
diff --git a/guest/port_listener/build.sh b/guest/port_listener/build.sh
deleted file mode 100755
index a1d0205..0000000
--- a/guest/port_listener/build.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-
-set -e
-
-check_sudo() {
- if [ "$EUID" -ne 0 ]; then
- echo "Please run as root."
- exit
- fi
-}
-
-install_prerequisites() {
- apt update
- apt install --no-install-recommends --assume-yes \
- bpftool \
- clang \
- libbpf-dev \
- libgoogle-glog-dev \
- libstdc++-14-dev
-}
-
-build_port_listener() {
- cp $(dirname $0)/src/* ${workdir}
- out_dir=${PWD}
- pushd ${workdir}
- bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
- clang \
- -O2 \
- -Wall \
- -target bpf \
- -g \
- -c listen_tracker.ebpf.c \
- -o listen_tracker.ebpf.o
- bpftool gen skeleton listen_tracker.ebpf.o > listen_tracker.skel.h
- clang++ \
- -O2 \
- -Wall \
- -lbpf \
- -lglog \
- -o port_listener \
- main.cc
- cp port_listener ${out_dir}
- popd
-}
-
-clean_up() {
- rm -rf ${workdir}
-}
-trap clean_up EXIT
-workdir=$(mktemp -d)
-
-check_sudo
-install_prerequisites
-build_port_listener
diff --git a/guest/port_listener/src/common.h b/guest/port_listener/src/common.h
deleted file mode 100644
index d6e507c..0000000
--- a/guest/port_listener/src/common.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2024 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.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/common.h
-
-#ifndef VM_TOOLS_PORT_LISTENER_COMMON_H_
-#define VM_TOOLS_PORT_LISTENER_COMMON_H_
-
-enum State {
- kPortListenerUp,
- kPortListenerDown,
-};
-
-struct event {
- enum State state;
- uint16_t port;
-};
-
-#endif // VM_TOOLS_PORT_LISTENER_COMMON_H_
diff --git a/guest/port_listener/src/listen_tracker.ebpf.c b/guest/port_listener/src/listen_tracker.ebpf.c
deleted file mode 100644
index 9e98aad..0000000
--- a/guest/port_listener/src/listen_tracker.ebpf.c
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2024 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.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/listen_tracker.ebpf.c
-
-// bpf_helpers.h uses types defined here
-#include <bpf/bpf_helpers.h>
-
-#include "common.h"
-#include "vmlinux.h"
-
-// For some reason 6.1 doesn't include these symbols in the debug build
-// so they don't get included in vmlinux.h. These features have existed since
-// well before 6.1.
-#define BPF_F_NO_PREALLOC (1U << 0)
-#define BPF_ANY 0
-
-struct {
- __uint(type, BPF_MAP_TYPE_RINGBUF);
- __uint(max_entries, 1 << 24);
-} events SEC(".maps");
-
-struct {
- __uint(type, BPF_MAP_TYPE_HASH);
- __type(key, struct sock*);
- __type(value, __u8);
- __uint(max_entries, 65535);
- __uint(map_flags, BPF_F_NO_PREALLOC);
-} sockmap SEC(".maps");
-
-const __u8 set_value = 0;
-
-SEC("tp/sock/inet_sock_set_state")
-int tracepoint_inet_sock_set_state(struct trace_event_raw_inet_sock_set_state* ctx) {
- // We don't support anything other than TCP.
- if (ctx->protocol != IPPROTO_TCP) {
- return 0;
- }
- struct sock* sk = (struct sock*)ctx->skaddr;
- // If we're transitioning away from LISTEN but we don't know about this
- // socket yet then don't report anything.
- if (ctx->oldstate == BPF_TCP_LISTEN && bpf_map_lookup_elem(&sockmap, &sk) == NULL) {
- return 0;
- }
- // If we aren't transitioning to or from TCP_LISTEN then we don't care.
- if (ctx->newstate != BPF_TCP_LISTEN && ctx->oldstate != BPF_TCP_LISTEN) {
- return 0;
- }
-
- struct event* ev;
- ev = bpf_ringbuf_reserve(&events, sizeof(*ev), 0);
- if (!ev) {
- return 0;
- }
- ev->port = ctx->sport;
-
- if (ctx->newstate == BPF_TCP_LISTEN) {
- bpf_map_update_elem(&sockmap, &sk, &set_value, BPF_ANY);
- ev->state = kPortListenerUp;
- }
- if (ctx->oldstate == BPF_TCP_LISTEN) {
- bpf_map_delete_elem(&sockmap, &sk);
- ev->state = kPortListenerDown;
- }
- bpf_ringbuf_submit(ev, 0);
-
- return 0;
-}
diff --git a/guest/port_listener/src/main.cc b/guest/port_listener/src/main.cc
deleted file mode 100644
index 1caceaf..0000000
--- a/guest/port_listener/src/main.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2024 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.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/main.cc
-
-#include <bpf/libbpf.h>
-#include <bpf/libbpf_legacy.h>
-#include <glog/logging.h>
-#include <linux/vm_sockets.h> // Needs to come after sys/socket.h
-#include <sys/socket.h>
-
-#include <memory>
-#include <unordered_map>
-
-#include "common.h"
-#include "listen_tracker.skel.h"
-
-typedef std::unordered_map<int, int> port_usage_map;
-
-namespace port_listener {
-namespace {
-
-int HandleEvent(void* ctx, void* const data, size_t size) {
- port_usage_map* map = reinterpret_cast<port_usage_map*>(ctx);
- const struct event* ev = (struct event*)data;
-
- switch (ev->state) {
- case kPortListenerUp:
- (*map)[ev->port]++;
- break;
-
- case kPortListenerDown:
- if ((*map)[ev->port] > 0) {
- (*map)[ev->port]--;
- } else {
- LOG(INFO) << "Received down event while port count was 0; ignoring";
- }
-
- break;
-
- default:
- LOG(ERROR) << "Unknown event state " << ev->state;
- }
-
- LOG(INFO) << "Listen event: port=" << ev->port << " state=" << ev->state;
-
- return 0;
-}
-
-typedef std::unique_ptr<struct ring_buffer, decltype(&ring_buffer__free)> ring_buffer_ptr;
-typedef std::unique_ptr<listen_tracker_ebpf, decltype(&listen_tracker_ebpf__destroy)>
- listen_tracker_ptr;
-
-// BPFProgram tracks the state and resources of the listen_tracker BPF program.
-class BPFProgram {
-public:
- // Default movable but not copyable.
- BPFProgram(BPFProgram&& other) = default;
- BPFProgram(const BPFProgram& other) = delete;
- BPFProgram& operator=(BPFProgram&& other) = default;
- BPFProgram& operator=(const BPFProgram& other) = delete;
-
- // Load loads the listen_tracker BPF program and prepares it for polling. On
- // error nullptr is returned.
- static std::unique_ptr<BPFProgram> Load() {
- auto* skel = listen_tracker_ebpf__open();
- if (!skel) {
- PLOG(ERROR) << "Failed to open listen_tracker BPF skeleton";
- return nullptr;
- }
- listen_tracker_ptr skeleton(skel, listen_tracker_ebpf__destroy);
-
- int err = listen_tracker_ebpf__load(skeleton.get());
- if (err) {
- PLOG(ERROR) << "Failed to load listen_tracker BPF program";
- return nullptr;
- }
-
- auto map = std::make_unique<port_usage_map>();
- auto* rb = ring_buffer__new(bpf_map__fd(skel->maps.events), HandleEvent, map.get(), NULL);
- if (!rb) {
- PLOG(ERROR) << "Failed to open ring buffer for listen_tracker";
- return nullptr;
- }
- ring_buffer_ptr ringbuf(rb, ring_buffer__free);
-
- err = listen_tracker_ebpf__attach(skeleton.get());
- if (err) {
- PLOG(ERROR) << "Failed to attach listen_tracker";
- return nullptr;
- }
-
- return std::unique_ptr<BPFProgram>(
- new BPFProgram(std::move(skeleton), std::move(ringbuf), std::move(map)));
- }
-
- // Poll waits for the listen_tracker BPF program to post a new event to the
- // ring buffer. BPFProgram handles integrating this new event into the
- // port_usage map and callers should consult port_usage() after Poll returns
- // for the latest data.
- const bool Poll() {
- int err = ring_buffer__poll(rb_.get(), -1);
- if (err < 0) {
- LOG(ERROR) << "Error polling ring buffer ret=" << err;
- return false;
- }
-
- return true;
- }
-
- const port_usage_map& port_usage() { return *port_usage_; }
-
-private:
- BPFProgram(listen_tracker_ptr&& skeleton, ring_buffer_ptr&& rb,
- std::unique_ptr<port_usage_map>&& port_usage)
- : skeleton_(std::move(skeleton)),
- rb_(std::move(rb)),
- port_usage_(std::move(port_usage)) {}
-
- listen_tracker_ptr skeleton_;
- ring_buffer_ptr rb_;
- std::unique_ptr<port_usage_map> port_usage_;
-};
-
-} // namespace
-} // namespace port_listener
-
-int main(int argc, char** argv) {
- google::InitGoogleLogging(argv[0]);
- libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
-
- // Load our BPF program.
- auto program = port_listener::BPFProgram::Load();
- if (program == nullptr) {
- LOG(ERROR) << "Failed to load BPF program";
- return EXIT_FAILURE;
- }
-
- // main loop: poll for listen updates
- for (;;) {
- if (!program->Poll()) {
- LOG(ERROR) << "Failure while polling BPF program";
- return EXIT_FAILURE;
- }
- // port_usage will be updated with the latest usage data
-
- for (auto it : program->port_usage()) {
- if (it.second <= 0) {
- continue;
- }
- // TODO(b/340126051): Add listening TCP4 ports.
- }
- // TODO(b/340126051): Notify port information to the guest agent.
- }
-}
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
index 61679f2..c64ff77 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
@@ -16,6 +16,8 @@
package com.android.virtualization.vmlauncher;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.Keep;
@@ -28,17 +30,47 @@
import io.grpc.stub.StreamObserver;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
final class DebianServiceImpl extends DebianServiceGrpc.DebianServiceImplBase {
public static final String TAG = "DebianService";
+ private static final String PREFERENCE_FILE_KEY =
+ "com.android.virtualization.terminal.PREFERENCE_FILE_KEY";
+ private static final String PREFERENCE_FORWARDING_PORTS = "PREFERENCE_FORWARDING_PORTS";
+ private static final String PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX =
+ "PREFERENCE_FORWARDING_PORT_IS_ENABLED_";
+
+ private final Context mContext;
+ private final SharedPreferences mSharedPref;
+ private SharedPreferences.OnSharedPreferenceChangeListener mPortForwardingListener;
private final DebianServiceCallback mCallback;
+
static {
System.loadLibrary("forwarder_host_jni");
}
- protected DebianServiceImpl(DebianServiceCallback callback) {
+ DebianServiceImpl(Context context, DebianServiceCallback callback) {
super();
mCallback = callback;
+ mContext = context;
+ mSharedPref = mContext.getSharedPreferences(PREFERENCE_FILE_KEY, Context.MODE_PRIVATE);
+ // TODO(b/340126051): Instead of putting fixed value, receive active port list info from the
+ // guest.
+ if (!mSharedPref.contains(PREFERENCE_FORWARDING_PORTS)) {
+ SharedPreferences.Editor editor = mSharedPref.edit();
+ Set<String> ports = new HashSet<>();
+ for (int port = 8080; port < 8090; port++) {
+ ports.add(Integer.toString(port));
+ editor.putBoolean(
+ PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX + Integer.toString(port),
+ false);
+ }
+ editor.putStringSet(PREFERENCE_FORWARDING_PORTS, ports);
+ editor.apply();
+ }
}
@Override
@@ -55,6 +87,19 @@
public void openForwardingRequestQueue(
QueueOpeningRequest request, StreamObserver<ForwardingRequestItem> responseObserver) {
Log.d(DebianServiceImpl.TAG, "OpenForwardingRequestQueue");
+ mPortForwardingListener =
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(
+ SharedPreferences sharedPreferences, String key) {
+ if (key.startsWith(PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX)
+ || key.equals(PREFERENCE_FORWARDING_PORTS)) {
+ updateListeningPorts();
+ }
+ }
+ };
+ mSharedPref.registerOnSharedPreferenceChangeListener(mPortForwardingListener);
+ updateListeningPorts();
runForwarderHost(request.getCid(), new ForwarderHostCallback(responseObserver));
responseObserver.onCompleted();
}
@@ -79,7 +124,32 @@
private static native void runForwarderHost(int cid, ForwarderHostCallback callback);
- public static native void terminateForwarderHost();
+ private static native void terminateForwarderHost();
+
+ void killForwarderHost() {
+ Log.d(DebianServiceImpl.TAG, "Stopping port forwarding");
+ if (mPortForwardingListener != null) {
+ mSharedPref.unregisterOnSharedPreferenceChangeListener(mPortForwardingListener);
+ terminateForwarderHost();
+ }
+ }
+
+ private static native void updateListeningPorts(int[] ports);
+
+ private void updateListeningPorts() {
+ updateListeningPorts(
+ mSharedPref
+ .getStringSet(PREFERENCE_FORWARDING_PORTS, Collections.emptySet())
+ .stream()
+ .filter(
+ port ->
+ mSharedPref.getBoolean(
+ PREFERENCE_FORWARDING_PORT_IS_ENABLED_PREFIX + port,
+ false))
+ .map(Integer::valueOf)
+ .mapToInt(Integer::intValue)
+ .toArray());
+ }
protected interface DebianServiceCallback {
void onIpAddressAvailable(String ipAddr);
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
index 277ec92..846fd26 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
@@ -60,6 +60,7 @@
private VirtualMachine mVirtualMachine;
private ResultReceiver mResultReceiver;
private Server mServer;
+ private DebianServiceImpl mDebianService;
@Override
public IBinder onBind(Intent intent) {
@@ -127,6 +128,7 @@
@Override
public void onDestroy() {
super.onDestroy();
+ stopDebianServer();
if (mVirtualMachine != null) {
if (mVirtualMachine.getStatus() == VirtualMachine.STATUS_RUNNING) {
try {
@@ -140,7 +142,6 @@
mExecutorService = null;
mVirtualMachine = null;
}
- stopDebianServer();
}
private void startDebianServer() {
@@ -172,10 +173,11 @@
try {
// TODO(b/372666638): gRPC for java doesn't support vsock for now.
int port = 0;
+ mDebianService = new DebianServiceImpl(this, this);
mServer =
OkHttpServerBuilder.forPort(port, InsecureServerCredentials.create())
.intercept(interceptor)
- .addService(new DebianServiceImpl(this))
+ .addService(mDebianService)
.build()
.start();
} catch (IOException e) {
@@ -197,8 +199,10 @@
}
private void stopDebianServer() {
+ if (mDebianService != null) {
+ mDebianService.killForwarderHost();
+ }
if (mServer != null) {
- DebianServiceImpl.terminateForwarderHost();
mServer.shutdown();
}
}