Snap for 8426163 from 5e06a0a2fb83f921ad8c9a25c7b70c241dc675e8 to mainline-tzdata2-release

Change-Id: Id650c5f6089116c6c50d24ba465f9a0f66aa05f9
diff --git a/Android.bp b/Android.bp
index 6b0f829..160a220 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,10 +14,6 @@
 // limitations under the License.
 //
 
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
 cc_binary {
     name: "gsi_tool",
     shared_libs: [
@@ -59,8 +55,14 @@
         "gsi_aidl_interface-cpp",
         "libbase",
         "libbinder",
+        "libcutils",
+        "liblog",
+        "libservices",
         "libutils",
     ],
+    static_libs: [
+        "libgsi",
+    ],
     export_include_dirs: ["include"],
 }
 
@@ -101,22 +103,9 @@
         "libgsi",
         "libgsid",
         "liblp",
-        "libselinux",
         "libutils",
         "libc++fs",
-        "libvold_binder",
     ],
-    header_libs: [
-        "libstorage_literals_headers",
-    ],
-    target: {
-        android: {
-            shared_libs: [
-                "libprocessgroup",
-                "libvndksupport",
-            ],
-        },
-    },
     local_include_dirs: ["include"],
 }
 
@@ -145,3 +134,7 @@
     ],
     path: "aidl",
 }
+
+vts_config {
+    name: "VtsGsiBootTest",
+}
diff --git a/AndroidTest.xml b/AndroidTest.xml
new file mode 100644
index 0000000..09ec7bd
--- /dev/null
+++ b/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for VTS VtsGsiBootTest">
+    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+        <option name="abort-on-push-failure" value="false"/>
+        <option name="push-group" value="HostDrivenTest.push"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+      <option name="test-module-name" value="VtsGsiBootTest"/>
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/gsi_boot_test/gsi_boot_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/gsi_boot_test/gsi_boot_test" />
+        <option name="binary-test-type" value="gtest"/>
+        <option name="precondition-first-api-level" value="29" />
+        <option name="test-timeout" value="1m"/>
+    </test>
+</configuration>
diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl
index c889987..5503493 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -90,7 +90,6 @@
      * Asynchronous enableGsi
      * @param result        callback for result
      */
-    @SuppressWarnings(value={"mixed-oneway"})
     oneway void enableGsiAsync(boolean oneShot, @utf8InCpp String dsuSlot, IGsiServiceCallback result);
 
     /**
@@ -121,7 +120,6 @@
      * Asynchronous removeGsi
      * @param result        callback for result
      */
-    @SuppressWarnings(value={"mixed-oneway"})
     oneway void removeGsiAsync(IGsiServiceCallback result);
 
     /**
@@ -181,16 +179,6 @@
     int createPartition(in @utf8InCpp String name, long size, boolean readOnly);
 
     /**
-     * Complete the current partition installation. A partition installation is
-     * complete after all pending bytes are written successfully.
-     * Returns an error if current installation still have pending bytes.
-     * Returns an error if there is any internal filesystem error.
-     *
-     * @return              0 on success, an error code on failure.
-     */
-    int closePartition();
-
-    /**
      * Wipe a partition. This will not work if the GSI is currently running.
      * The partition will not be removed, but the first block will be zeroed.
      *
@@ -224,16 +212,10 @@
      * 2. Open a new partition installer.
      * 3. Create and map the new partition.
      *
-     * In other words, getAvbPublicKey() should be called after
-     * createPartition() is called and before closePartition() is called.
+     * In other words, getAvbPublicKey() works between two createPartition() calls.
      *
      * @param dst           Output the AVB public key.
      * @return              0 on success, an error code on failure.
      */
     int getAvbPublicKey(out AvbPublicKey dst);
-
-    /**
-     * Returns the suggested scratch partition size for overlayFS.
-     */
-    long suggestScratchSize();
 }
diff --git a/daemon.cpp b/daemon.cpp
index a1ee809..ba05eb6 100644
--- a/daemon.cpp
+++ b/daemon.cpp
@@ -14,6 +14,8 @@
 // limitations under the License.
 //
 
+#include <getopt.h>
+
 #include <iostream>
 #include <string>
 
@@ -21,6 +23,7 @@
 #include <binder/BinderService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
+#include <libgsi/libgsi.h>
 #include <libgsi/libgsid.h>
 
 #include "gsi_service.h"
