Merge "Always use versioned VNDK directory"
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 31cb853..44ed3a2 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -117,6 +117,7 @@
     atexit(adb_server_cleanup);
 
     init_transport_registration();
+    init_reconnect_handler();
     init_mdns_transport_discovery();
 
     usb_init();
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 32bf029..ddd3ff0 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -36,10 +36,11 @@
 
 
 @contextlib.contextmanager
-def fake_adb_server(protocol=socket.AF_INET, port=0):
-    """Creates a fake ADB server that just replies with a CNXN packet."""
+def fake_adbd(protocol=socket.AF_INET, port=0):
+    """Creates a fake ADB daemon that just replies with a CNXN packet."""
 
     serversock = socket.socket(protocol, socket.SOCK_STREAM)
+    serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     if protocol == socket.AF_INET:
         serversock.bind(('127.0.0.1', port))
     else:
@@ -60,31 +61,33 @@
         rlist = [readpipe, serversock]
         cnxn_sent = {}
         while True:
-            ready, _, _ = select.select(rlist, [], [])
-            for r in ready:
-                if r == readpipe:
+            read_ready, _, _ = select.select(rlist, [], [])
+            for ready in read_ready:
+                if ready == readpipe:
                     # Closure pipe
-                    os.close(r)
+                    os.close(ready)
                     serversock.shutdown(socket.SHUT_RDWR)
                     serversock.close()
                     return
-                elif r == serversock:
+                elif ready == serversock:
                     # Server socket
-                    conn, _ = r.accept()
+                    conn, _ = ready.accept()
                     rlist.append(conn)
                 else:
                     # Client socket
-                    data = r.recv(1024)
-                    if not data:
-                        if r in cnxn_sent:
-                            del cnxn_sent[r]
-                        rlist.remove(r)
+                    data = ready.recv(1024)
+                    if not data or data.startswith('OPEN'):
+                        if ready in cnxn_sent:
+                            del cnxn_sent[ready]
+                        ready.shutdown(socket.SHUT_RDWR)
+                        ready.close()
+                        rlist.remove(ready)
                         continue
-                    if r in cnxn_sent:
+                    if ready in cnxn_sent:
                         continue
-                    cnxn_sent[r] = True
-                    r.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
-                                          'device::ro.product.name=fakeadb'))
+                    cnxn_sent[ready] = True
+                    ready.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
+                                              'device::ro.product.name=fakeadb'))
 
     port = serversock.getsockname()[1]
     server_thread = threading.Thread(target=_handle)
@@ -97,8 +100,52 @@
         server_thread.join()
 
 
-class NonApiTest(unittest.TestCase):
-    """Tests for ADB that aren't a part of the AndroidDevice API."""
+@contextlib.contextmanager
+def adb_connect(unittest, serial):
+    """Context manager for an ADB connection.
+
+    This automatically disconnects when done with the connection.
+    """
+
+    output = subprocess.check_output(['adb', 'connect', serial])
+    unittest.assertEqual(output.strip(), 'connected to {}'.format(serial))
+
+    try:
+        yield
+    finally:
+        # Perform best-effort disconnection. Discard the output.
+        subprocess.Popen(['adb', 'disconnect', serial],
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE).communicate()
+
+
+@contextlib.contextmanager
+def adb_server():
+    """Context manager for an ADB server.
+
+    This creates an ADB server and returns the port it's listening on.
+    """
+
+    port = 5038
+    # Kill any existing server on this non-default port.
+    subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+                            stderr=subprocess.STDOUT)
+    read_pipe, write_pipe = os.pipe()
+    proc = subprocess.Popen(['adb', '-L', 'tcp:localhost:{}'.format(port),
+                             'fork-server', 'server',
+                             '--reply-fd', str(write_pipe)])
+    try:
+        os.close(write_pipe)
+        greeting = os.read(read_pipe, 1024)
+        assert greeting == 'OK\n', repr(greeting)
+        yield port
+    finally:
+        proc.terminate()
+        proc.wait()
+
+
+class CommandlineTest(unittest.TestCase):
+    """Tests for the ADB commandline."""
 
     def test_help(self):
         """Make sure we get _something_ out of help."""
@@ -120,28 +167,37 @@
                 revision_line, r'^Revision [0-9a-f]{12}-android$')
 
     def test_tcpip_error_messages(self):
-        p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-        out, _ = p.communicate()
-        self.assertEqual(1, p.returncode)
+        """Make sure 'adb tcpip' parsing is sane."""
+        proc = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        out, _ = proc.communicate()
+        self.assertEqual(1, proc.returncode)
         self.assertIn('requires an argument', out)
 
-        p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
-                             stderr=subprocess.STDOUT)
-        out, _ = p.communicate()
-        self.assertEqual(1, p.returncode)
+        proc = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT)
+        out, _ = proc.communicate()
+        self.assertEqual(1, proc.returncode)
         self.assertIn('invalid port', out)
 
-    # Helper method that reads a pipe until it is closed, then sets the event.
-    def _read_pipe_and_set_event(self, pipe, event):
-        x = pipe.read()
+
+class ServerTest(unittest.TestCase):
+    """Tests for the ADB server."""
+
+    @staticmethod
+    def _read_pipe_and_set_event(pipe, event):
+        """Reads a pipe until it is closed, then sets the event."""
+        pipe.read()
         event.set()
 
-    # Test that launch_server() does not let the adb server inherit
-    # stdin/stdout/stderr handles which can cause callers of adb.exe to hang.
-    # This test also runs fine on unix even though the impetus is an issue
-    # unique to Windows.
     def test_handle_inheritance(self):
+        """Test that launch_server() does not inherit handles.
+
+        launch_server() should not let the adb server inherit
+        stdin/stdout/stderr handles, which can cause callers of adb.exe to hang.
+        This test also runs fine on unix even though the impetus is an issue
+        unique to Windows.
+        """
         # This test takes 5 seconds to run on Windows: if there is no adb server
         # running on the the port used below, adb kill-server tries to make a
         # TCP connection to a closed port and that takes 1 second on Windows;
@@ -163,29 +219,30 @@
 
         try:
             # Run the adb client and have it start the adb server.
-            p = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
-                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
+            proc = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
+                                    stdin=subprocess.PIPE,
+                                    stdout=subprocess.PIPE,
+                                    stderr=subprocess.PIPE)
 
             # Start threads that set events when stdout/stderr are closed.
             stdout_event = threading.Event()
             stdout_thread = threading.Thread(
-                    target=self._read_pipe_and_set_event,
-                    args=(p.stdout, stdout_event))
+                target=ServerTest._read_pipe_and_set_event,
+                args=(proc.stdout, stdout_event))
             stdout_thread.daemon = True
             stdout_thread.start()
 
             stderr_event = threading.Event()
             stderr_thread = threading.Thread(
-                    target=self._read_pipe_and_set_event,
-                    args=(p.stderr, stderr_event))
+                target=ServerTest._read_pipe_and_set_event,
+                args=(proc.stderr, stderr_event))
             stderr_thread.daemon = True
             stderr_thread.start()
 
             # Wait for the adb client to finish. Once that has occurred, if
             # stdin/stderr/stdout are still open, it must be open in the adb
             # server.
