summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 02:07:04 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 02:07:04 +0000
commit6616caca652e832fb6144d3b428468bc1414c9cb (patch)
treed57fde808eb29d5bc6d96fa2497d95e4257729ba
parent5e06a0a2fb83f921ad8c9a25c7b70c241dc675e8 (diff)
parent3631a30920e393cd9608cc51ed2a3ea49d0bd620 (diff)
downloadgsid-android12-mainline-captiveportallogin-release.tar.gz
Snap for 7550930 from 3631a30920e393cd9608cc51ed2a3ea49d0bd620 to mainline-captiveportallogin-releaseandroid-mainline-12.0.0_r6android-mainline-12.0.0_r23android12-mainline-captiveportallogin-release
Change-Id: If53ad79c5d6839eb097c9c4d3501edcc6a53ce7b
-rw-r--r--Android.bp27
-rw-r--r--AndroidTest.xml30
-rw-r--r--aidl/android/gsi/IGsiService.aidl20
-rw-r--r--daemon.cpp6
-rw-r--r--gsi_service.cpp127
-rw-r--r--gsi_service.h2
-rw-r--r--gsi_tool.cpp148
-rw-r--r--gsid.rc5
-rw-r--r--include/libgsi/libgsi.h40
-rw-r--r--libgsi.cpp4
-rw-r--r--libgsi_private.h3
-rw-r--r--libgsid.cpp1
-rw-r--r--partition_installer.cpp60
-rw-r--r--partition_installer.h15
-rw-r--r--tests/Android.bp14
-rw-r--r--tests/AndroidManifest.xml30
-rw-r--r--tests/DSUEndtoEndTest.java34
-rw-r--r--tests/LockScreenAutomation.java151
18 files changed, 382 insertions, 335 deletions
diff --git a/Android.bp b/Android.bp
index 160a220..6b0f829 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_binary {
name: "gsi_tool",
shared_libs: [
@@ -55,14 +59,8 @@ cc_library_static {
"gsi_aidl_interface-cpp",
"libbase",
"libbinder",
- "libcutils",
- "liblog",
- "libservices",
"libutils",
],
- static_libs: [
- "libgsi",
- ],
export_include_dirs: ["include"],
}
@@ -103,9 +101,22 @@ cc_binary {
"libgsi",
"libgsid",
"liblp",
+ "libselinux",
"libutils",
"libc++fs",
+ "libvold_binder",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libprocessgroup",
+ "libvndksupport",
+ ],
+ },
+ },
local_include_dirs: ["include"],
}
@@ -134,7 +145,3 @@ filegroup {
],
path: "aidl",
}
-
-vts_config {
- name: "VtsGsiBootTest",
-}
diff --git a/AndroidTest.xml b/AndroidTest.xml
deleted file mode 100644
index 09ec7bd..0000000
--- a/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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 5503493..c889987 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -90,6 +90,7 @@ interface IGsiService {
* Asynchronous enableGsi
* @param result callback for result
*/
+ @SuppressWarnings(value={"mixed-oneway"})
oneway void enableGsiAsync(boolean oneShot, @utf8InCpp String dsuSlot, IGsiServiceCallback result);
/**
@@ -120,6 +121,7 @@ interface IGsiService {
* Asynchronous removeGsi
* @param result callback for result
*/
+ @SuppressWarnings(value={"mixed-oneway"})
oneway void removeGsiAsync(IGsiServiceCallback result);
/**
@@ -179,6 +181,16 @@ interface IGsiService {
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.
*
@@ -212,10 +224,16 @@ interface IGsiService {
* 2. Open a new partition installer.
* 3. Create and map the new partition.
*
- * In other words, getAvbPublicKey() works between two createPartition() calls.
+ * In other words, getAvbPublicKey() should be called after
+ * createPartition() is called and before closePartition() is called.
*
* @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 ba05eb6..a1ee809 100644
--- a/daemon.cpp
+++ b/daemon.cpp
@@ -14,8 +14,6 @@
// limitations under the License.
//
-#include <getopt.h>
-
#include <iostream>
#include <string>
@@ -23,7 +21,6 @@
#include <binder/BinderService.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <libgsi/libgsi.h>
#include <libgsi/libgsid.h>
#include "gsi_service.h"
@@ -52,6 +49,9 @@ static int DumpDeviceMapper() {
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 ab2339b..939f603 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -16,12 +16,7 @@
#include "gsi_service.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/statvfs.h>
#include <sys/vfs.h>
#include <unistd.h>
@@ -38,6 +33,8 @@
#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>
@@ -46,6 +43,8 @@
#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"
@@ -56,6 +55,7 @@ namespace gsi {
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,13 +67,21 @@ using android::base::WriteStringToFile;
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_ = {};
}
@@ -119,6 +127,8 @@ int GsiService::SaveInstallation(const std::string& installation) {
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_);
@@ -165,9 +175,6 @@ binder::Status GsiService::createPartition(const ::std::string& name, int64_t si
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) {
@@ -179,14 +186,38 @@ binder::Status GsiService::createPartition(const ::std::string& name, int64_t si
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_ = {};
- int status = installer_->StartInstall();
- if (status != INSTALL_OK) {
- installer_ = nullptr;
+ *_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();
}
- *_aidl_return = status;
+ // It is important to not reset |installer_| here because other methods such
+ // as enableGsi() relies on the state of |installer_|.
+ *_aidl_return = installer_->FinishInstall();
return binder::Status::ok();
}
@@ -278,6 +309,7 @@ binder::Status GsiService::enableGsi(bool one_shot, const std::string& dsuSlot,
*_aidl_return = INSTALL_ERROR_GENERIC;
return binder::Status::ok();
}
+ RestoreconMetadataFiles();
if (installer_) {
ENFORCE_SYSTEM;
installer_ = {};
@@ -472,6 +504,31 @@ binder::Status GsiService::getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_ret
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;
@@ -707,6 +764,8 @@ bool ImageService::CheckUid() {
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/";
@@ -728,9 +787,11 @@ binder::Status GsiService::openImageService(const std::string& prefix,
PLOG(ERROR) << "realpath failed for data: " << in_data_dir;
return BinderError("Invalid path");
}
- if (!android::base::StartsWith(metadata_dir, kImageMetadataPrefix) ||
- !android::base::StartsWith(data_dir, kImageDataPrefix)) {
- 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");
}
uid_t uid = IPCThreadState::self()->getCallingUid();
@@ -763,7 +824,7 @@ binder::Status GsiService::CheckUid(AccessLevel level) {
}
static bool IsExternalStoragePath(const std::string& path) {
- if (!android::base::StartsWith(path, "/mnt/media_rw/")) {
+ if (!android::base::StartsWith(path, kDsuSDPrefix)) {
return false;
}
unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
@@ -788,6 +849,14 @@ int GsiService::ValidateInstallParams(std::string& install_dir) {
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)) {
@@ -878,6 +947,10 @@ int GsiService::ReenableGsi(bool one_shot) {
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);
@@ -907,6 +980,22 @@ bool GsiService::RemoveGsiFiles(const std::string& install_dir) {
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 229db36..c9e4e05 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -42,6 +42,7 @@ class GsiService : public BinderService<GsiService>, public BnGsiService {
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;
@@ -67,6 +68,7 @@ class GsiService : public BinderService<GsiService>, public BnGsiService {
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 4c246cc..a6a79a5 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -35,7 +35,6 @@
#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>
@@ -51,6 +50,7 @@ using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>;
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,6 +61,7 @@ static const std::map<std::string, CommandCallback> kCommandMap = {
{"disable", Disable},
{"enable", Enable},
{"install", Install},
+ {"create-partition", CreatePartition},
{"wipe", Wipe},
{"wipe-data", WipeData},
{"status", Status},
@@ -256,7 +257,7 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
return EX_SOFTWARE;
}
- android::base::unique_fd input(dup(1));
+ android::base::unique_fd input(dup(STDIN_FILENO));
if (input < 0) {
std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
return EX_SOFTWARE;
@@ -277,6 +278,12 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
<< "\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);
@@ -294,6 +301,13 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
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";
@@ -323,6 +337,134 @@ static int Install(sp<IGsiService> gsid, int argc, char** argv) {
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;
@@ -570,7 +712,7 @@ static int usage(int /* argc */, char* argv[]) {
}
int main(int argc, char** argv) {
- android::base::InitLogging(argv, android::base::StdioLogger, android::base::DefaultAborter);
+ android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
android::sp<IGsiService> service = GetGsiService();
if (!service) {
diff --git a/gsid.rc b/gsid.rc
index 6b9663f..2c1a10b 100644
--- a/gsid.rc
+++ b/gsid.rc
@@ -10,8 +10,13 @@ on post-fs
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 fbe2f17..41898df 100644
--- a/include/libgsi/libgsi.h
+++ b/include/libgsi/libgsi.h
@@ -18,6 +18,9 @@
#include <string>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
namespace android {
namespace gsi {
@@ -25,6 +28,14 @@ static constexpr char kGsiServiceName[] = "gsiservice";
#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";
@@ -33,6 +44,10 @@ static constexpr char kDsuActiveFile[] = DSU_METADATA_PREFIX "active";
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";
}
@@ -41,6 +56,24 @@ static inline std::string DsuInstallDirFile(const std::string& dsu_slot) {
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);
@@ -51,11 +84,16 @@ static constexpr char kGsiInstalledProp[] = "gsid.image_installed";
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
-bool GetActiveDsu(std::string* active_dsu);
+static inline bool GetActiveDsu(std::string* active_dsu) {
+ return android::base::ReadFileToString(kDsuActiveFile, 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 b342cc5..3b0db51 100644
--- a/libgsi.cpp
+++ b/libgsi.cpp
@@ -37,10 +37,6 @@ using android::base::ReadFileToString;
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 51c7915..82814a9 100644
--- a/libgsi_private.h
+++ b/libgsi_private.h
@@ -28,5 +28,8 @@ static constexpr char kInstallStatusOk[] = "ok";
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 b42833c..23eeae4 100644
--- a/libgsid.cpp
+++ b/libgsid.cpp
@@ -15,7 +15,6 @@
//
#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 357df50..79af71a 100644
--- a/partition_installer.cpp
+++ b/partition_installer.cpp
@@ -39,10 +39,6 @@ using namespace android::fiemap;
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)
@@ -56,33 +52,32 @@ PartitionInstaller::PartitionInstaller(GsiService* service, const std::string& i
}
PartitionInstaller::~PartitionInstaller() {
- Finish();
- if (!succeeded_) {
- // Close open handles before we remove files.
- system_device_ = nullptr;
- PostInstallCleanup(images_.get());
+ if (FinishInstall() != IGsiService::INSTALL_OK) {
+ LOG(ERROR) << "Installation failed: install_dir=" << install_dir_
+ << ", dsu_slot=" << active_dsu_ << ", partition_name=" << name_;
}
if (IsAshmemMapped()) {
UnmapAshmem();
}
}
-void PartitionInstaller::PostInstallCleanup() {
- auto manager = ImageManager::Open(MetadataDir(active_dsu_), install_dir_);
- if (!manager) {
- LOG(ERROR) << "Could not open image manager";
- return;
- }
- 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);
+int PartitionInstaller::FinishInstall() {
+ if (finished_) {
+ return finished_status_;
+ }
+ 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);
}
- manager->DeleteBackingImage(file);
+ return finished_status_;
}
int PartitionInstaller::StartInstall() {
@@ -96,7 +91,6 @@ int PartitionInstaller::StartInstall() {
if (!Format()) {
return IGsiService::INSTALL_ERROR_GENERIC;
}
- succeeded_ = true;
} else {
// Map ${name}_gsi so we can write to it.
system_device_ = OpenPartition(GetBackingFile(name_));
@@ -132,8 +126,8 @@ int PartitionInstaller::PerformSanityChecks() {
// 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 = 1ULL * sb.f_bavail * sb.f_frsize;
- uint64_t fs_size = sb.f_blocks * sb.f_frsize;
+ 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;
if (free_space <= (size_)) {
LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
return IGsiService::INSTALL_ERROR_NO_SPACE;
@@ -308,26 +302,22 @@ bool PartitionInstaller::Format() {
return true;
}
-int PartitionInstaller::Finish() {
- if (readOnly_ && gsi_bytes_written_ != size_) {
+int PartitionInstaller::CheckInstallState() {
+ if (readOnly_ && !IsFinishedWriting()) {
// 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(system_device_->fd())) {
- PLOG(ERROR) << "fsync failed for " << name_ << "_gsi";
+ if (system_device_ != nullptr && fsync(GetPartitionFd())) {
+ PLOG(ERROR) << "fsync failed for " << GetBackingFile(name_);
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 1503648..920af47 100644
--- a/partition_installer.h
+++ b/partition_installer.h
@@ -53,14 +53,17 @@ class PartitionInstaller final {
static int WipeWritable(const std::string& active_dsu, const std::string& install_dir,
const std::string& name);
- // Clean up install state if gsid crashed and restarted.
- void PostInstallCleanup();
- void PostInstallCleanup(ImageManager* manager);
+ // 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();
const std::string& install_dir() const { return install_dir_; }
private:
- int Finish();
int PerformSanityChecks();
int Preallocate();
bool Format();
@@ -82,10 +85,12 @@ class PartitionInstaller final {
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 034209f..2cb67ab 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_defaults {
name: "gsi_boot_defaults",
shared_libs: [
@@ -58,13 +62,3 @@ java_test_host {
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
deleted file mode 100644
index 19d0c53..0000000
--- a/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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 69d40ad..e717079 100644
--- a/tests/DSUEndtoEndTest.java
+++ b/tests/DSUEndtoEndTest.java
@@ -47,11 +47,6 @@ import java.util.concurrent.TimeUnit;
@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";
@@ -74,7 +69,6 @@ public class DSUEndtoEndTest extends BaseHostJUnit4Test {
@After
public void teardown() throws Exception {
- uninstallPackage(PACKAGE);
if (mUnsparseSystemImage != null) {
mUnsparseSystemImage.delete();
}
@@ -129,13 +123,6 @@ public class DSUEndtoEndTest extends BaseHostJUnit4Test {
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 +
@@ -145,7 +132,7 @@ public class DSUEndtoEndTest extends BaseHostJUnit4Test {
expectGsiStatus("running");
- rebootAndUnlock();
+ getDevice().rebootUntilOnline();
expectGsiStatus("installed");
@@ -162,16 +149,10 @@ public class DSUEndtoEndTest extends BaseHostJUnit4Test {
getDevice().executeShellV2Command("gsi_tool wipe");
- rebootAndUnlock();
+ getDevice().rebootUntilOnline();
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();
}
@@ -182,16 +163,5 @@ public class DSUEndtoEndTest extends BaseHostJUnit4Test {
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
deleted file mode 100644
index afefa1c..0000000
--- a/tests/LockScreenAutomation.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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);
- }
-}