@@ -49,9 +52,6 @@
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
-    // Create globally readable files.
-    umask(0022);
-
     if (argc > 1) {
         if (argv[1] == "run-startup-tasks"s) {
             android::gsi::GsiService::RunStartupTasks();
diff --git a/gsi_service.cpp b/gsi_service.cpp
index 939f603..ab2339b 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -16,7 +16,12 @@
 
 #include "gsi_service.h"
 
-#include <sys/statvfs.h>
+#include <errno.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
@@ -33,8 +38,6 @@
 #include <android-base/strings.h>
 #include <android/gsi/BnImageService.h>
 #include <android/gsi/IGsiService.h>
-#include <android/os/IVold.h>
-#include <binder/IServiceManager.h>
 #include <binder/LazyServiceRegistrar.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
@@ -43,8 +46,6 @@
 #include <libfiemap/image_manager.h>
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-#include <storage_literals/storage_literals.h>
 
 #include "file_paths.h"
 #include "libgsi_private.h"
@@ -55,7 +56,6 @@
 using namespace std::literals;
 using namespace android::fs_mgr;
 using namespace android::fiemap;
-using namespace android::storage_literals;
 using android::base::ReadFileToString;
 using android::base::ReadFullyAtOffset;
 using android::base::RemoveFileIfExists;
@@ -67,21 +67,13 @@
 using android::binder::LazyServiceRegistrar;
 using android::dm::DeviceMapper;
 
+static std::mutex sInstanceLock;
+
 // Default userdata image size.
 static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024;
 
 static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst);
 
-// Fix the file contexts of dsu metadata files.
-// By default, newly created files inherit the file contexts of their parent
-// directory. Since globally readable public metadata files are labeled with a
-// different context, gsi_public_metadata_file, we need to call this function to
-// fix their contexts after creating them.
-static void RestoreconMetadataFiles() {
-    auto flags = SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH;
-    selinux_android_restorecon(DSU_METADATA_PREFIX, flags);
-}
-
 GsiService::GsiService() {
     progress_ = {};
 }
@@ -127,8 +119,6 @@
     return INSTALL_OK;
 }
 
-static bool IsExternalStoragePath(const std::string& path);
-
 binder::Status GsiService::openInstall(const std::string& install_dir, int* _aidl_return) {
     ENFORCE_SYSTEM;
     std::lock_guard<std::mutex> guard(lock_);
@@ -175,6 +165,9 @@
         return binder::Status::ok();
     }
 
+    // Make sure a pending interrupted installations are cleaned up.
+    installer_ = nullptr;
+
     // Do some precursor validation on the arguments before diving into the
     // install process.
     if (size % LP_SECTOR_SIZE) {
@@ -186,38 +179,14 @@
     if (size == 0 && name == "userdata") {
         size = kDefaultUserdataSize;
     }
-
-    if (name == "userdata") {
-        auto dsu_slot = GetDsuSlot(install_dir_);
-        auto key_dir = DefaultDsuMetadataKeyDir(dsu_slot);
-        auto key_dir_file = DsuMetadataKeyDirFile(dsu_slot);
-        if (!android::base::WriteStringToFile(key_dir, key_dir_file)) {
-            PLOG(ERROR) << "write failed: " << key_dir_file;
-            *_aidl_return = INSTALL_ERROR_GENERIC;
-            return binder::Status::ok();
-        }
-        RestoreconMetadataFiles();
-    }
-
     installer_ = std::make_unique<PartitionInstaller>(this, install_dir_, name,
                                                       GetDsuSlot(install_dir_), size, readOnly);
     progress_ = {};
-    *_aidl_return = installer_->StartInstall();
-    return binder::Status::ok();
-}
-
-binder::Status GsiService::closePartition(int32_t* _aidl_return) {
-    ENFORCE_SYSTEM;
-    std::lock_guard<std::mutex> guard(lock_);
-
-    if (installer_ == nullptr) {
-        LOG(ERROR) << "createPartition() has to be called before closePartition()";
-        *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
-        return binder::Status::ok();
+    int status = installer_->StartInstall();
+    if (status != INSTALL_OK) {
+        installer_ = nullptr;
     }
-    // It is important to not reset |installer_| here because other methods such
-    // as enableGsi() relies on the state of |installer_|.
-    *_aidl_return = installer_->FinishInstall();
+    *_aidl_return = status;
     return binder::Status::ok();
 }
 
@@ -309,7 +278,6 @@
         *_aidl_return = INSTALL_ERROR_GENERIC;
         return binder::Status::ok();
     }
-    RestoreconMetadataFiles();
     if (installer_) {
         ENFORCE_SYSTEM;
         installer_ = {};
@@ -504,31 +472,6 @@
     return binder::Status::ok();
 }
 