-            p.wait()
+            proc.wait()
 
             # Try to write to stdin which we expect is closed. If it isn't
             # closed, we should get an IOError. If we don't get an IOError,
@@ -193,7 +250,7 @@
             # probably letting the adb server inherit stdin which would be
             # wrong.
             with self.assertRaises(IOError):
-                p.stdin.write('x')
+                proc.stdin.write('x')
 
             # Wait a few seconds for stdout/stderr to be closed (in the success
             # case, this won't wait at all). If there is a timeout, that means
@@ -207,8 +264,12 @@
             subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
                                     stderr=subprocess.STDOUT)
 
-    # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+
+class EmulatorTest(unittest.TestCase):
+    """Tests for the emulator connection."""
+
     def _reset_socket_on_close(self, sock):
+        """Use SO_LINGER to cause TCP RST segment to be sent on socket close."""
         # The linger structure is two shorts on Windows, but two ints on Unix.
         linger_format = 'hh' if os.name == 'nt' else 'ii'
         l_onoff = 1
@@ -227,7 +288,7 @@
         Bug: https://code.google.com/p/android/issues/detail?id=21021
         """
         with contextlib.closing(
-                socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+            socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
             # Use SO_REUSEADDR so subsequent runs of the test can grab the port
             # even if it is in TIME_WAIT.
             listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -237,7 +298,7 @@
 
             # Now that listening has started, start adb emu kill, telling it to
             # connect to our mock emulator.
-            p = subprocess.Popen(
+            proc = subprocess.Popen(
                 ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
                 stderr=subprocess.STDOUT)
 
@@ -246,12 +307,16 @@
                 # If WSAECONNABORTED (10053) is raised by any socket calls,
                 # then adb probably isn't reading the data that we sent it.
                 conn.sendall('Android Console: type \'help\' for a list ' +
-                                'of commands\r\n')
+                             'of commands\r\n')
                 conn.sendall('OK\r\n')
 
-                with contextlib.closing(conn.makefile()) as f:
-                    self.assertEqual('kill\n', f.readline())
-                    self.assertEqual('quit\n', f.readline())
+                with contextlib.closing(conn.makefile()) as connf:
+                    line = connf.readline()
+                    if line.startswith('auth'):
+                        # Ignore the first auth line.
+                        line = connf.readline()
+                    self.assertEqual('kill\n', line)
+                    self.assertEqual('quit\n', connf.readline())
 
                 conn.sendall('OK: killing emulator, bye bye\r\n')
 
@@ -264,11 +329,48 @@
                 self._reset_socket_on_close(conn)
 
             # Wait for adb to finish, so we can check return code.
-            p.communicate()
+            proc.communicate()
 
             # If this fails, adb probably isn't ignoring WSAECONNRESET when
             # reading the response from the adb emu kill command (on Windows).
-            self.assertEqual(0, p.returncode)
+            self.assertEqual(0, proc.returncode)
+
+    def test_emulator_connect(self):
+        """Ensure that the emulator can connect.
+
+        Bug: http://b/78991667
+        """
+        with adb_server() as server_port:
+            with fake_adbd() as port:
+                serial = 'emulator-{}'.format(port - 1)
+                # Ensure that the emulator is not there.
+                try:
+                    subprocess.check_output(['adb', '-P', str(server_port),
+                                             '-s', serial, 'get-state'],
+                                            stderr=subprocess.STDOUT)
+                    self.fail('Device should not be available')
+                except subprocess.CalledProcessError as err:
+                    self.assertEqual(
+                        err.output.strip(),
+                        'error: device \'{}\' not found'.format(serial))
+
+                # Let the ADB server know that the emulator has started.
+                with contextlib.closing(
+                    socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
+                    sock.connect(('localhost', server_port))
+                    command = 'host:emulator:{}'.format(port)
+                    sock.sendall('%04x%s' % (len(command), command))
+
+                # Ensure the emulator is there.
+                subprocess.check_call(['adb', '-P', str(server_port),
+                                       '-s', serial, 'wait-for-device'])
+                output = subprocess.check_output(['adb', '-P', str(server_port),
+                                                  '-s', serial, 'get-state'])
+                self.assertEqual(output.strip(), 'device')
+
+
+class ConnectionTest(unittest.TestCase):
+    """Tests for adb connect."""
 
     def test_connect_ipv4_ipv6(self):
         """Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6.
@@ -277,38 +379,67 @@
         """
         for protocol in (socket.AF_INET, socket.AF_INET6):
             try:
-                with fake_adb_server(protocol=protocol) as port:
-                    output = subprocess.check_output(
-                        ['adb', 'connect', 'localhost:{}'.format(port)])
-
-                    self.assertEqual(
-                        output.strip(), 'connected to localhost:{}'.format(port))
+                with fake_adbd(protocol=protocol) as port:
+                    serial = 'localhost:{}'.format(port)
+                    with adb_connect(self, serial):
+                        pass
             except socket.error:
                 print("IPv6 not available, skipping")
                 continue
 
     def test_already_connected(self):
-        with fake_adb_server() as port:
-            output = subprocess.check_output(
-                ['adb', 'connect', 'localhost:{}'.format(port)])
+        """Ensure that an already-connected device stays connected."""
 
-            self.assertEqual(
-                output.strip(), 'connected to localhost:{}'.format(port))
+        with fake_adbd() as port:
+            serial = 'localhost:{}'.format(port)
+            with adb_connect(self, serial):
+                # b/31250450: this always returns 0 but probably shouldn't.
+                output = subprocess.check_output(['adb', 'connect', serial])
+                self.assertEqual(
+                    output.strip(), 'already connected to {}'.format(serial))
 
-            # b/31250450: this always returns 0 but probably shouldn't.
-            output = subprocess.check_output(
-                ['adb', 'connect', 'localhost:{}'.format(port)])
+    def test_reconnect(self):
+        """Ensure that a disconnected device reconnects."""
 
-            self.assertEqual(
-                output.strip(), 'already connected to localhost:{}'.format(port))
+        with fake_adbd() as port:
+            serial = 'localhost:{}'.format(port)
+            with adb_connect(self, serial):
+                output = subprocess.check_output(['adb', '-s', serial,
+                                                  'get-state'])
+                self.assertEqual(output.strip(), 'device')
+
+                # This will fail.
+                proc = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'],
+                                        stdout=subprocess.PIPE,
+                                        stderr=subprocess.STDOUT)
+                output, _ = proc.communicate()
+                self.assertEqual(output.strip(), 'error: closed')
+
+                subprocess.check_call(['adb', '-s', serial, 'wait-for-device'])
+
+                output = subprocess.check_output(['adb', '-s', serial,
+                                                  'get-state'])
+                self.assertEqual(output.strip(), 'device')
+
+                # Once we explicitly kick a device, it won't attempt to
+                # reconnect.
+                output = subprocess.check_output(['adb', 'disconnect', serial])
+                self.assertEqual(
+                    output.strip(), 'disconnected {}'.format(serial))
+                try:
+                    subprocess.check_output(['adb', '-s', serial, 'get-state'],
+                                            stderr=subprocess.STDOUT)
+                    self.fail('Device should not be available')
+                except subprocess.CalledProcessError as err:
+                    self.assertEqual(
+                        err.output.strip(),
+                        'error: device \'{}\' not found'.format(serial))
+
 
 def main():
+    """Main entrypoint."""
     random.seed(0)
-    if len(adb.get_devices()) > 0:
-        suite = unittest.TestLoader().loadTestsFromName(__name__)
-        unittest.TextTestRunner(verbosity=3).run(suite)
-    else:
-        print('Test suite must be run with attached devices')
+    unittest.main(verbosity=3)
 
 
 if __name__ == '__main__':
diff --git a/adb/transport.cpp b/adb/transport.cpp
index be7f8fe..beec13a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -33,6 +33,7 @@
 #include <deque>
 #include <list>
 #include <mutex>
+#include <queue>
 #include <thread>
 
 #include <android-base/logging.h>
@@ -50,7 +51,9 @@
 #include "adb_utils.h"
 #include "fdevent.h"
 
-static void transport_unref(atransport *t);
+static void register_transport(atransport* transport);
+static void remove_transport(atransport* transport);
+static void transport_unref(atransport* transport);
 
 // TODO: unordered_map<TransportId, atransport*>
 static auto& transport_list = *new std::list<atransport*>();
@@ -77,6 +80,130 @@
     ~ScopedAssumeLocked() RELEASE() {}
 };
 
+// Tracks and handles atransport*s that are attempting reconnection.
+class ReconnectHandler {
+  public:
+    ReconnectHandler() = default;
+    ~ReconnectHandler() = default;
+
+    // Starts the ReconnectHandler thread.
+    void Start();
+
+    // Requests the ReconnectHandler thread to stop.
+    void Stop();
+
+    // Adds the atransport* to the queue of reconnect attempts.
+    void TrackTransport(atransport* transport);
+
+  private:
+    // The main thread loop.
+    void Run();
+
+    // Tracks a reconnection attempt.
+    struct ReconnectAttempt {
+        atransport* transport;
+        std::chrono::system_clock::time_point deadline;
+        size_t attempts_left;
+    };
+
+    // Only retry for up to one minute.
+    static constexpr const std::chrono::seconds kDefaultTimeout = std::chrono::seconds(10);
+    static constexpr const size_t kMaxAttempts = 6;
+
+    // Protects all members.
+    std::mutex reconnect_mutex_;
+    bool running_ GUARDED_BY(reconnect_mutex_) = true;
+    std::thread handler_thread_;
+    std::condition_variable reconnect_cv_;
+    std::queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
+
+    DISALLOW_COPY_AND_ASSIGN(ReconnectHandler);
+};
+
+void ReconnectHandler::Start() {
+    check_main_thread();
+    handler_thread_ = std::thread(&ReconnectHandler::Run, this);
+}
+
+void ReconnectHandler::Stop() {
+    check_main_thread();
+    {
+        std::lock_guard<std::mutex> lock(reconnect_mutex_);
+        running_ = false;
+    }
+    reconnect_cv_.notify_one();
+    handler_thread_.join();
+
+    // Drain the queue to free all resources.
+    std::lock_guard<std::mutex> lock(reconnect_mutex_);
+    while (!reconnect_queue_.empty()) {
+        ReconnectAttempt attempt = reconnect_queue_.front();
+        reconnect_queue_.pop();
+        remove_transport(attempt.transport);
+    }
+}
+
+void ReconnectHandler::TrackTransport(atransport* transport) {
+    check_main_thread();
+    {
+        std::lock_guard<std::mutex> lock(reconnect_mutex_);
+        if (!running_) return;
+        reconnect_queue_.emplace(ReconnectAttempt{
+            transport, std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
+            ReconnectHandler::kMaxAttempts});
+    }
+    reconnect_cv_.notify_one();
+}
+
+void ReconnectHandler::Run() {
+    while (true) {
+        ReconnectAttempt attempt;
+        {
+            std::unique_lock<std::mutex> lock(reconnect_mutex_);
+            ScopedAssumeLocked assume_lock(reconnect_mutex_);
+
+            auto deadline = std::chrono::time_point<std::chrono::system_clock>::max();
+            if (!reconnect_queue_.empty()) deadline = reconnect_queue_.front().deadline;
+            reconnect_cv_.wait_until(lock, deadline, [&]() REQUIRES(reconnect_mutex_) {
+                return !running_ ||
+                       (!reconnect_queue_.empty() && reconnect_queue_.front().deadline < deadline);
+            });
+
+            if (!running_) return;
+            attempt = reconnect_queue_.front();
+            reconnect_queue_.pop();
+            if (attempt.transport->kicked()) {
+                D("transport %s was kicked. giving up on it.", attempt.transport->serial);
+                remove_transport(attempt.transport);
+                continue;
+            }
+        }
+        D("attempting to reconnect %s", attempt.transport->serial);
+
+        if (!attempt.transport->Reconnect()) {
+            D("attempting to reconnect %s failed.", attempt.transport->serial);
+            if (attempt.attempts_left == 0) {
+                D("transport %s exceeded the number of retry attempts. giving up on it.",
+                  attempt.transport->serial);
+                remove_transport(attempt.transport);
+                continue;
+            }
+
+            std::lock_guard<std::mutex> lock(reconnect_mutex_);
+            reconnect_queue_.emplace(ReconnectAttempt{
+                attempt.transport,
+                std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
+                attempt.attempts_left - 1});
+            continue;
+        }
+
+        D("reconnection to %s succeeded.", attempt.transport->serial);
+        register_transport(attempt.transport);
+    }
+}
+
+static auto& reconnect_handler = *new ReconnectHandler();
+
 }  // namespace
 
 TransportId NextTransportId() {
@@ -477,8 +604,6 @@
     return 0;
 }
 
-static void remove_transport(atransport*);
-
 static void transport_registration_func(int _fd, unsigned ev, void*) {
     tmsg m;
     atransport* t;
@@ -515,8 +640,9 @@
 
     /* don't create transport threads for inaccessible devices */
     if (t->GetConnectionState() != kCsNoPerm) {
-        /* initial references are the two threads */
-        t->ref_count = 1;
+        // The connection gets a reference to the atransport. It will release it
+        // upon a read/write error.
+        t->ref_count++;
         t->connection()->SetTransportName(t->serial_name());
         t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
             if (!check_header(p.get(), t)) {
@@ -547,13 +673,20 @@
 
     {
         std::lock_guard<std::recursive_mutex> lock(transport_lock);
-        pending_list.remove(t);
-        transport_list.push_front(t);
+        auto it = std::find(pending_list.begin(), pending_list.end(), t);
+        if (it != pending_list.end()) {
+            pending_list.remove(t);
+            transport_list.push_front(t);
+        }
     }
 
     update_transports();
 }
 
+void init_reconnect_handler(void) {
+    reconnect_handler.Start();
+}
+
 void init_transport_registration(void) {
     int s[2];
 
@@ -572,6 +705,7 @@
 }
 
 void kick_all_transports() {
+    reconnect_handler.Stop();
     // To avoid only writing part of a packet to a transport after exit, kick all transports.
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     for (auto t : transport_list) {
@@ -601,15 +735,21 @@
 }
 
 static void transport_unref(atransport* t) {
+    check_main_thread();
     CHECK(t != nullptr);
 
     std::lock_guard<std::recursive_mutex> lock(transport_lock);
     CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
-        D("transport: %s unref (kicking and closing)", t->serial);
         t->connection()->Stop();
-        remove_transport(t);
+        if (t->IsTcpDevice() && !t->kicked()) {
+            D("transport: %s unref (attempting reconnection) %d", t->serial, t->kicked());
+            reconnect_handler.TrackTransport(t);
+        } else {
+            D("transport: %s unref (kicking and closing)", t->serial);
+            remove_transport(t);
+        }
     } else {
         D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
     }
@@ -781,9 +921,8 @@
 }
 
 void atransport::Kick() {
-    if (!kicked_) {
-        D("kicking transport %s", this->serial);
-        kicked_ = true;
+    if (!kicked_.exchange(true)) {
+        D("kicking transport %p %s", this, this->serial);
         this->connection()->Stop();
     }
 }
@@ -941,6 +1080,10 @@
     connection_waitable_->SetConnectionEstablished(success);
 }
 
+bool atransport::Reconnect() {
+    return reconnect_(this);
+}
+
 #if ADB_HOST
 
 // We use newline as our delimiter, make sure to never output it.
@@ -1021,8 +1164,9 @@
 }
 #endif  // ADB_HOST
 
-int register_socket_transport(int s, const char* serial, int port, int local) {
-    atransport* t = new atransport();
+int register_socket_transport(int s, const char* serial, int port, int local,
+                              atransport::ReconnectCallback reconnect) {
+    atransport* t = new atransport(std::move(reconnect), kCsOffline);
 
     if (!serial) {
         char buf[32];
@@ -1103,7 +1247,7 @@
 
 void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
                             unsigned writeable) {
-    atransport* t = new atransport((writeable ? kCsConnecting : kCsNoPerm));
+    atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm);
 
     D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb);
diff --git a/adb/transport.h b/adb/transport.h
index e1cbc09..ae9cc02 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,20 +198,27 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    atransport(ConnectionState state = kCsConnecting)
+    using ReconnectCallback = std::function<bool(atransport*)>;
+
+    atransport(ReconnectCallback reconnect, ConnectionState state)
         : id(NextTransportId()),
+          kicked_(false),
           connection_state_(state),
           connection_waitable_(std::make_shared<ConnectionWaitable>()),
-          connection_(nullptr) {
+          connection_(nullptr),
+          reconnect_(std::move(reconnect)) {
         // Initialize protocol to min version for compatibility with older versions.
         // Version will be updated post-connect.
         protocol_version = A_VERSION_MIN;
         max_payload = MAX_PAYLOAD;
     }
+    atransport(ConnectionState state = kCsOffline)
+        : atransport([](atransport*) { return false; }, state) {}
     virtual ~atransport();
 
     int Write(apacket* p);
     void Kick();
+    bool kicked() const { return kicked_; }
 
     // ConnectionState can be read by all threads, but can only be written in the main thread.
     ConnectionState GetConnectionState() const;
@@ -286,8 +293,12 @@
     // Gets a shared reference to the ConnectionWaitable.
     std::shared_ptr<ConnectionWaitable> connection_waitable() { return connection_waitable_; }
 
+    // Attempts to reconnect with the underlying Connection. Returns true if the
+    // reconnection attempt succeeded.
+    bool Reconnect();
+
   private:
-    bool kicked_ = false;
+    std::atomic<bool> kicked_;
 
     // A set of features transmitted in the banner with the initial connection.
     // This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -310,6 +321,9 @@
     // The underlying connection object.
     std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
 
+    // A callback that will be invoked when the atransport needs to reconnect.
+    ReconnectCallback reconnect_;
+
     std::mutex mutex_;
 
     DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -333,6 +347,7 @@
 // Stops iteration and returns false if fn returns false, otherwise returns true.
 bool iterate_transports(std::function<bool(const atransport*)> fn);
 
+void init_reconnect_handler(void);
 void init_transport_registration(void);
 void init_mdns_transport_discovery(void);
 std::string list_transports(bool long_listing);
@@ -347,7 +362,8 @@
 void connect_device(const std::string& address, std::string* response);
 
 /* cause new transports to be init'd and added to the list */
-int register_socket_transport(int s, const char* serial, int port, int local);
+int register_socket_transport(int s, const char* serial, int port, int local,
+                              atransport::ReconnectCallback reconnect);
 
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index e81f27c..181d666 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -68,28 +68,24 @@
     return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
 }
 
-void connect_device(const std::string& address, std::string* response) {
-    if (address.empty()) {
-        *response = "empty address";
-        return;
-    }
-
+std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
+                                                    std::string* response) {
     std::string serial;
     std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
     if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
-        return;
+        return std::make_tuple(unique_fd(), port, serial);
     }
 
     std::string error;
-    int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
+    unique_fd fd(network_connect(host.c_str(), port, SOCK_STREAM, 10, &error));
     if (fd == -1) {
         *response = android::base::StringPrintf("unable to connect to %s: %s",
                                                 serial.c_str(), error.c_str());
-        return;
+        return std::make_tuple(std::move(fd), port, serial);
     }
 
-    D("client: connected %s remote on fd %d", serial.c_str(), fd);
+    D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
     close_on_exec(fd);
     disable_tcp_nagle(fd);
 
@@ -98,7 +94,38 @@
         D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
     }
 
-    int ret = register_socket_transport(fd, serial.c_str(), port, 0);
+    return std::make_tuple(std::move(fd), port, serial);
+}
+
+void connect_device(const std::string& address, std::string* response) {
+    if (address.empty()) {
+        *response = "empty address";
+        return;
+    }
+
+    unique_fd fd;
+    int port;
+    std::string serial;
+    std::tie(fd, port, serial) = tcp_connect(address, response);
+    auto reconnect = [address](atransport* t) {
+        std::string response;
+        unique_fd fd;
+        int port;
+        std::string serial;
+        std::tie(fd, port, serial) = tcp_connect(address, &response);
+        if (fd == -1) {
+            D("reconnect failed: %s", response.c_str());
+            return false;
+        }
+
+        // This invokes the part of register_socket_transport() that needs to be
+        // invoked if the atransport* has already been setup. This eventually
+        // calls atransport->SetConnection() with a newly created Connection*
+        // that will in turn send the CNXN packet.
+        return init_socket_transport(t, fd.release(), port, 0) >= 0;
+    };
+
+    int ret = register_socket_transport(fd.release(), serial.c_str(), port, 0, std::move(reconnect));
     if (ret < 0) {
         adb_close(fd);
         if (ret == -EALREADY) {
@@ -135,7 +162,8 @@
         close_on_exec(fd);
         disable_tcp_nagle(fd);
         std::string serial = getEmulatorSerialString(console_port);
-        if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+        if (register_socket_transport(fd, serial.c_str(), adb_port, 1,
+                                      [](atransport*) { return false; }) == 0) {
             return 0;
         }
         adb_close(fd);
@@ -239,7 +267,8 @@
             close_on_exec(fd);
             disable_tcp_nagle(fd);
             std::string serial = android::base::StringPrintf("host-%d", fd);
-            if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
+            if (register_socket_transport(fd, serial.c_str(), port, 1,
+                                          [](atransport*) { return false; }) != 0) {
                 adb_close(fd);
             }
         }
@@ -338,7 +367,8 @@
                 /* Host is connected. Register the transport, and start the
                  * exchange. */
                 std::string serial = android::base::StringPrintf("host-%d", fd);
-                if (register_socket_transport(fd, serial.c_str(), port, 1) != 0 ||
+                if (register_socket_transport(fd, serial.c_str(), port, 1,
+                                              [](atransport*) { return false; }) != 0 ||
                     !WriteFdExactly(fd, _start_req, strlen(_start_req))) {
                     adb_close(fd);
                 }
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index dea2e17..079a574 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -304,7 +304,16 @@
 
   crash_mutex.lock();
   if (lock_count++ > 0) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, aborting");
+    signal(SIGABRT, SIG_DFL);
+    raise(SIGABRT);
+    sigset_t sigset;
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGABRT);
+    sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
+
+    // Just in case...
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "abort didn't exit, exiting");
     _exit(1);
   }
 
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 472ab59..af4d6c1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -686,6 +686,49 @@
     return a;
 }
 
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ *   /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+    std::set<std::string> boot_devices;
+
+    for (int i = 0; i < fstab.num_entries; i++) {
+        std::string blk_device(fstab.recs[i].blk_device);
+        // Skips blk_device that doesn't conform to the format.
+        if (!android::base::StartsWith(blk_device, "/dev/block") ||
+            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+            continue;
+        }
+        // Skips non-by_name blk_device.
+        // /dev/block/<type>/<device>/by-name/<partition>
+        //                           ^ slash_by_name
+        auto slash_by_name = blk_device.find("/by-name");
+        if (slash_by_name == std::string::npos) continue;
+        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
+
+        // Erases /dev/block/, now we have <type>/<device>
+        blk_device.erase(0, std::string("/dev/block/").size());
+
+        // <type>/<device>
+        //       ^ first_slash
+        auto first_slash = blk_device.find('/');
+        if (first_slash == std::string::npos) continue;
+
+        auto boot_device = blk_device.substr(first_slash + 1);
+        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+    }
+
+    return boot_devices;
+}
+
 struct fstab *fs_mgr_read_fstab(const char *fstab_path)
 {
     FILE *fstab_file;
@@ -863,6 +906,23 @@
     return nullptr;
 }
 
+std::set<std::string> fs_mgr_get_boot_devices() {
+    // boot_devices can be specified in device tree.
+    std::string dt_value;
+    std::string file_name = get_android_dt_dir() + "/boot_devices";
+    if (read_dt_file(file_name, &dt_value)) {
+        auto boot_devices = android::base::Split(dt_value, ",");
+        return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+    }
+
+    // Fallback to extract boot devices from fstab.
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    if (fstab) return extract_boot_devices(*fstab);
+
+    return {};
+}
+
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
 {
     return fstab->fs_mgr_flags & MF_VOLDMANAGED;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 04ccfc5..d232cca 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,6 +22,7 @@
 #include <stdint.h>
 #include <stdio.h>
 
+#include <set>
 #include <string>
 
 /*
@@ -89,5 +90,6 @@
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
+std::set<std::string> fs_mgr_get_boot_devices();
 
 #endif /* __CORE_FS_TAB_H */
diff --git a/init/README.md b/init/README.md
index c08b07a..550ef05 100644
--- a/init/README.md
+++ b/init/README.md
@@ -195,6 +195,10 @@
 > This service will not automatically start with its class.
   It must be explicitly started by name or by interface name.
 
+`enter_namespace <type> <path>`
+> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
+  _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
+
 `file <path> <type>`
 > Open a file path and pass its fd to the launched process. _type_ must be
   "r", "w" or "rw".  For native executables see libcutils
diff --git a/init/devices.cpp b/init/devices.cpp
index 8d27f4f..ada1e28 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -329,10 +329,10 @@
                          << partition_name_sanitized << "'";
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
-    }
-
-    if (uevent.partition_num >= 0) {
-        links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
+        // Adds symlink: /dev/block/by-name/<partition_name>.
+        if (boot_devices_.find(device) != boot_devices_.end()) {
+            links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
+        }
     }
 
     auto last_slash = uevent.path.rfind('/');
@@ -350,8 +350,14 @@
                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
             }
 
-            if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
-                PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+            if (symlink(devpath.c_str(), link.c_str())) {
+                if (errno != EEXIST) {
+                    PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+                } else if (std::string link_path;
+                           Readlink(link, &link_path) && link_path != devpath) {
+                    PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
+                                << ", which already links to: " << link_path;
+                }
             }
         }
     }
@@ -415,16 +421,18 @@
 
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
-                             std::vector<Subsystem> subsystems, bool skip_restorecon)
+                             std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+                             bool skip_restorecon)
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
+      boot_devices_(std::move(boot_devices)),
       skip_restorecon_(skip_restorecon),
       sysfs_mount_point_("/sys") {}
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                    std::vector<Subsystem>{}, false) {}
+                    std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
 
 }  // namespace init
 }  // namespace android
diff --git a/init/devices.h b/init/devices.h
index 1f8f1e8..f9035da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <algorithm>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -103,8 +104,8 @@
 
     DeviceHandler();
     DeviceHandler(std::vector<Permissions> dev_permissions,
-                  std::vector<SysfsPermissions> sysfs_permissions,
-                  std::vector<Subsystem> subsystems, bool skip_restorecon);
+                  std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
+                  std::set<std::string> boot_devices, bool skip_restorecon);
     ~DeviceHandler(){};
 
     void HandleDeviceEvent(const Uevent& uevent);
@@ -125,6 +126,7 @@
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
+    std::set<std::string> boot_devices_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
 };
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index eba00cb..d658f4d 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -84,7 +84,6 @@
     };
     std::vector<std::string> expected_result{
         "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
-        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
@@ -100,7 +99,6 @@
         .partition_num = 1,
     };
     std::vector<std::string> expected_result{
-        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
         "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
     };
 
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index a5ba647..34de640 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -77,7 +77,7 @@
     std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
     std::vector<fstab_rec*> mount_fstab_recs_;
     std::set<std::string> required_devices_partition_names_;
-    DeviceHandler device_handler_;
+    std::unique_ptr<DeviceHandler> device_handler_;
     UeventListener uevent_listener_;
 };
 
@@ -147,6 +147,11 @@
     if (IsDmLinearEnabled()) {
         dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
     }
+
+    auto boot_devices = fs_mgr_get_boot_devices();
+    device_handler_ =
+        std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+                                        std::vector<Subsystem>{}, std::move(boot_devices), false);
 }
 
 std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -205,7 +210,7 @@
         bool found = false;
         auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
             if (uevent.path == dm_path) {
-                device_handler_.HandleDeviceEvent(uevent);
+                device_handler_->HandleDeviceEvent(uevent);
                 found = true;
                 return ListenerAction::kStop;
             }
@@ -262,7 +267,7 @@
     if (iter != required_devices_partition_names_.end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
         required_devices_partition_names_.erase(iter);
-        device_handler_.HandleDeviceEvent(uevent);
+        device_handler_->HandleDeviceEvent(uevent);
         if (required_devices_partition_names_.empty()) {
             return ListenerAction::kStop;
         } else {
@@ -299,7 +304,7 @@
     auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
         if (uevent.device_name == device_name) {
             LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
-            device_handler_.HandleDeviceEvent(uevent);
+            device_handler_->HandleDeviceEvent(uevent);
             found = true;
             return ListenerAction::kStop;
         }
@@ -468,9 +473,8 @@
         // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
         // is not empty. e.g.,
         //   - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
-        //   - /dev/block/platform/soc.0/f9824900.sdhci/by-num/p1
         //   - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
-        std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+        std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
         if (!links.empty()) {
             auto[it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
             if (!inserted) {
diff --git a/init/service.cpp b/init/service.cpp
index 03c2cee..0e08d9b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <hidl-util/FQName.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
@@ -59,13 +60,13 @@
 using android::base::ParseInt;
 using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
 namespace android {
 namespace init {
 
-static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
-                                                        const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
     std::string computed_context;
 
     char* raw_con = nullptr;
@@ -101,36 +102,49 @@
     return computed_context;
 }
 
-static void SetUpPidNamespace(const std::string& service_name) {
+Result<Success> Service::SetUpMountNamespace() const {
     constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
 
-    // It's OK to LOG(FATAL) in this function since it's running in the first
-    // child process.
-
     // Recursively remount / as slave like zygote does so unmounting and mounting /proc
     // doesn't interfere with the parent namespace's /proc mount. This will also
     // prevent any other mounts/unmounts initiated by the service from interfering
     // with the parent namespace but will still allow mount events from the parent
     // namespace to propagate to the child.
     if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
-        PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
-    }
-    // umount() then mount() /proc.
-    // Note that it is not sufficient to mount with MS_REMOUNT.
-    if (umount("/proc") == -1) {
-        PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
-    }
-    if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
-        PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
+        return ErrnoError() << "Could not remount(/) recursively as slave";
     }
 
-    if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
-        PLOG(FATAL) << "couldn't set name for " << service_name;
+    // umount() then mount() /proc and/or /sys
+    // Note that it is not sufficient to mount with MS_REMOUNT.
+    if (namespace_flags_ & CLONE_NEWPID) {
+        if (umount("/proc") == -1) {
+            return ErrnoError() << "Could not umount(/proc)";
+        }
+        if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+            return ErrnoError() << "Could not mount(/proc)";
+        }
+    }
+    bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
+                                   [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+    if (remount_sys) {
+        if (umount2("/sys", MNT_DETACH) == -1) {
+            return ErrnoError() << "Could not umount(/sys)";
+        }
+        if (mount("", "/sys", "sys", kSafeFlags, "") == -1) {
+            return ErrnoError() << "Could not mount(/sys)";
+        }
+    }
+    return Success();
+}
+
+Result<Success> Service::SetUpPidNamespace() const {
+    if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
+        return ErrnoError() << "Could not set name";
     }
 
     pid_t child_pid = fork();
     if (child_pid == -1) {
-        PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+        return ErrnoError() << "Could not fork init inside the PID namespace";
     }
 
     if (child_pid > 0) {
@@ -153,6 +167,20 @@
         }
         _exit(WEXITSTATUS(init_exitstatus));
     }
+    return Success();
+}
+
+Result<Success> Service::EnterNamespaces() const {
+    for (const auto& [nstype, path] : namespaces_to_enter_) {
+        auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
+        if (!fd) {
+            return ErrnoError() << "Could not open namespace at " << path;
+        }
+        if (setns(fd, nstype) == -1) {
+            return ErrnoError() << "Could not setns() namespace at " << path;
+        }
+    }
+    return Success();
 }
 
 static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
@@ -422,6 +450,20 @@
     return Success();
 }
 
+Result<Success> Service::ParseEnterNamespace(const std::vector<std::string>& args) {
+    if (args[1] != "net") {
+        return Error() << "Init only supports entering network namespaces";
+    }
+    if (!namespaces_to_enter_.empty()) {
+        return Error() << "Only one network namespace may be entered";
+    }
+    // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
+    // present. Therefore, they also require mount namespaces.
+    namespace_flags_ |= CLONE_NEWNS;
+    namespaces_to_enter_.emplace_back(CLONE_NEWNET, args[2]);
+    return Success();
+}
+
 Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
     auto gid = DecodeUid(args[1]);
     if (!gid) {
@@ -691,6 +733,8 @@
         {"console",     {0,     1,    &Service::ParseConsole}},
         {"critical",    {0,     0,    &Service::ParseCritical}},
         {"disabled",    {0,     0,    &Service::ParseDisabled}},
+        {"enter_namespace",
+                        {2,     2,    &Service::ParseEnterNamespace}},
         {"file",        {2,     2,    &Service::ParseFile}},
         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
         {"interface",   {2,     2,    &Service::ParseInterface}},
@@ -793,7 +837,7 @@
     if (!seclabel_.empty()) {
         scon = seclabel_;
     } else {
-        auto result = ComputeContextFromExecutable(name_, args_[0]);
+        auto result = ComputeContextFromExecutable(args_[0]);
         if (!result) {
             return result.error();
         }
@@ -812,10 +856,24 @@
     if (pid == 0) {
         umask(077);
 
+        if (auto result = EnterNamespaces(); !result) {
+            LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
+        }
+
+        if (namespace_flags_ & CLONE_NEWNS) {
+            if (auto result = SetUpMountNamespace(); !result) {
+                LOG(FATAL) << "Service '" << name_
+                           << "' could not set up mount namespace: " << result.error();
+            }
+        }
+
         if (namespace_flags_ & CLONE_NEWPID) {
             // This will fork again to run an init process inside the PID
             // namespace.
-            SetUpPidNamespace(name_);
+            if (auto result = SetUpPidNamespace(); !result) {
+                LOG(FATAL) << "Service '" << name_
+                           << "' could not set up PID namespace: " << result.error();
+            }
         }
 
         for (const auto& [key, value] : environment_vars_) {
diff --git a/init/service.h b/init/service.h
index 9cb35b8..cbfd52f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -125,6 +125,9 @@
     using OptionParser = Result<Success> (Service::*)(const std::vector<std::string>& args);
     class OptionParserMap;
 
+    Result<Success> SetUpMountNamespace() const;
+    Result<Success> SetUpPidNamespace() const;
+    Result<Success> EnterNamespaces() const;
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
@@ -137,6 +140,7 @@
     Result<Success> ParseConsole(const std::vector<std::string>& args);
     Result<Success> ParseCritical(const std::vector<std::string>& args);
     Result<Success> ParseDisabled(const std::vector<std::string>& args);
+    Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
     Result<Success> ParseGroup(const std::vector<std::string>& args);
     Result<Success> ParsePriority(const std::vector<std::string>& args);
     Result<Success> ParseInterface(const std::vector<std::string>& args);
@@ -181,6 +185,8 @@
     std::vector<gid_t> supp_gids_;
     CapSet capabilities_;
     unsigned namespace_flags_;
+    // Pair of namespace type, path to namespace.
+    std::vector<std::pair<int, std::string>> namespaces_to_enter_;
 
     std::string seclabel_;
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 1435d82..a284203 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -30,6 +30,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <fstab/fstab.h>
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
@@ -242,8 +243,9 @@
     std::string hardware = android::base::GetProperty("ro.hardware", "");
     parser.ParseConfig("/ueventd." + hardware + ".rc");
 
+    auto boot_devices = fs_mgr_get_boot_devices();
     return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
-                         std::move(subsystems), true);
+                         std::move(subsystems), std::move(boot_devices), true);
 }
 
 int ueventd_main(int argc, char** argv) {
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index ae8fa94..6d9141a 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -12,6 +12,27 @@
 Usage
 -------
 
+### In Android apps ###
+
+libmemunreachble is loaded by zygote and can be triggered with `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To enable malloc\_debug backtraces on allocations for a single app process on a userdebug device, use:
+```
+adb root
+adb shell setprop libc.debug.malloc.program app_process
+adb shell setprop wrap.[process] "\$\@"
+adb shell setprop libc.debug.malloc.options backtrace=4
+```
+
+Kill and restart the app, trigger the leak, and then run `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To disable malloc\_debug:
+```
+adb shell setprop libc.debug.malloc.options "''"
+adb shell setprop libc.debug.malloc.program "''"
+adb shell setprop wrap.[process]  "''"
+```
+
 ### C interface ###
 
 #### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5d996e9..82f2e73 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -202,6 +202,7 @@
         "tests/files/offline/jit_debug_x86/*",
         "tests/files/offline/jit_map_arm/*",
         "tests/files/offline/gnu_debugdata_arm/*",
+        "tests/files/offline/offset_arm/*",
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 02f8a9a..3762107 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -160,14 +160,14 @@
 }
 
 // The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
-               Memory* process_memory, bool* finished) {
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+               bool* finished) {
   if (!valid_) {
     return false;
   }
 
   // The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
-  if (regs->StepIfSignalHandler(rel_pc + elf_offset, this, process_memory)) {
+  if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
     *finished = false;
     return true;
   }
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 9a6c6df..099cc9e 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -220,8 +220,7 @@
           in_device_map = true;
         } else {
           bool finished;
-          stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
-                              &finished);
+          stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
           elf->GetLastError(&last_error_);
           if (stepped && finished) {
             break;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 385973e..f4cdbda 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,8 +65,8 @@
 
   uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
 
-  bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
-            Memory* process_memory, bool* finished);
+  bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+            bool* finished);
 
   ElfInterface* CreateInterfaceFromMemory(Memory* memory);
 
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f9028c4..aecbf6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -133,7 +133,7 @@
   ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
 
   bool finished;
-  ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
+  ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
 }
 
 TEST_F(ElfTest, elf32_invalid_machine) {
@@ -330,7 +330,7 @@
   elf.FakeSetValid(true);
   elf.FakeSetLoadBias(0);
   bool finished;
-  ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x3000, 0x1000, &regs, &process_memory, &finished));
   EXPECT_FALSE(finished);
   EXPECT_EQ(15U, regs.pc());
   EXPECT_EQ(13U, regs.sp());
@@ -370,7 +370,7 @@
   EXPECT_CALL(*interface, Step(0x1000, 0, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
-  ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x1004, 0x1000, &regs, &process_memory, &finished));
 }
 
 TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@@ -388,7 +388,7 @@
   EXPECT_CALL(*interface, Step(0x7300, 0x4000, &regs, &process_memory, &finished))
       .WillOnce(::testing::Return(true));
 
-  ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, &regs, &process_memory, &finished));
+  ASSERT_TRUE(elf.Step(0x7304, 0x7300, &regs, &process_memory, &finished));
 }
 
 TEST_F(ElfTest, get_global_invalid_elf) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 2b8f0c2..285fc9e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -46,6 +46,12 @@
 
 namespace unwindstack {
 
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+  MemoryOffline* memory = new MemoryOffline;
+  ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+  parts->Add(memory);
+}
+
 class UnwindOfflineTest : public ::testing::Test {
  protected:
   void TearDown() override {
@@ -64,9 +70,24 @@
     maps_.reset(new BufferMaps(data.c_str()));
     ASSERT_TRUE(maps_->Parse());
 
-    std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
-    ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
-    process_memory_.reset(stack_memory.release());
+    std::string stack_name(dir_ + "stack.data");
+    struct stat st;
+    if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+      std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+      ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+      process_memory_.reset(stack_memory.release());
+    } else {
+      std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+      for (size_t i = 0;; i++) {
+        stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+        if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+          ASSERT_TRUE(i != 0) << "No stack data files found.";
+          break;
+        }
+        AddMemory(stack_name, stack_memory.get());
+      }
+      process_memory_.reset(stack_memory.release());
+    }
 
     switch (arch) {
       case ARCH_ARM: {
@@ -180,7 +201,7 @@
 }
 
 TEST_F(UnwindOfflineTest, pc_straddle_arm) {
-  Init("straddle_arm/", ARCH_ARM);
+  ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
 
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
@@ -204,7 +225,7 @@
 }
 
 TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
-  Init("gnu_debugdata_arm/", ARCH_ARM);
+  ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
 
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
@@ -224,7 +245,7 @@
 }
 
 TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
-  Init("straddle_arm64/", ARCH_ARM64);
+  ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
 
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
@@ -255,14 +276,8 @@
   EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
 }
 
-static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
-  MemoryOffline* memory = new MemoryOffline;
-  ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
-  parts->Add(memory);
-}
-
 TEST_F(UnwindOfflineTest, jit_debug_x86) {
-  Init("jit_debug_x86/", ARCH_X86);
+  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
   AddMemory(dir_ + "descriptor.data", memory);
@@ -555,7 +570,7 @@
 }
 
 TEST_F(UnwindOfflineTest, jit_debug_arm) {
-  Init("jit_debug_arm/", ARCH_ARM);
+  ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
   AddMemory(dir_ + "descriptor.data", memory);
@@ -873,7 +888,7 @@
 // fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
 // No .gnu_debugdata section in the elf file, so no symbols.
 TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
-  Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
+  ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
 
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
@@ -902,7 +917,7 @@
 // The elf has bad eh_frame unwind information for the pcs. If eh_frame
 // is used first, the unwind will not match the expected output.
 TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
-  Init("debug_frame_first_x86/", ARCH_X86);
+  ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
 
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
@@ -930,7 +945,7 @@
 
 // Make sure that a pc that is at the beginning of an fde unwinds correctly.
 TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
-  Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
+  ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
 
   Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
   unwinder.Unwind();
@@ -957,7 +972,7 @@
 }
 
 TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
-  Init("art_quick_osr_stub_arm/", ARCH_ARM);
+  ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
 
   MemoryOfflineParts* memory = new MemoryOfflineParts;
   AddMemory(dir_ + "descriptor.data", memory);
@@ -1072,7 +1087,7 @@
 }
 
 TEST_F(UnwindOfflineTest, jit_map_arm) {
-  Init("jit_map_arm/", ARCH_ARM);
+  ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
 
   maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
              "jit_map0.so", 0);
@@ -1111,4 +1126,78 @@
   EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
 }
 
+TEST_F(UnwindOfflineTest, offset_arm) {
+  ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 0032bfa0 (offset 0x42000)  libunwindstack_test (SignalInnerFunction+40)\n"
+      "  #01 pc 0032bfeb (offset 0x42000)  libunwindstack_test (SignalMiddleFunction+2)\n"
+      "  #02 pc 0032bff3 (offset 0x42000)  libunwindstack_test (SignalOuterFunction+2)\n"
+      "  #03 pc 0032fed3 (offset 0x42000)  libunwindstack_test "
+      "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
+      "  #04 pc 00026528 (offset 0x25000)  libc.so\n"
+      "  #05 pc 00000000  <unknown>\n"
+      "  #06 pc 0032c2d9 (offset 0x42000)  libunwindstack_test (InnerFunction+736)\n"
+      "  #07 pc 0032cc4f (offset 0x42000)  libunwindstack_test (MiddleFunction+42)\n"
+      "  #08 pc 0032cc81 (offset 0x42000)  libunwindstack_test (OuterFunction+42)\n"
+      "  #09 pc 0032e547 (offset 0x42000)  libunwindstack_test "
+      "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
+      "  #10 pc 0032ed99 (offset 0x42000)  libunwindstack_test "
+      "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
+      "  #11 pc 00354453 (offset 0x42000)  libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
+      "  #12 pc 00354de7 (offset 0x42000)  libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
+      "  #13 pc 00355105 (offset 0x42000)  libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+      "  #14 pc 0035a215 (offset 0x42000)  libunwindstack_test "
+      "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
+      "  #15 pc 00359f4f (offset 0x42000)  libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+      "  #16 pc 0034d3db (offset 0x42000)  libunwindstack_test (main+38)\n"
+      "  #17 pc 00092c0d (offset 0x25000)  libc.so (__libc_init+48)\n"
+      "  #18 pc 0004202f (offset 0x42000)  libunwindstack_test (_start_main+38)\n",
+      frame_info);
+
+  EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
+  EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
+  EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
+  EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
+  EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
+  EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
+  EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
+  EXPECT_EQ(0U, unwinder.frames()[5].pc);
+  EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
+  EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
+  EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
+  EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
+  EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
+  EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
+  EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
+  EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
+  EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
+  EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
+  EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
+  EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
+  EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
+  EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
+  EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
+  EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
+  EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
+  EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
+  EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
+  EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
+  EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
+  EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
+  EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
+  EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
+  EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
+  EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
new file mode 100644
index 0000000..9f5c8ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
new file mode 100644
index 0000000..7a30bfa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
new file mode 100644
index 0000000..6224464
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -0,0 +1,2 @@
+2b6c000-2e92000 r-xp 42000 00:00 0   libunwindstack_test
+f4135000-f41a9000 r-xp 25000 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt
new file mode 100644
index 0000000..1f4ac8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 5
+r1: 5
+r2: 4
+r3: 1
+r4: 73804b6b
+r5: f3c9c000
+r6: 2ea09ac
+r7: 10624dd3
+r8: f41b5d8c
+r9: f3c9c000
+r10: 6f17
+r11: f3c94048
+ip: 2ea0807
+sp: f43d2ccc
+lr: 2e55fef
+pc: 2e55fa0
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data
new file mode 100644
index 0000000..23a9874
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data
new file mode 100644
index 0000000..49bdd1e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 74868d4..589731d 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -30,6 +30,7 @@
 #include <memory>
 #include <string>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <unwindstack/Elf.h>
@@ -78,30 +79,44 @@
   return true;
 }
 
-bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
-  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("stack.data", "w+"), &fclose);
-  if (fp == nullptr) {
-    printf("Failed to create stack.data.\n");
-    return false;
-  }
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+  for (size_t i = 0; i < stacks.size(); i++) {
+    std::string file_name;
+    if (stacks.size() != 1) {
+      file_name = "stack" + std::to_string(i) + ".data";
+    } else {
+      file_name = "stack.data";
+    }
 
-  size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
-  if (bytes != sizeof(sp_start)) {
-    perror("Failed to write all data.");
-    return false;
-  }
+    // Do this first, so if it fails, we don't create the file.
+    uint64_t sp_start = stacks[i].first;
+    uint64_t sp_end = stacks[i].second;
+    std::vector<uint8_t> buffer(sp_end - sp_start);
+    auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+    if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+      printf("Unable to read stack data.\n");
+      return false;
+    }
 
-  std::vector<uint8_t> buffer(sp_end - sp_start);
-  auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
-  if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
-    printf("Unable to read stack data.\n");
-    return false;
-  }
+    printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
 
-  bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
-  if (bytes != buffer.size()) {
-    printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
-    return 1;
+    std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
+    if (fp == nullptr) {
+      printf("Failed to create stack.data.\n");
+      return false;
+    }
+
+    size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+    if (bytes != sizeof(sp_start)) {
+      perror("Failed to write all data.");
+      return false;
+    }
+
+    bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+    if (bytes != buffer.size()) {
+      printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+      return false;
+    }
   }
 
   return true;
@@ -110,17 +125,11 @@
 bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
   std::string cur_name;
   if (info->name.empty()) {
-    cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start);
+    cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
   } else {
-    cur_name = basename(info->name.c_str());
-    cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start);
+    cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
   }
 
-  std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
-  if (output == nullptr) {
-    printf("Cannot create %s\n", cur_name.c_str());
-    return false;
-  }
   std::vector<uint8_t> buffer(info->end - info->start);
   // If this is a mapped in file, it might not be possible to read the entire
   // map, so read all that is readable.
@@ -129,6 +138,13 @@
     printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
     return false;
   }
+
+  std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+  if (output == nullptr) {
+    printf("Cannot create %s\n", cur_name.c_str());
+    return false;
+  }
+
   size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
   if (bytes_written != bytes) {
     printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
@@ -198,9 +214,21 @@
   unwinder.Unwind();
 
   std::unordered_map<uint64_t, map_info_t> maps_by_start;
-  uint64_t last_sp;
+  std::vector<std::pair<uint64_t, uint64_t>> stacks;
+  uint64_t sp_map_start = 0;
+  unwindstack::MapInfo* map_info = maps.Find(sp);
+  if (map_info != nullptr) {
+    stacks.emplace_back(std::make_pair(sp, map_info->end));
+    sp_map_start = map_info->start;
+  }
+
   for (auto frame : unwinder.frames()) {
-    last_sp = frame.sp;
+    map_info = maps.Find(frame.sp);
+    if (map_info != nullptr && sp_map_start != map_info->start) {
+      stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
+      sp_map_start = map_info->start;
+    }
+
     if (maps_by_start.count(frame.map_start) == 0) {
       auto info = &maps_by_start[frame.map_start];
       info->start = frame.map_start;
@@ -211,7 +239,12 @@
         // Try to create the elf from memory, this will handle cases where
         // the data only exists in memory such as vdso data on x86.
         if (!CreateElfFromMemory(process_memory, info)) {
-          return 1;
+          printf("Ignoring map ");
+          if (!info->name.empty()) {
+            printf("%s\n", info->name.c_str());
+          } else {
+            printf("anonymous:%" PRIx64 "\n", info->start);
+          }
         }
       }
     }
@@ -221,7 +254,7 @@
     printf("%s\n", unwinder.FormatFrame(i).c_str());
   }
 
-  if (!SaveStack(pid, sp, last_sp)) {
+  if (!SaveStack(pid, stacks)) {
     return 1;
   }
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d3504ad..197047d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -419,6 +419,7 @@
     mkdir /data/misc/radio 0770 system radio
     mkdir /data/misc/sms 0770 system radio
     mkdir /data/misc/carrierid 0770 system radio
+    mkdir /data/misc/apns 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
     mkdir /data/misc/textclassifier 0771 system system
     mkdir /data/misc/vpn 0770 system vpn