-binder::Status GsiService::suggestScratchSize(int64_t* _aidl_return) {
-    ENFORCE_SYSTEM;
-
-    static constexpr uint64_t kMinScratchSize = 512_MiB;
-    static constexpr uint64_t kMaxScratchSize = 2_GiB;
-
-    uint64_t size = 0;
-    struct statvfs info;
-    if (statvfs(install_dir_.c_str(), &info)) {
-        PLOG(ERROR) << "Could not statvfs(" << install_dir_ << ")";
-    } else {
-        // Keep the storage device at least 40% free, plus 1% for jitter.
-        constexpr int jitter = 1;
-        const uint64_t reserved_blocks =
-                static_cast<uint64_t>(info.f_blocks) * (kMinimumFreeSpaceThreshold + jitter) / 100;
-        if (info.f_bavail > reserved_blocks) {
-            size = (info.f_bavail - reserved_blocks) * info.f_frsize;
-        }
-    }
-
-    // We can safely downcast the result here, since we clamped the result within int64_t range.
-    *_aidl_return = std::clamp(size, kMinScratchSize, kMaxScratchSize);
-    return binder::Status::ok();
-}
-
 bool GsiService::CreateInstallStatusFile() {
     if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
         PLOG(ERROR) << "write " << kDsuInstallStatusFile;
@@ -764,8 +707,6 @@
 
 binder::Status GsiService::openImageService(const std::string& prefix,
                                             android::sp<IImageService>* _aidl_return) {
-    using android::base::StartsWith;
-
     static constexpr char kImageMetadataPrefix[] = "/metadata/gsi/";
     static constexpr char kImageDataPrefix[] = "/data/gsi/";
 
@@ -787,11 +728,9 @@
         PLOG(ERROR) << "realpath failed for data: " << in_data_dir;
         return BinderError("Invalid path");
     }
-    if (!StartsWith(metadata_dir, kImageMetadataPrefix)) {
-        return BinderError("Invalid metadata path");
-    }
-    if (!StartsWith(data_dir, kImageDataPrefix) && !StartsWith(data_dir, kDsuSDPrefix)) {
-        return BinderError("Invalid data path");
+    if (!android::base::StartsWith(metadata_dir, kImageMetadataPrefix) ||
+        !android::base::StartsWith(data_dir, kImageDataPrefix)) {
+        return BinderError("Invalid path");
     }
 
     uid_t uid = IPCThreadState::self()->getCallingUid();
@@ -824,7 +763,7 @@
 }
 
 static bool IsExternalStoragePath(const std::string& path) {
-    if (!android::base::StartsWith(path, kDsuSDPrefix)) {
+    if (!android::base::StartsWith(path, "/mnt/media_rw/")) {
         return false;
     }
     unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
@@ -849,14 +788,6 @@
         install_dir = kDefaultDsuImageFolder;
     }
 
-    if (access(install_dir.c_str(), F_OK) != 0 && (errno == ENOENT)) {
-        if (android::base::StartsWith(install_dir, kDsuSDPrefix)) {
-            if (mkdir(install_dir.c_str(), 0755) != 0) {
-                PLOG(ERROR) << "Failed to create " << install_dir;
-                return INSTALL_ERROR_GENERIC;
-            }
-        }
-    }
     // Normalize the path and add a trailing slash.
     std::string origInstallDir = install_dir;
     if (!android::base::Realpath(origInstallDir, &install_dir)) {
@@ -947,10 +878,6 @@
     return IGsiService::INSTALL_OK;
 }
 
-static android::sp<android::os::IVold> GetVoldService() {
-    return android::waitForService<android::os::IVold>(android::String16("vold"));
-}
-
 bool GsiService::RemoveGsiFiles(const std::string& install_dir) {
     bool ok = true;
     auto active_dsu = GetDsuSlot(install_dir);
@@ -980,22 +907,6 @@
             ok = false;
         }
     }
-    if (auto vold = GetVoldService()) {
-        auto status = vold->destroyDsuMetadataKey(dsu_slot);
-        if (status.isOk()) {
-            std::string message;
-            if (!RemoveFileIfExists(DsuMetadataKeyDirFile(dsu_slot), &message)) {
-                LOG(ERROR) << message;
-                ok = false;
-            }
-        } else {
-            LOG(ERROR) << "Failed to destroy DSU metadata encryption key.";
-            ok = false;
-        }
-    } else {
-        LOG(ERROR) << "Failed to retrieve vold service.";
-        ok = false;
-    }
     if (ok) {
         SetProperty(kGsiInstalledProp, "0");
     }
diff --git a/gsi_service.h b/gsi_service.h
index c9e4e05..229db36 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -42,7 +42,6 @@
     binder::Status closeInstall(int32_t* _aidl_return) override;
     binder::Status createPartition(const ::std::string& name, int64_t size, bool readOnly,
                                    int32_t* _aidl_return) override;
-    binder::Status closePartition(int32_t* _aidl_return) override;
     binder::Status commitGsiChunkFromStream(const ::android::os::ParcelFileDescriptor& stream,
                                             int64_t bytes, bool* _aidl_return) override;
     binder::Status getInstallProgress(::android::gsi::GsiProgress* _aidl_return) override;
@@ -68,7 +67,6 @@
                                     android::sp<IImageService>* _aidl_return) override;
     binder::Status dumpDeviceMapperDevices(std::string* _aidl_return) override;
     binder::Status getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) override;
-    binder::Status suggestScratchSize(int64_t* _aidl_return) override;
 
     // This is in GsiService, rather than GsiInstaller, since we need to access
     // it outside of the main lock which protects the unique_ptr.
diff --git a/gsi_tool.cpp b/gsi_tool.cpp
index a6a79a5..4c246cc 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -35,6 +35,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/gsi/IGsiService.h>
+#include <binder/IServiceManager.h>
 #include <cutils/android_reboot.h>
 #include <libgsi/libgsi.h>
 #include <libgsi/libgsid.h>
@@ -50,7 +51,6 @@
 static int Disable(sp<IGsiService> gsid, int argc, char** argv);
 static int Enable(sp<IGsiService> gsid, int argc, char** argv);
 static int Install(sp<IGsiService> gsid, int argc, char** argv);
-static int CreatePartition(sp<IGsiService> gsid, int argc, char** argv);
 static int Wipe(sp<IGsiService> gsid, int argc, char** argv);
 static int WipeData(sp<IGsiService> gsid, int argc, char** argv);
 static int Status(sp<IGsiService> gsid, int argc, char** argv);
@@ -61,7 +61,6 @@
         {"disable", Disable},
         {"enable", Enable},
         {"install", Install},
-        {"create-partition", CreatePartition},
         {"wipe", Wipe},
         {"wipe-data", WipeData},
         {"status", Status},
@@ -257,7 +256,7 @@
         return EX_SOFTWARE;
     }
 
-    android::base::unique_fd input(dup(STDIN_FILENO));
+    android::base::unique_fd input(dup(1));
     if (input < 0) {
         std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
         return EX_SOFTWARE;
@@ -278,12 +277,6 @@
                       << "\n";
             return EX_SOFTWARE;
         }
-        status = gsid->closePartition(&error);
-        if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-            std::cerr << "Could not closePartition(userdata): " << ErrorMessage(status, error)
-                      << std::endl;
-            return EX_SOFTWARE;
-        }
     }
 
     status = gsid->createPartition(partition, gsiSize, true, &error);
@@ -301,13 +294,6 @@
         return EX_SOFTWARE;
     }
 
-    status = gsid->closePartition(&error);
-    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-        std::cerr << "Could not closePartition(" << partition
-                  << "): " << ErrorMessage(status, error) << std::endl;
-        return EX_SOFTWARE;
-    }
-
     status = gsid->closeInstall(&error);
     if (!status.isOk() || error != IGsiService::INSTALL_OK) {
         std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n";
@@ -337,134 +323,6 @@
     return 0;
 }
 
-// Experimental API
-static int CreatePartition(sp<IGsiService> gsid, int argc, char** argv) {
-    std::string installDir;
-    std::string partitionName;
-    bool readOnly = true;
-    int64_t partitionSize = 0;
-
-    struct option options[] = {
-            {"install-dir", required_argument, nullptr, 'i'},
-            {"partition-name", required_argument, nullptr, 'p'},
-            {"readwrite", no_argument, nullptr, 'r'},
-            {"size", required_argument, nullptr, 's'},
-            {nullptr, 0, nullptr, 0},
-    };
-
-    int rv = 0;
-    while ((rv = getopt_long_only(argc, argv, "", options, nullptr)) != -1) {
-        switch (rv) {
-            case 'i':
-                installDir = optarg;
-                break;
-            case 'p':
-                partitionName = optarg;
-                break;
-            case 'r':
-                readOnly = false;
-                break;
-            case 's':
-                if (!android::base::ParseInt(optarg, &partitionSize)) {
-                    std::cerr << "Could not parse partition size: " << optarg << std::endl;
-                    return EX_USAGE;
-                }
-                break;
-            default:
-                return EX_USAGE;
-        }
-    }
-
-    if (getuid() != 0) {
-        std::cerr << "must be root to install a DSU" << std::endl;
-        return EX_NOPERM;
-    }
-
-    bool gsiRunning = false;
-    auto status = gsid->isGsiRunning(&gsiRunning);
-    if (!status.isOk()) {
-        std::cerr << "Could not get DSU running status: " << ErrorMessage(status) << std::endl;
-        return EX_SOFTWARE;
-    }
-    if (gsiRunning) {
-        std::cerr << "Could not install DSU within an active DSU." << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    if (partitionSize <= 0) {
-        std::cerr << "Partition size must be greater than zero: " << partitionSize << std::endl;
-        return EX_USAGE;
-    }
-
-    // Note: the progress bar needs to be re-started in between each call.
-    ProgressBar progress(gsid);
-    progress.Display();
-
-    int error;
-    status = gsid->openInstall(installDir, &error);
-    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-        std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error)
-                  << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    status = gsid->createPartition(partitionName, partitionSize, readOnly, &error);
-    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-        std::cerr << "Could not create DSU partition: " << ErrorMessage(status, error) << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    if (readOnly) {
-        android::base::unique_fd input(dup(STDIN_FILENO));
-        if (input < 0) {
-            std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
-            return EX_SOFTWARE;
-        }
-        android::os::ParcelFileDescriptor stream(std::move(input));
-
-        bool ok = false;
-        status = gsid->commitGsiChunkFromStream(stream, partitionSize, &ok);
-        if (!ok) {
-            std::cerr << "Could not commit data from stdin: " << ErrorMessage(status) << std::endl;
-            return EX_SOFTWARE;
-        }
-    }
-
-    status = gsid->closePartition(&error);
-    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-        std::cerr << "Could not close DSU partition:" << ErrorMessage(status, error) << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    status = gsid->closeInstall(&error);
-    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-        std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error)
-                  << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    progress.Finish();
-
-    std::string dsuSlot;
-    status = gsid->getActiveDsuSlot(&dsuSlot);
-    if (!status.isOk()) {
-        std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    // Immediately enable DSU after a partition is installed to ensure the installation status file
-    // is created.
-    status = gsid->enableGsi(/* one_shot = */ true, dsuSlot, &error);
-    if (!status.isOk() || error != IGsiService::INSTALL_OK) {
-        std::cerr << "Could not make DSU bootable: " << ErrorMessage(status, error) << std::endl;
-        return EX_SOFTWARE;
-    }
-
-    std::cout << "Enabled DSU slot: " << dsuSlot << std::endl;
-    std::cout << "Please reboot to use the DSU." << std::endl;
-    return 0;
-}
-
 static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) {
     if (argc > 1) {
         std::cerr << "Unrecognized arguments to wipe." << std::endl;
@@ -712,7 +570,7 @@
 }
 
 int main(int argc, char** argv) {
-    android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+    android::base::InitLogging(argv, android::base::StdioLogger, android::base::DefaultAborter);
 
     android::sp<IGsiService> service = GetGsiService();
     if (!service) {
diff --git a/gsid.rc b/gsid.rc
index 2c1a10b..6b9663f 100644
--- a/gsid.rc
+++ b/gsid.rc
@@ -10,13 +10,8 @@
     mkdir /metadata/gsi/dsu 0771 root system
     mkdir /metadata/gsi/ota 0771 root system
     mkdir /metadata/gsi/remount 0771 root system
-    chmod 0664 /metadata/gsi/dsu/active
-    chmod 0664 /metadata/gsi/dsu/booted
-    chmod 0664 /metadata/gsi/dsu/lp_names
 
 on post-fs-data
-    write /data/gsi_persistent_data 0
-    chown system system /data/gsi_persistent_data
     mkdir /data/gsi 0700 root root encryption=None
     mkdir /data/gsi/dsu 0700 root root
     mkdir /data/gsi/ota 0700 root root
diff --git a/include/libgsi/libgsi.h b/include/libgsi/libgsi.h
index 41898df..fbe2f17 100644
--- a/include/libgsi/libgsi.h
+++ b/include/libgsi/libgsi.h
@@ -18,9 +18,6 @@
 
 #include <string>
 
-#include <android-base/file.h>
-#include <android-base/strings.h>
-
 namespace android {
 namespace gsi {
 
@@ -28,14 +25,6 @@
 
 #define DSU_METADATA_PREFIX "/metadata/gsi/dsu/"
 
-// These files need to be globally readable so that fs_mgr_fstab, which is
-// statically linked into processes, can return consistent result for non-root
-// processes:
-// * kDsuActiveFile
-// * kGsiBootedIndicatorFile
-// * kGsiLpNamesFile
-// * DsuMetadataKeyDirFile(slot)
-
 static constexpr char kGsiBootedIndicatorFile[] = DSU_METADATA_PREFIX "booted";
 
 static constexpr char kGsiLpNamesFile[] = DSU_METADATA_PREFIX "lp_names";
@@ -44,10 +33,6 @@
 
 static constexpr char kDsuAvbKeyDir[] = DSU_METADATA_PREFIX "avb/";
 
-static constexpr char kDsuMetadataKeyDirPrefix[] = "/metadata/vold/metadata_encryption/dsu/";
-
-static constexpr char kDsuSDPrefix[] = "/mnt/media_rw/";
-
 static inline std::string DsuLpMetadataFile(const std::string& dsu_slot) {
     return DSU_METADATA_PREFIX + dsu_slot + "/lp_metadata";
 }
@@ -56,24 +41,6 @@
     return DSU_METADATA_PREFIX + dsu_slot + "/install_dir";
 }
 
-static inline std::string DsuMetadataKeyDirFile(const std::string& dsu_slot) {
-    return DSU_METADATA_PREFIX + dsu_slot + "/metadata_encryption_dir";
-}
-
-static inline std::string DefaultDsuMetadataKeyDir(const std::string& dsu_slot) {
-    return kDsuMetadataKeyDirPrefix + dsu_slot;
-}
-
-static inline std::string GetDsuMetadataKeyDir(const std::string& dsu_slot) {
-    auto key_dir_file = DsuMetadataKeyDirFile(dsu_slot);
-    std::string key_dir;
-    if (android::base::ReadFileToString(key_dir_file, &key_dir) &&
-        android::base::StartsWith(key_dir, kDsuMetadataKeyDirPrefix)) {
-        return key_dir;
-    }
-    return DefaultDsuMetadataKeyDir(dsu_slot);
-}
-
 // install_dir "/data/gsi/dsu/dsu" has a slot name "dsu"
 // install_dir "/data/gsi/dsu/dsu2" has a slot name "dsu2"
 std::string GetDsuSlot(const std::string& install_dir);
@@ -84,16 +51,11 @@
 
 static constexpr char kDsuPostfix[] = "_gsi";
 
-inline constexpr char kDsuScratch[] = "scratch_gsi";
-inline constexpr char kDsuUserdata[] = "userdata_gsi";
-
 static constexpr int kMaxBootAttempts = 1;
 
 // Get the currently active dsu slot
 // Return true on success
-static inline bool GetActiveDsu(std::string* active_dsu) {
-    return android::base::ReadFileToString(kDsuActiveFile, active_dsu);
-}
+bool GetActiveDsu(std::string* active_dsu);
 
 // Returns true if the currently running system image is a live GSI.
 bool IsGsiRunning();
diff --git a/libgsi.cpp b/libgsi.cpp
index 3b0db51..b342cc5 100644
--- a/libgsi.cpp
+++ b/libgsi.cpp
@@ -37,6 +37,10 @@
 using android::base::Split;
 using android::base::unique_fd;
 
+bool GetActiveDsu(std::string* active_dsu) {
+    return android::base::ReadFileToString(kDsuActiveFile, active_dsu);
+}
+
 bool IsGsiRunning() {
     return !access(kGsiBootedIndicatorFile, F_OK);
 }
diff --git a/libgsi_private.h b/libgsi_private.h
index 82814a9..51c7915 100644
--- a/libgsi_private.h
+++ b/libgsi_private.h
@@ -28,8 +28,5 @@
 static constexpr char kInstallStatusWipe[] = "wipe";
 static constexpr char kInstallStatusDisabled[] = "disabled";
 
-// We are looking for /data to have at least 40% free space.
-static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
-
 }  // namespace gsi
 }  // namespace android
diff --git a/libgsid.cpp b/libgsid.cpp
index 23eeae4..b42833c 100644
--- a/libgsid.cpp
+++ b/libgsid.cpp
@@ -15,6 +15,7 @@
 //
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/gsi/IGsiService.h>
 #include <binder/IServiceManager.h>
 #include <libgsi/libgsi.h>
diff --git a/partition_installer.cpp b/partition_installer.cpp
index 79af71a..357df50 100644
--- a/partition_installer.cpp
+++ b/partition_installer.cpp
@@ -39,6 +39,10 @@
 using namespace android::fs_mgr;
 using android::base::unique_fd;
 
+// The default size of userdata.img for GSI.
+// We are looking for /data to have atleast 40% free space
+static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
+
 PartitionInstaller::PartitionInstaller(GsiService* service, const std::string& install_dir,
                                        const std::string& name, const std::string& active_dsu,
                                        int64_t size, bool read_only)
@@ -52,32 +56,33 @@
 }
 
 PartitionInstaller::~PartitionInstaller() {
-    if (FinishInstall() != IGsiService::INSTALL_OK) {
-        LOG(ERROR) << "Installation failed: install_dir=" << install_dir_
-                   << ", dsu_slot=" << active_dsu_ << ", partition_name=" << name_;
+    Finish();
+    if (!succeeded_) {
+        // Close open handles before we remove files.
+        system_device_ = nullptr;
+        PostInstallCleanup(images_.get());
     }
     if (IsAshmemMapped()) {
         UnmapAshmem();
     }
 }
 
-int PartitionInstaller::FinishInstall() {
-    if (finished_) {
-        return finished_status_;
+void PartitionInstaller::PostInstallCleanup() {
+    auto manager = ImageManager::Open(MetadataDir(active_dsu_), install_dir_);
+    if (!manager) {
+        LOG(ERROR) << "Could not open image manager";
+        return;
     }
-    finished_ = true;
-    finished_status_ = CheckInstallState();
-    system_device_ = nullptr;
-    if (finished_status_ != IGsiService::INSTALL_OK) {
-        auto file = GetBackingFile(name_);
-        LOG(ERROR) << "Installation failed, clean up: " << file;
-        if (images_->IsImageMapped(file)) {
-            LOG(ERROR) << "unmap " << file;
-            images_->UnmapImageDevice(file);
-        }
-        images_->DeleteBackingImage(file);
+    return PostInstallCleanup(manager.get());
+}
+
+void PartitionInstaller::PostInstallCleanup(ImageManager* manager) {
+    std::string file = GetBackingFile(name_);
+    if (manager->IsImageMapped(file)) {
+        LOG(ERROR) << "unmap " << file;
+        manager->UnmapImageDevice(file);
     }
-    return finished_status_;
+    manager->DeleteBackingImage(file);
 }
 
 int PartitionInstaller::StartInstall() {
@@ -91,6 +96,7 @@
         if (!Format()) {
             return IGsiService::INSTALL_ERROR_GENERIC;
         }
+        succeeded_ = true;
     } else {
         // Map ${name}_gsi so we can write to it.
         system_device_ = OpenPartition(GetBackingFile(name_));
@@ -126,8 +132,8 @@
 
     // This is the same as android::vold::GetFreebytes() but we also
     // need the total file system size so we open code it here.
-    uint64_t free_space = static_cast<uint64_t>(sb.f_bavail) * sb.f_frsize;
-    uint64_t fs_size = static_cast<uint64_t>(sb.f_blocks) * sb.f_frsize;
+    uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
+    uint64_t fs_size = sb.f_blocks * sb.f_frsize;
     if (free_space <= (size_)) {
         LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
         return IGsiService::INSTALL_ERROR_NO_SPACE;
@@ -302,22 +308,26 @@
     return true;
 }
 
-int PartitionInstaller::CheckInstallState() {
-    if (readOnly_ && !IsFinishedWriting()) {
+int PartitionInstaller::Finish() {
+    if (readOnly_ && gsi_bytes_written_ != size_) {
         // We cannot boot if the image is incomplete.
         LOG(ERROR) << "image incomplete; expected " << size_ << " bytes, waiting for "
                    << (size_ - gsi_bytes_written_) << " bytes";
         return IGsiService::INSTALL_ERROR_GENERIC;
     }
-    if (system_device_ != nullptr && fsync(GetPartitionFd())) {
-        PLOG(ERROR) << "fsync failed for " << GetBackingFile(name_);
+    if (system_device_ != nullptr && fsync(system_device_->fd())) {
+        PLOG(ERROR) << "fsync failed for " << name_ << "_gsi";
         return IGsiService::INSTALL_ERROR_GENERIC;
     }
+    system_device_ = {};
+
     // If files moved (are no longer pinned), the metadata file will be invalid.
     // This check can be removed once b/133967059 is fixed.
     if (!images_->Validate()) {
         return IGsiService::INSTALL_ERROR_GENERIC;
     }
+
+    succeeded_ = true;
     return IGsiService::INSTALL_OK;
 }
 
diff --git a/partition_installer.h b/partition_installer.h
index 920af47..1503648 100644
--- a/partition_installer.h
+++ b/partition_installer.h
@@ -53,17 +53,14 @@
     static int WipeWritable(const std::string& active_dsu, const std::string& install_dir,
                             const std::string& name);
 
-    // Finish a partition installation and release resources.
-    // If the installation is incomplete or corrupted, the backing image would
-    // be cleaned up and an error code is returned.
-    // No method other than FinishInstall() and ~PartitionInstaller() should be
-    // called after calling this method.
-    // This method is also called by the destructor to free up resources.
-    int FinishInstall();
+    // Clean up install state if gsid crashed and restarted.
+    void PostInstallCleanup();
+    void PostInstallCleanup(ImageManager* manager);
 
     const std::string& install_dir() const { return install_dir_; }
 
   private:
+    int Finish();
     int PerformSanityChecks();
     int Preallocate();
     bool Format();
@@ -85,12 +82,10 @@
     bool readOnly_;
     // Remaining data we're waiting to receive for the GSI image.
     uint64_t gsi_bytes_written_ = 0;
+    bool succeeded_ = false;
     uint64_t ashmem_size_ = -1;
     void* ashmem_data_ = MAP_FAILED;
 
-    bool finished_ = false;
-    int finished_status_ = 0;
-
     std::unique_ptr<MappedDevice> system_device_;
 };
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 2cb67ab..034209f 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -14,10 +14,6 @@
 // limitations under the License.
 //
 
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
 cc_defaults {
     name: "gsi_boot_defaults",
     shared_libs: [
@@ -62,3 +58,13 @@
     test_config: "dsu-test.xml",
     test_suites: ["general-tests"],
 }
+
+android_test {
+    name: "LockScreenAutomation",
+    srcs: ["LockScreenAutomation.java"],
+    libs: ["junit", "android.test.base.stubs"],
+    static_libs: ["androidx.test.uiautomator"],
+    certificate: "platform",
+    manifest: "AndroidManifest.xml",
+    test_suites: ["general-tests"],
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..19d0c53
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 Google Inc.
+
+     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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.google.android.lockscreenautomation"
+  android:sharedUserId="android.uid.system"
+  >
+
+    <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="28"/>
+
+    <application debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner"
+                     android:targetPackage="com.google.android.lockscreenautomation"
+                     android:label="Lock Screen Automation"/>
+</manifest>
diff --git a/tests/DSUEndtoEndTest.java b/tests/DSUEndtoEndTest.java
index e717079..69d40ad 100644
--- a/tests/DSUEndtoEndTest.java
+++ b/tests/DSUEndtoEndTest.java
@@ -47,6 +47,11 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class DSUEndtoEndTest extends BaseHostJUnit4Test {
     private static final long kDefaultUserdataSize = 4L * 1024 * 1024 * 1024;
+    private static final String APK = "LockScreenAutomation.apk";
+    private static final String PACKAGE = "com.google.android.lockscreenautomation";
+    private static final String UI_AUTOMATOR_INSTRUMENTATION_RUNNER =
+        "androidx.test.uiautomator.UiAutomatorInstrumentationTestRunner";
+    private static final String CLASS = "LockScreenAutomation";
     private static final String LPUNPACK_PATH = "bin/lpunpack";
     private static final String SIMG2IMG_PATH = "bin/simg2img";
 
@@ -69,6 +74,7 @@
 
     @After
     public void teardown() throws Exception {
+        uninstallPackage(PACKAGE);
         if (mUnsparseSystemImage != null) {
             mUnsparseSystemImage.delete();
         }
@@ -123,6 +129,13 @@
 
         expectGsiStatus("normal");
 
+        installPackage(APK);
+        String method = "setPin";
+        String testClass = PACKAGE + "." + CLASS;
+        String testMethod = testClass + "." + method;
+        Assert.assertTrue(testMethod + " failed.",
+            runDeviceTests(UI_AUTOMATOR_INSTRUMENTATION_RUNNER, PACKAGE, testClass, method));
+
         // Sleep after installing to allow time for gsi_tool to reboot. This prevents a race between
         // the device rebooting and waitForDeviceAvailable() returning.
         getDevice().executeShellV2Command("gsi_tool install --userdata-size " + mUserdataSize +
@@ -132,7 +145,7 @@
 
         expectGsiStatus("running");
 
-        getDevice().rebootUntilOnline();
+        rebootAndUnlock();
 
         expectGsiStatus("installed");
 
@@ -149,10 +162,16 @@
 
         getDevice().executeShellV2Command("gsi_tool wipe");
 
-        getDevice().rebootUntilOnline();
+        rebootAndUnlock();
 
         expectGsiStatus("normal");
 
+        method = "removePin";
+        testClass = PACKAGE + "." + CLASS;
+        testMethod = testClass + "." + method;
+        Assert.assertTrue(testMethod + " failed.",
+            runDeviceTests(UI_AUTOMATOR_INSTRUMENTATION_RUNNER, PACKAGE, testClass, method));
+
         if (wasRoot) {
             getDevice().enableAdbRoot();
         }
@@ -163,5 +182,16 @@
         String status = result.getStdout().split("\n", 2)[0].trim();
         Assert.assertEquals("Device not in expected DSU state", expected, status);
     }
+
+    private void rebootAndUnlock() throws Exception {
+        getDevice().rebootUntilOnline();
+        getDevice().executeShellV2Command("input keyevent 224"); // KeyEvent.KEYCODE_WAKEUP
+        getDevice().executeShellV2Command("wm dismiss-keyguard");
+        getDevice().executeShellV2Command("input keyevent 7"); // KeyEvent.KEYCODE_0
+        getDevice().executeShellV2Command("input keyevent 7");
+        getDevice().executeShellV2Command("input keyevent 7");
+        getDevice().executeShellV2Command("input keyevent 7");
+        getDevice().executeShellV2Command("input keyevent 66"); // KeyEvent.KEYCODE_ENTER
+    }
 }
 
diff --git a/tests/LockScreenAutomation.java b/tests/LockScreenAutomation.java
new file mode 100644
index 0000000..afefa1c
--- /dev/null
+++ b/tests/LockScreenAutomation.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lockscreenautomation;
+
+import org.junit.Assert;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.provider.Settings;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiAutomatorTestCase;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
+import android.view.KeyEvent;
+
+/**
+ * Methods for configuring lock screen settings
+ */
+public class LockScreenAutomation extends UiAutomatorTestCase {
+
+    private static final String SETTINGS_PACKAGE = "com.android.settings";
+
+    private static final long TIMEOUT = 2000L;
+
+    private Context mContext;
+    private UiDevice mDevice;
+
+    public void setPin() throws Exception {
+        mContext = getInstrumentation().getContext();
+        mDevice = UiDevice.getInstance(getInstrumentation());
+
+        mDevice.wakeUp();
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_MENU);
+        mDevice.waitForIdle(TIMEOUT);
+        launchLockScreenSettings();
+
+        PackageManager pm = mContext.getPackageManager();
+        Resources res = pm.getResourcesForApplication(SETTINGS_PACKAGE);
+
+        int resId = res.getIdentifier("unlock_set_unlock_pin_title", "string", SETTINGS_PACKAGE);
+        findAndClick(By.text(res.getString(resId)));
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressEnter();
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+
+        // Re-enter PIN
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressEnter();
+
+        findAndClick(By.res(SETTINGS_PACKAGE, "redact_sensitive"));
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        findAndClick(By.clazz("android.widget.Button"));
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+    }
+
+    public void unlock() throws Exception {
+        mContext = getInstrumentation().getContext();
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER);
+    }
+
+    public void removePin() throws Exception {
+        mContext = getInstrumentation().getContext();
+        mDevice = UiDevice.getInstance(getInstrumentation());
+
+        mDevice.wakeUp();
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_MENU);
+        mDevice.waitForIdle(TIMEOUT);
+        launchLockScreenSettings();
+
+        PackageManager pm = mContext.getPackageManager();
+        Resources res = pm.getResourcesForApplication(SETTINGS_PACKAGE);
+
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_0);
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+        mDevice.pressEnter();
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+
+        int resId = res.getIdentifier("unlock_set_unlock_off_title", "string", SETTINGS_PACKAGE);
+        findAndClick(By.text(res.getString(resId)));
+        mDevice.waitForWindowUpdate(SETTINGS_PACKAGE, 5);
+
+        findAndClick(By.res("android", "button1"));
+        mDevice.waitForIdle(TIMEOUT);
+    }
+
+    private void findAndClick(BySelector selector)
+    {
+        for (int i = 0; i < 3; i++) {
+            mDevice.wait(Until.findObject(selector), TIMEOUT);
+            UiObject2 obj = mDevice.findObject(selector);
+            if (obj != null) {
+                obj.click();
+                return;
+            }
+        }
+        Assert.fail("Could not find and click " + selector);
+    }
+
+    private void launchLockScreenSettings() {
+        final Intent intent = new Intent().setClassName(SETTINGS_PACKAGE, "com.android.settings.password.ChooseLockGeneric");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(intent);
+        mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT);
+    }
+}