Merge "adb: add host tool to check for WinUSB descriptor."
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 716fe95..978eed0 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -134,8 +134,6 @@
"libfs_mgr",
"libgsi",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"liblog",
"liblp",
"libsparse",
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index fe2e052..bb63df8 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -1,33 +1,27 @@
-Android Overlayfs integration with adb remount
+Android OverlayFS Integration with adb Remount
==============================================
Introduction
------------
-Users working with userdebug or eng builds expect to be able to
-remount the system partition as read-write and then add or modify
-any number of files without reflashing the system image, which is
-understandably efficient for a development cycle.
-Limited memory systems that chose to use readonly filesystems like
-*squashfs*, or *Logical Resizable Android Partitions* which land
-system partition images right-sized, and with filesystem that have
-been deduped on the block level to compress the content; means that
-either a remount is not possible directly, or when done offers
-little or no utility because of remaining space limitations or
-support logistics.
+Users working with userdebug or eng builds expect to be able to remount the
+system partition as read-write and then add or modify any number of files
+without reflashing the system image, which is efficient for a development cycle.
-*Overlayfs* comes to the rescue for these debug scenarios, and logic
-will _automatically_ setup backing storage for a writable filesystem
-as an upper reference, and mount overtop the lower. These actions
-will be performed in the **adb disable-verity** and **adb remount**
-requests.
+Limited memory systems use read-only types of file systems or logical resizable
+Android partitions (LRAPs). These file systems land system partition images
+right-sized, and have been deduped at the block level to compress the content.
+This means that a remount either isn’t possible, or isn't useful because of
+space limitations or support logistics.
-Operations
-----------
+OverlayFS resolves these debug scenarios with the _adb disable-verity_ and
+_adb remount_ commands, which set up backing storage for a writable file
+system as an upper reference, and mount the lower reference on top.
-### Cookbook
+Performing a remount
+--------------------
-The typical action to utilize the remount facility is:
+Use the following sequence to perform the remount.
$ adb root
$ adb disable-verity
@@ -36,7 +30,7 @@
$ adb root
$ adb remount
-Followed by one of the following:
+Then enter one of the following sequences:
$ adb stop
$ adb sync
@@ -48,75 +42,67 @@
$ adb push <source> <destination>
$ adb reboot
-Note that the sequence above:
+Note that you can replace these two lines:
$ adb disable-verity
$ adb reboot
-*or*
-
- $ adb remount
-
-can be replaced in both places with:
+with this line:
$ adb remount -R
-which will not reboot if everything is already prepared and ready
-to go.
+**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
-None of this changes if *overlayfs* needs to be engaged.
-The decisions whether to use traditional direct filesystem remount,
-or one wrapped by *overlayfs* is automatically determined based on
-a probe of the filesystem types and space remaining.
+None of this changes if OverlayFS needs to be engaged.
+The decisions whether to use traditional direct file-system remount,
+or one wrapped by OverlayFS is automatically determined based on
+a probe of the file-system types and space remaining.
### Backing Storage
-When *overlayfs* logic is feasible, it will use either the
+When *OverlayFS* logic is feasible, it uses either the
**/cache/overlay/** directory for non-A/B devices, or the
**/mnt/scratch/overlay** directory for A/B devices that have
-access to *Logical Resizable Android Partitions*.
+access to *LRAP*.
+It is also possible for an A/B device to use the system_<other> partition
+for backing storage. eg: if booting off system_a+vendor_a, use system_b.
The backing store is used as soon as possible in the boot
-process and can occur at first stage init, or at the
-mount_all init rc commands.
+process and can occur at first stage init, or when the
+*mount_all* commands are run in init RC scripts.
-This early as possible attachment of *overlayfs* means that
-*sepolicy* or *init* itself can also be pushed and used after
-the exec phases that accompany each stage.
+By attaching OverlayFS early, SEpolicy or init can be pushed and used after the exec phases of each stage.
Caveats
-------
-- Space used in the backing storage is on a file by file basis
- and will require more space than if updated in place. As such
- it is important to be mindful of any wasted space, for instance
- **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
- will have a negative impact on the overall right-sizing of images
- and thus free dynamic partition space.
-- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
- with "*overlayfs: override_creds=off option bypass creator_cred*"
- if kernel is 4.4 or higher.
+- Backing storage requires more space than immutable storage, as backing is
+ done file by file. Be mindful of wasted space. For example, defining
+ **BOARD_IMAGE_PARTITION_RESERVED_SIZE** has a negative impact on the
+ right-sizing of images and requires more free dynamic partition space.
+- The kernel requires **CONFIG_OVERLAY_FS=y**. If the kernel version is higher
+ than 4.4, it requires source to be in line with android-common kernels.
The patch series is available on the upstream mailing list and the latest as
- of Jul 24 2019 is https://lore.kernel.org/patchwork/patch/1104577/.
- This patch adds an override_creds _mount_ option to overlayfs that
+ of Sep 5 2019 is https://www.spinics.net/lists/linux-mtd/msg08331.html
+ This patch adds an override_creds _mount_ option to OverlayFS that
permits legacy behavior for systems that do not have overlapping
sepolicy rules, principals of least privilege, which is how Android behaves.
-- *adb enable-verity* will free up overlayfs and as a bonus the
- device will be reverted pristine to before any content was updated.
- Update engine does not take advantage of this, will perform a full OTA.
-- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
- true as adb remount overrides are incompatible with an OTA resources.
+ For 4.19 and higher a rework of the xattr handling to deal with recursion
+ is required. https://patchwork.kernel.org/patch/11117145/ is a start of that
+ adjustment.
+- _adb enable-verity_ frees up OverlayFS and reverts the device to the state
+ prior to content updates. The update engine performs a full OTA.
+- _adb remount_ overrides are incompatible with OTA resources, so the update
+ engine may not run if fs_mgr_overlayfs_is_setup() returns true.
+- If a dynamic partition runs out of space, making a logical partition larger
+ may fail because of the scratch partition. If this happens, clear the scratch
+ storage by running either either _fastboot flashall_ or _adb enable-verity_.
+ Then reinstate the overrides and continue.
- For implementation simplicity on retrofit dynamic partition devices,
take the whole alternate super (eg: if "*a*" slot, then the whole of
"*system_b*").
Since landing a filesystem on the alternate super physical device
without differentiating if it is setup to support logical or physical,
the alternate slot metadata and previous content will be lost.
-- If dynamic partitions runs out of space, resizing a logical
- partition larger may fail because of the scratch partition.
- If this happens, either fastboot flashall or adb enable-verity can
- be used to clear scratch storage to permit the flash.
- Then reinstate the overrides and continue.
-- File bugs or submit fixes for review.
- There are other subtle caveats requiring complex logic to solve.
Have evaluated them as too complex or not worth the trouble, please
File a bug if a use case needs to be covered.
@@ -125,7 +111,7 @@
out and we reserve the right to not inform, if the layering
does not prevent any messaging.
- Space remaining threshold is hard coded. If 1% or more space
- still remains, overlayfs will not be used, yet that amount of
+ still remains, OverlayFS will not be used, yet that amount of
space remaining is problematic.
- Flashing a partition via bootloader fastboot, as opposed to user
space fastbootd, is not detected, thus a partition may have
@@ -139,3 +125,4 @@
to confusion. When debugging using **adb remount** it is
currently advised to confirm update is present after a reboot
to develop confidence.
+- File bugs or submit fixes for review.
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index bc197cd..4dbacd7 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -706,10 +706,12 @@
// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
-// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
-// GSI will be used.
+// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
+// only common files for all targets can be put into system partition. It is under
+// /system/system_ext because GSI is a single system.img that includes the contents of system_ext
+// partition and product partition under /system/system_ext and /system/product, respectively.
bool SkipMountingPartitions(Fstab* fstab) {
- constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+ constexpr const char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
std::string skip_config;
auto save_errno = errno;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index a54db58..8df9c52 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -49,6 +49,7 @@
name: "libsnapshot_sources",
srcs: [
"snapshot.cpp",
+ "utility.cpp",
],
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 6f0e804..c41a951 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -211,25 +211,44 @@
std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
bool Truncate(LockedFile* file);
+ enum class SnapshotState : int { None, Created, Merging, MergeCompleted };
+ static std::string to_string(SnapshotState state);
+
+ // This state is persisted per-snapshot in /metadata/ota/snapshots/.
+ struct SnapshotStatus {
+ SnapshotState state = SnapshotState::None;
+ uint64_t device_size = 0;
+ uint64_t snapshot_size = 0;
+ uint64_t cow_partition_size = 0;
+ uint64_t cow_file_size = 0;
+
+ // These are non-zero when merging.
+ uint64_t sectors_allocated = 0;
+ uint64_t metadata_sectors = 0;
+ };
+
// Create a new snapshot record. This creates the backing COW store and
// persists information needed to map the device. The device can be mapped
// with MapSnapshot().
//
- // |device_size| should be the size of the base_device that will be passed
- // via MapDevice(). |snapshot_size| should be the number of bytes in the
- // base device, starting from 0, that will be snapshotted. The cow_size
+ // |status|.device_size should be the size of the base_device that will be passed
+ // via MapDevice(). |status|.snapshot_size should be the number of bytes in the
+ // base device, starting from 0, that will be snapshotted. |status|.cow_file_size
// should be the amount of space that will be allocated to store snapshot
// deltas.
//
- // If |snapshot_size| < device_size, then the device will always
+ // If |status|.snapshot_size < |status|.device_size, then the device will always
// be mapped with two table entries: a dm-snapshot range covering
// snapshot_size, and a dm-linear range covering the remainder.
//
- // All sizes are specified in bytes, and the device and snapshot sizes
- // must be a multiple of the sector size (512 bytes). |cow_size| will
- // be rounded up to the nearest sector.
- bool CreateSnapshot(LockedFile* lock, const std::string& name, uint64_t device_size,
- uint64_t snapshot_size, uint64_t cow_size);
+ // All sizes are specified in bytes, and the device, snapshot and COW partition sizes
+ // must be a multiple of the sector size (512 bytes). COW file size will be rounded up
+ // to the nearest sector.
+ bool CreateSnapshot(LockedFile* lock, const std::string& name, SnapshotStatus status);
+
+ // |name| should be the base partition name (e.g. "system_a"). Create the
+ // backing COW image using the size previously passed to CreateSnapshot().
+ bool CreateCowImage(LockedFile* lock, const std::string& name);
// Map a snapshot device that was previously created with CreateSnapshot.
// If a merge was previously initiated, the device-mapper table will have a
@@ -239,15 +258,23 @@
// timeout_ms is 0, then no wait will occur and |dev_path| may not yet
// exist on return.
bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,
- const std::chrono::milliseconds& timeout_ms, std::string* dev_path);
+ const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
+ std::string* dev_path);
- // Remove the backing copy-on-write image for the named snapshot. The
+ // Map a COW image that was previous created with CreateCowImage.
+ bool MapCowImage(const std::string& name, const std::chrono::milliseconds& timeout_ms,
+ std::string* cow_image_device);
+
+ // Remove the backing copy-on-write image and snapshot states for the named snapshot. The
// caller is responsible for ensuring that the snapshot is unmapped.
bool DeleteSnapshot(LockedFile* lock, const std::string& name);
// Unmap a snapshot device previously mapped with MapSnapshotDevice().
bool UnmapSnapshot(LockedFile* lock, const std::string& name);
+ // Unmap a COW image device previously mapped with MapCowImage().
+ bool UnmapCowImage(const std::string& name);
+
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
@@ -270,22 +297,6 @@
bool WriteUpdateState(LockedFile* file, UpdateState state);
std::string GetStateFilePath() const;
- enum class SnapshotState : int { Created, Merging, MergeCompleted };
- static std::string to_string(SnapshotState state);
-
- // This state is persisted per-snapshot in /metadata/ota/snapshots/.
- struct SnapshotStatus {
- SnapshotState state;
- uint64_t device_size;
- uint64_t snapshot_size;
- uint64_t cow_partition_size;
- uint64_t cow_file_size;
-
- // These are non-zero when merging.
- uint64_t sectors_allocated = 0;
- uint64_t metadata_sectors = 0;
- };
-
// Helpers for merging.
bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
bool RewriteSnapshotDeviceTable(const std::string& dm_name);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 7e57421..f00129a 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -36,6 +36,8 @@
#include <libfiemap/image_manager.h>
#include <liblp/liblp.h>
+#include "utility.h"
+
namespace android {
namespace snapshot {
@@ -54,6 +56,7 @@
using android::fs_mgr::GetPartitionName;
using android::fs_mgr::LpMetadata;
using android::fs_mgr::SlotNumberForSlotSuffix;
+using std::chrono::duration_cast;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -99,10 +102,14 @@
metadata_dir_ = device_->GetMetadataDir();
}
-static std::string GetCowName(const std::string& snapshot_name) {
+[[maybe_unused]] static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
}
+static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
+ return snapshot_name + "-cow-img";
+}
+
static std::string GetBaseDeviceName(const std::string& partition_name) {
return partition_name + "-base";
}
@@ -177,49 +184,60 @@
}
bool SnapshotManager::CreateSnapshot(LockedFile* lock, const std::string& name,
- uint64_t device_size, uint64_t snapshot_size,
- uint64_t cow_size) {
+ SnapshotManager::SnapshotStatus status) {
CHECK(lock);
- if (!EnsureImageManager()) return false;
-
+ CHECK(lock->lock_mode() == LOCK_EX);
// Sanity check these sizes. Like liblp, we guarantee the partition size
// is respected, which means it has to be sector-aligned. (This guarantee
// is useful for locating avb footers correctly). The COW size, however,
// can be arbitrarily larger than specified, so we can safely round it up.
- if (device_size % kSectorSize != 0) {
+ if (status.device_size % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name
- << " device size is not a multiple of the sector size: " << device_size;
+ << " device size is not a multiple of the sector size: " << status.device_size;
return false;
}
- if (snapshot_size % kSectorSize != 0) {
- LOG(ERROR) << "Snapshot " << name
- << " snapshot size is not a multiple of the sector size: " << snapshot_size;
+ if (status.snapshot_size % kSectorSize != 0) {
+ LOG(ERROR) << "Snapshot " << name << " snapshot size is not a multiple of the sector size: "
+ << status.snapshot_size;
return false;
}
// Round the COW size up to the nearest sector.
- cow_size += kSectorSize - 1;
- cow_size &= ~(kSectorSize - 1);
+ status.cow_file_size += kSectorSize - 1;
+ status.cow_file_size &= ~(kSectorSize - 1);
- LOG(INFO) << "Snapshot " << name << " will have COW size " << cow_size;
+ status.state = SnapshotState::Created;
+ status.sectors_allocated = 0;
+ status.metadata_sectors = 0;
- // Note, we leave the status file hanging around if we fail to create the
- // actual backing image. This is harmless, since it'll get removed when
- // CancelUpdate is called.
- SnapshotStatus status = {
- .state = SnapshotState::Created,
- .device_size = device_size,
- .snapshot_size = snapshot_size,
- .cow_file_size = cow_size,
- };
if (!WriteSnapshotStatus(lock, name, status)) {
PLOG(ERROR) << "Could not write snapshot status: " << name;
return false;
}
+ return true;
+}
- auto cow_name = GetCowName(name);
+bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
+ CHECK(lock);
+ CHECK(lock->lock_mode() == LOCK_EX);
+ if (!EnsureImageManager()) return false;
+
+ SnapshotStatus status;
+ if (!ReadSnapshotStatus(lock, name, &status)) {
+ return false;
+ }
+
+ // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
+ // Sanity check this.
+ if (status.cow_file_size % kSectorSize != 0) {
+ LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
+ << status.cow_file_size;
+ return false;
+ }
+
+ std::string cow_image_name = GetCowImageDeviceName(name);
int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
- if (!images_->CreateBackingImage(cow_name, cow_size, cow_flags)) {
+ if (!images_->CreateBackingImage(cow_image_name, status.cow_file_size, cow_flags)) {
return false;
}
@@ -238,11 +256,11 @@
// workaround that will be discussed again when the kernel API gets
// consolidated.
ssize_t dm_snap_magic_size = 4; // 32 bit
- return images_->ZeroFillNewImage(cow_name, dm_snap_magic_size);
+ return images_->ZeroFillNewImage(cow_image_name, dm_snap_magic_size);
}
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
- const std::string& base_device,
+ const std::string& base_device, const std::string& cow_device,
const std::chrono::milliseconds& timeout_ms,
std::string* dev_path) {
CHECK(lock);
@@ -288,22 +306,7 @@
uint64_t snapshot_sectors = status.snapshot_size / kSectorSize;
uint64_t linear_sectors = (status.device_size - status.snapshot_size) / kSectorSize;
- auto cow_name = GetCowName(name);
- bool ok;
- std::string cow_dev;
- if (has_local_image_manager_) {
- // If we forced a local image manager, it means we don't have binder,
- // which means first-stage init. We must use device-mapper.
- const auto& opener = device_->GetPartitionOpener();
- ok = images_->MapImageWithDeviceMapper(opener, cow_name, &cow_dev);
- } else {
- ok = images_->MapImageDevice(cow_name, timeout_ms, &cow_dev);
- }
- if (!ok) {
- LOG(ERROR) << "Could not map image device: " << cow_name;
- return false;
- }
auto& dm = DeviceMapper::Instance();
@@ -335,11 +338,10 @@
auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
DmTable table;
- table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_dev, mode,
+ table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
kSnapshotChunkSize);
if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
LOG(ERROR) << "Could not create snapshot device: " << snap_name;
- images_->UnmapImageDevice(cow_name);
return false;
}
@@ -355,7 +357,6 @@
if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
LOG(ERROR) << "Could not create outer snapshot device: " << name;
dm.DeleteDevice(snap_name);
- images_->UnmapImageDevice(cow_name);
return false;
}
}
@@ -366,9 +367,29 @@
return true;
}
+bool SnapshotManager::MapCowImage(const std::string& name,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* cow_dev) {
+ if (!EnsureImageManager()) return false;
+ auto cow_image_name = GetCowImageDeviceName(name);
+
+ bool ok;
+ if (has_local_image_manager_) {
+ // If we forced a local image manager, it means we don't have binder,
+ // which means first-stage init. We must use device-mapper.
+ const auto& opener = device_->GetPartitionOpener();
+ ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, cow_dev);
+ } else {
+ ok = images_->MapImageDevice(cow_image_name, timeout_ms, cow_dev);
+ }
+ if (!ok) {
+ LOG(ERROR) << "Could not map image device: " << cow_image_name;
+ }
+ return ok;
+}
+
bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
CHECK(lock);
- if (!EnsureImageManager()) return false;
SnapshotStatus status;
if (!ReadSnapshotStatus(lock, name, &status)) {
@@ -389,23 +410,25 @@
return false;
}
- auto cow_name = GetCowName(name);
- if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
- return false;
- }
return true;
}
+bool SnapshotManager::UnmapCowImage(const std::string& name) {
+ if (!EnsureImageManager()) return false;
+ return images_->UnmapImageIfExists(GetCowImageDeviceName(name));
+}
+
bool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name) {
CHECK(lock);
+ CHECK(lock->lock_mode() == LOCK_EX);
if (!EnsureImageManager()) return false;
- auto cow_name = GetCowName(name);
- if (images_->BackingImageExists(cow_name)) {
- if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
+ auto cow_image_name = GetCowImageDeviceName(name);
+ if (images_->BackingImageExists(cow_image_name)) {
+ if (!images_->UnmapImageIfExists(cow_image_name)) {
return false;
}
- if (!images_->DeleteBackingImage(cow_name)) {
+ if (!images_->DeleteBackingImage(cow_image_name)) {
return false;
}
}
@@ -1153,9 +1176,28 @@
return true;
}
+static std::chrono::milliseconds GetRemainingTime(
+ const std::chrono::milliseconds& timeout,
+ const std::chrono::time_point<std::chrono::steady_clock>& begin) {
+ // If no timeout is specified, execute all commands without specifying any timeout.
+ if (timeout.count() == 0) return std::chrono::milliseconds(0);
+ auto passed_time = std::chrono::steady_clock::now() - begin;
+ auto remaining_time = timeout - duration_cast<std::chrono::milliseconds>(passed_time);
+ if (remaining_time.count() <= 0) {
+ LOG(ERROR) << "MapPartitionWithSnapshot has reached timeout " << timeout.count() << "ms ("
+ << remaining_time.count() << "ms remaining)";
+ // Return min() instead of remaining_time here because 0 is treated as a special value for
+ // no timeout, where the rest of the commands will still be executed.
+ return std::chrono::milliseconds::min();
+ }
+ return remaining_time;
+}
+
bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
CreateLogicalPartitionParams params,
std::string* path) {
+ auto begin = std::chrono::steady_clock::now();
+
CHECK(lock);
path->clear();
@@ -1207,6 +1249,11 @@
params.device_name = GetBaseDeviceName(params.GetPartitionName());
}
+ AutoDeviceList created_devices;
+
+ // Create the base device for the snapshot, or if there is no snapshot, the
+ // device itself. This device consists of the real blocks in the super
+ // partition that this logical partition occupies.
auto& dm = DeviceMapper::Instance();
std::string ignore_path;
if (!CreateLogicalPartition(params, &ignore_path)) {
@@ -1214,7 +1261,10 @@
<< " as device " << params.GetDeviceName();
return false;
}
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
+
if (!live_snapshot_status.has_value()) {
+ created_devices.Release();
return true;
}
@@ -1225,10 +1275,35 @@
LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
return false;
}
- if (!MapSnapshot(lock, params.GetPartitionName(), base_device, {}, path)) {
+
+ // If there is a timeout specified, compute the remaining time to call Map* functions.
+ // init calls CreateLogicalAndSnapshotPartitions, which has no timeout specified. Still call
+ // Map* functions in this case.
+ auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
+ std::string cow_image_device;
+ if (!MapCowImage(params.GetPartitionName(), remaining_time, &cow_image_device)) {
+ LOG(ERROR) << "Could not map cow image for partition: " << params.GetPartitionName();
+ return false;
+ }
+ created_devices.EmplaceBack<AutoUnmapImage>(images_.get(),
+ GetCowImageDeviceName(params.partition_name));
+
+ // TODO: map cow linear device here
+ std::string cow_device = cow_image_device;
+
+ remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
+ if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
+ path)) {
LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
return false;
}
+ // No need to add params.GetPartitionName() to created_devices since it is immediately released.
+
+ created_devices.Release();
LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
@@ -1373,7 +1448,9 @@
return false;
}
- if (pieces[0] == "created") {
+ if (pieces[0] == "none") {
+ status->state = SnapshotState::None;
+ } else if (pieces[0] == "created") {
status->state = SnapshotState::Created;
} else if (pieces[0] == "merging") {
status->state = SnapshotState::Merging;
@@ -1381,6 +1458,7 @@
status->state = SnapshotState::MergeCompleted;
} else {
LOG(ERROR) << "Unrecognized state " << pieces[0] << " for snapshot: " << name;
+ return false;
}
if (!android::base::ParseUint(pieces[1], &status->device_size)) {
@@ -1412,6 +1490,8 @@
std::string SnapshotManager::to_string(SnapshotState state) {
switch (state) {
+ case SnapshotState::None:
+ return "none";
case SnapshotState::Created:
return "created";
case SnapshotState::Merging:
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 8487339..429fd8e 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -106,7 +106,7 @@
"test_partition_b"};
for (const auto& snapshot : snapshots) {
DeleteSnapshotDevice(snapshot);
- DeleteBackingImage(image_manager_, snapshot + "-cow");
+ DeleteBackingImage(image_manager_, snapshot + "-cow-img");
auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
android::base::RemoveFileIfExists(status_file);
@@ -214,6 +214,7 @@
void DeleteSnapshotDevice(const std::string& snapshot) {
DeleteDevice(snapshot);
DeleteDevice(snapshot + "-inner");
+ ASSERT_TRUE(image_manager_->UnmapImageIfExists(snapshot + "-cow-img"));
}
void DeleteDevice(const std::string& device) {
if (dm_.GetState(device) != DmDeviceState::INVALID) {
@@ -231,8 +232,11 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
std::vector<std::string> snapshots;
ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));
@@ -249,6 +253,7 @@
}
ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
+ ASSERT_TRUE(sm->UnmapCowImage("test-snapshot"));
ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), "test-snapshot"));
}
@@ -256,14 +261,21 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
std::string base_device;
ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+ std::string cow_device;
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+
std::string snap_device;
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
}
@@ -272,14 +284,21 @@
static const uint64_t kSnapshotSize = 1024 * 1024;
static const uint64_t kDeviceSize = 1024 * 1024 * 2;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kSnapshotSize,
- kSnapshotSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kSnapshotSize,
+ .cow_file_size = kSnapshotSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
std::string base_device;
ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+ std::string cow_device;
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+
std::string snap_device;
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
}
@@ -317,13 +336,18 @@
static const uint64_t kDeviceSize = 1024 * 1024;
- std::string base_device, snap_device;
+ std::string base_device, cow_device, snap_device;
ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
ASSERT_TRUE(MapUpdatePartitions());
ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
- kDeviceSize));
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+ ASSERT_TRUE(sm->MapCowImage("test_partition_b", 10s, &cow_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
+ &snap_device));
std::string test_string = "This is a test string.";
{
@@ -375,16 +399,21 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
- std::string base_device, snap_device;
+ std::string base_device, cow_device, snap_device;
ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
- ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+ ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
// Keep an open handle to the cow device. This should cause the merge to
// be incomplete.
- auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow", "");
+ auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow-img", "");
unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
@@ -399,12 +428,18 @@
// COW cannot be removed due to open fd, so expect a soft failure.
ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeNeedsReboot);
+ // Release the handle to the COW device to fake a reboot.
+ fd.reset();
+ // Wait 1s, otherwise DeleteSnapshotDevice may fail with EBUSY.
+ sleep(1);
// Forcefully delete the snapshot device, so it looks like we just rebooted.
DeleteSnapshotDevice("test-snapshot");
// Map snapshot should fail now, because we're in a merge-complete state.
ASSERT_TRUE(AcquireLock());
- ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+ ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+ ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
+ &snap_device));
// Release everything and now the merge should complete.
fd = {};
@@ -423,8 +458,11 @@
ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
ASSERT_TRUE(MapUpdatePartitions());
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
// Simulate a reboot into the new slot.
lock_ = nullptr;
@@ -462,8 +500,11 @@
ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
ASSERT_TRUE(MapUpdatePartitions());
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
// Simulate a reboot into the new slot.
lock_ = nullptr;
@@ -507,8 +548,11 @@
ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
ASSERT_TRUE(MapUpdatePartitions());
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
- kDeviceSize));
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
+ {.device_size = kDeviceSize,
+ .snapshot_size = kDeviceSize,
+ .cow_file_size = kDeviceSize}));
+ ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
// Simulate a reboot into the new slot.
lock_ = nullptr;
@@ -527,7 +571,7 @@
// Now, reflash super. Note that we haven't called ProcessUpdateState, so the
// status is still Merging.
DeleteSnapshotDevice("test_partition_b");
- ASSERT_TRUE(init->image_manager()->UnmapImageDevice("test_partition_b-cow"));
+ ASSERT_TRUE(init->image_manager()->UnmapImageIfExists("test_partition_b-cow-img"));
FormatFakeSuper();
ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
new file mode 100644
index 0000000..164b472
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -0,0 +1,56 @@
+// 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.
+
+#include "utility.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+void AutoDevice::Release() {
+ name_.clear();
+}
+
+AutoDeviceList::~AutoDeviceList() {
+ // Destroy devices in the reverse order because newer devices may have dependencies
+ // on older devices.
+ for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {
+ it->reset();
+ }
+}
+
+void AutoDeviceList::Release() {
+ for (auto&& p : devices_) {
+ p->Release();
+ }
+}
+
+AutoUnmapDevice::~AutoUnmapDevice() {
+ if (name_.empty()) return;
+ if (!dm_->DeleteDeviceIfExists(name_)) {
+ LOG(ERROR) << "Failed to auto unmap device " << name_;
+ }
+}
+
+AutoUnmapImage::~AutoUnmapImage() {
+ if (name_.empty()) return;
+ if (!images_->UnmapImageIfExists(name_)) {
+ LOG(ERROR) << "Failed to auto unmap cow image " << name_;
+ }
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
new file mode 100644
index 0000000..cbab472
--- /dev/null
+++ b/fs_mgr/libsnapshot/utility.h
@@ -0,0 +1,85 @@
+// 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.
+
+#pragma once
+
+#include <string>
+
+#include <android-base/macros.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+
+namespace android {
+namespace snapshot {
+
+struct AutoDevice {
+ virtual ~AutoDevice(){};
+ void Release();
+
+ protected:
+ AutoDevice(const std::string& name) : name_(name) {}
+ std::string name_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoDevice);
+ AutoDevice(AutoDevice&& other) = delete;
+};
+
+// A list of devices we created along the way.
+// - Whenever a device is created that is subject to GC'ed at the end of
+// this function, add it to this list.
+// - If any error has occurred, the list is destroyed, and all these devices
+// are cleaned up.
+// - Upon success, Release() should be called so that the created devices
+// are kept.
+struct AutoDeviceList {
+ ~AutoDeviceList();
+ template <typename T, typename... Args>
+ void EmplaceBack(Args&&... args) {
+ devices_.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
+ }
+ void Release();
+
+ private:
+ std::vector<std::unique_ptr<AutoDevice>> devices_;
+};
+
+// Automatically unmap a device upon deletion.
+struct AutoUnmapDevice : AutoDevice {
+ // On destruct, delete |name| from device mapper.
+ AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
+ : AutoDevice(name), dm_(dm) {}
+ AutoUnmapDevice(AutoUnmapDevice&& other) = default;
+ ~AutoUnmapDevice();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
+ android::dm::DeviceMapper* dm_ = nullptr;
+};
+
+// Automatically unmap an image upon deletion.
+struct AutoUnmapImage : AutoDevice {
+ // On destruct, delete |name| from image manager.
+ AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
+ : AutoDevice(name), images_(images) {}
+ AutoUnmapImage(AutoUnmapImage&& other) = default;
+ ~AutoUnmapImage();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutoUnmapImage);
+ android::fiemap::IImageManager* images_ = nullptr;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 778e08c..27a6452 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -38,8 +38,6 @@
"libkeystore_aidl",
"libkeystore_binder",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"android.hardware.gatekeeper@1.0",
"libgatekeeper_aidl",
],
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 53be526..4f89bfb 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -42,8 +42,6 @@
"libbase",
"libcutils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"liblog",
"libutils",
"android.hardware.health@2.0",
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 4e34759..66ff399 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -21,7 +21,6 @@
android.hardware.health@1.0-convert \
libbinderthreadstate \
libcharger_sysprop \
- libhidltransport \
libhidlbase \
libhealthstoragedefault \
libminui \
@@ -74,7 +73,6 @@
android.hardware.health@1.0-convert \
libbinderthreadstate \
libcharger_sysprop \
- libhidltransport \
libhidlbase \
libhealthstoragedefault \
libvndksupport \
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index 0f5a864..9c2ca75 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -175,7 +175,6 @@
return -1;
}
- LOG(INFO) << "Setting policy on " << dir;
int result =
fscrypt_policy_ensure(dir.c_str(), policy.c_str(), policy.length(), modes[0].c_str(),
modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts");
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 4e4554a..c7dff5a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -15,8 +15,6 @@
"liblog",
"libbase",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"libutils",
"android.hardware.memtrack@1.0",
],
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 8be4dd0..98921be 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,6 +205,7 @@
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
"String8_test.cpp",
+ "String16_test.cpp",
"StrongPointer_test.cpp",
"Unicode_test.cpp",
"Vector_test.cpp",
@@ -289,3 +290,9 @@
],
shared_libs: ["libutils_test_singleton1"],
}
+
+cc_benchmark {
+ name: "libutils_benchmark",
+ srcs: ["Vector_benchmark.cpp"],
+ shared_libs: ["libutils"],
+}
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 7910c6e..3e703db 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -41,6 +41,7 @@
// The following is OK on Android-supported platforms.
sb->mRefs.store(1, std::memory_order_relaxed);
sb->mSize = size;
+ sb->mClientMetadata = 0;
}
return sb;
}
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index fdf13a9..476c842 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -102,7 +102,12 @@
// Must be sized to preserve correct alignment.
mutable std::atomic<int32_t> mRefs;
size_t mSize;
- uint32_t mReserved[2];
+ uint32_t mReserved;
+public:
+ // mClientMetadata is reserved for client use. It is initialized to 0
+ // and the clients can do whatever they want with it. Note that this is
+ // placed last so that it is adjcent to the buffer allocated.
+ uint32_t mClientMetadata;
};
static_assert(sizeof(SharedBuffer) % 8 == 0
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 818b171..5c3cf32 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,21 +24,21 @@
namespace android {
+static const StaticString16 emptyString(u"");
static inline char16_t* getEmptyString() {
- static SharedBuffer* gEmptyStringBuf = [] {
- SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
- char16_t* str = static_cast<char16_t*>(buf->data());
- *str = 0;
- return buf;
- }();
-
- gEmptyStringBuf->acquire();
- return static_cast<char16_t*>(gEmptyStringBuf->data());
+ return const_cast<char16_t*>(emptyString.string());
}
// ---------------------------------------------------------------------------
-static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
+void* String16::alloc(size_t size)
+{
+ SharedBuffer* buf = SharedBuffer::alloc(size);
+ buf->mClientMetadata = kIsSharedBufferAllocated;
+ return buf;
+}
+
+char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len)
{
if (u8len == 0) return getEmptyString();
@@ -49,7 +49,7 @@
return getEmptyString();
}
- SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t) * (u16len + 1)));
if (buf) {
u8cur = (const uint8_t*) u8str;
char16_t* u16str = (char16_t*)buf->data();
@@ -66,13 +66,13 @@
return getEmptyString();
}
-static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
+char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) {
if (u16len >= SIZE_MAX / sizeof(char16_t)) {
android_errorWriteLog(0x534e4554, "73826242");
abort();
}
- SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc((u16len + 1) * sizeof(char16_t)));
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (buf) {
char16_t* str = (char16_t*)buf->data();
@@ -97,8 +97,8 @@
// having run. In this case we always allocate an empty string. It's less
// efficient than using getEmptyString(), but we assume it's uncommon.
- char16_t* data = static_cast<char16_t*>(
- SharedBuffer::alloc(sizeof(char16_t))->data());
+ SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t)));
+ char16_t* data = static_cast<char16_t*>(buf->data());
data[0] = 0;
mString = data;
}
@@ -106,7 +106,7 @@
String16::String16(const String16& o)
: mString(o.mString)
{
- SharedBuffer::bufferFromData(mString)->acquire();
+ acquire();
}
String16::String16(const String16& o, size_t len, size_t begin)
@@ -136,26 +136,30 @@
String16::~String16()
{
- SharedBuffer::bufferFromData(mString)->release();
+ release();
}
size_t String16::size() const
{
- return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+ if (isStaticString()) {
+ return staticStringSize();
+ } else {
+ return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;
+ }
}
void String16::setTo(const String16& other)
{
- SharedBuffer::bufferFromData(other.mString)->acquire();
- SharedBuffer::bufferFromData(mString)->release();
+ release();
mString = other.mString;
+ acquire();
}
status_t String16::setTo(const String16& other, size_t len, size_t begin)
{
const size_t N = other.size();
if (begin >= N) {
- SharedBuffer::bufferFromData(mString)->release();
+ release();
mString = getEmptyString();
return OK;
}
@@ -184,8 +188,7 @@
abort();
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((len+1)*sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
memmove(str, other, len*sizeof(char16_t));
@@ -212,8 +215,8 @@
abort();
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
@@ -238,8 +241,8 @@
abort();
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+otherLen+1)*sizeof(char16_t));
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
@@ -273,8 +276,8 @@
len, myLen, String8(chrs, len).string());
#endif
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+len+1)*sizeof(char16_t));
+ SharedBuffer* buf =
+ static_cast<SharedBuffer*>(editResize((myLen + len + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
if (pos < myLen) {
@@ -338,23 +341,87 @@
return strstr16(mString, chrs) != nullptr;
}
+void* String16::edit() {
+ SharedBuffer* buf;
+ if (isStaticString()) {
+ buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
+ if (buf) {
+ buf->acquire();
+ memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
+ }
+ } else {
+ buf = SharedBuffer::bufferFromData(mString)->edit();
+ buf->mClientMetadata = kIsSharedBufferAllocated;
+ }
+ return buf;
+}
+
+void* String16::editResize(size_t newSize) {
+ SharedBuffer* buf;
+ if (isStaticString()) {
+ size_t copySize = (size() + 1) * sizeof(char16_t);
+ if (newSize < copySize) {
+ copySize = newSize;
+ }
+ buf = static_cast<SharedBuffer*>(alloc(newSize));
+ if (buf) {
+ buf->acquire();
+ memcpy(buf->data(), mString, copySize);
+ }
+ } else {
+ buf = SharedBuffer::bufferFromData(mString)->editResize(newSize);
+ buf->mClientMetadata = kIsSharedBufferAllocated;
+ }
+ return buf;
+}
+
+void String16::acquire()
+{
+ if (!isStaticString()) {
+ SharedBuffer::bufferFromData(mString)->acquire();
+ }
+}
+
+void String16::release()
+{
+ if (!isStaticString()) {
+ SharedBuffer::bufferFromData(mString)->release();
+ }
+}
+
+bool String16::isStaticString() const {
+ // See String16.h for notes on the memory layout of String16::StaticData and
+ // SharedBuffer.
+ static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
+ const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
+ return (*(p - 1) & kIsSharedBufferAllocated) == 0;
+}
+
+size_t String16::staticStringSize() const {
+ // See String16.h for notes on the memory layout of String16::StaticData and
+ // SharedBuffer.
+ static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
+ const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
+ return static_cast<size_t>(*(p - 1));
+}
+
status_t String16::makeLower()
{
const size_t N = size();
const char16_t* str = string();
- char16_t* edit = nullptr;
+ char16_t* edited = nullptr;
for (size_t i=0; i<N; i++) {
const char16_t v = str[i];
if (v >= 'A' && v <= 'Z') {
- if (!edit) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!edited) {
+ SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
if (!buf) {
return NO_MEMORY;
}
- edit = (char16_t*)buf->data();
- mString = str = edit;
+ edited = (char16_t*)buf->data();
+ mString = str = edited;
}
- edit[i] = tolower((char)v);
+ edited[i] = tolower((char)v);
}
}
return OK;
@@ -364,18 +431,18 @@
{
const size_t N = size();
const char16_t* str = string();
- char16_t* edit = nullptr;
+ char16_t* edited = nullptr;
for (size_t i=0; i<N; i++) {
if (str[i] == replaceThis) {
- if (!edit) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
+ if (!edited) {
+ SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
if (!buf) {
return NO_MEMORY;
}
- edit = (char16_t*)buf->data();
- mString = str = edit;
+ edited = (char16_t*)buf->data();
+ mString = str = edited;
}
- edit[i] = withThis;
+ edited[i] = withThis;
}
}
return OK;
@@ -385,7 +452,7 @@
{
const size_t N = size();
if (begin >= N) {
- SharedBuffer::bufferFromData(mString)->release();
+ release();
mString = getEmptyString();
return OK;
}
@@ -395,8 +462,7 @@
}
if (begin > 0) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((N+1)*sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
if (!buf) {
return NO_MEMORY;
}
@@ -404,8 +470,7 @@
memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
mString = str;
}
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((len+1)*sizeof(char16_t));
+ SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
if (buf) {
char16_t* str = (char16_t*)buf->data();
str[len] = 0;
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
new file mode 100644
index 0000000..f1f24c3
--- /dev/null
+++ b/libutils/String16_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {
+ if (strcmp16(a, b) != 0) {
+ return ::testing::AssertionFailure()
+ << "\"" << String8(a).c_str() << "\" not equal to \"" << String8(b).c_str() << "\"";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+#define EXPECT_STR16EQ(a, b) EXPECT_TRUE(Char16_tStringEquals(a, b))
+
+TEST(String16Test, FromChar16_t) {
+ String16 tmp(u"Verify me");
+ EXPECT_STR16EQ(u"Verify me", tmp);
+}
+
+TEST(String16Test, FromChar16_tSized) {
+ String16 tmp(u"Verify me", 7);
+ EXPECT_STR16EQ(u"Verify ", tmp);
+}
+
+TEST(String16Test, FromChar) {
+ String16 tmp("Verify me");
+ EXPECT_STR16EQ(u"Verify me", tmp);
+}
+
+TEST(String16Test, FromCharSized) {
+ String16 tmp("Verify me", 7);
+ EXPECT_STR16EQ(u"Verify ", tmp);
+}
+
+TEST(String16Test, Copy) {
+ String16 tmp("Verify me");
+ String16 another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, Move) {
+ String16 tmp("Verify me");
+ String16 another(std::move(tmp));
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, Size) {
+ String16 tmp("Verify me");
+ EXPECT_EQ(9U, tmp.size());
+}
+
+TEST(String16Test, setTo) {
+ String16 tmp("Verify me");
+ tmp.setTo(u"New content");
+ EXPECT_EQ(11U, tmp.size());
+ EXPECT_STR16EQ(u"New content", tmp);
+}
+
+TEST(String16Test, Append) {
+ String16 tmp("Verify me");
+ tmp.append(String16("Hello"));
+ EXPECT_EQ(14U, tmp.size());
+ EXPECT_STR16EQ(u"Verify meHello", tmp);
+}
+
+TEST(String16Test, Insert) {
+ String16 tmp("Verify me");
+ tmp.insert(6, u"Insert");
+ EXPECT_EQ(15U, tmp.size());
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+}
+
+TEST(String16Test, Remove) {
+ String16 tmp("Verify me");
+ tmp.remove(2, 6);
+ EXPECT_EQ(2U, tmp.size());
+ EXPECT_STR16EQ(u" m", tmp);
+}
+
+TEST(String16Test, MakeLower) {
+ String16 tmp("Verify Me!");
+ tmp.makeLower();
+ EXPECT_EQ(10U, tmp.size());
+ EXPECT_STR16EQ(u"verify me!", tmp);
+}
+
+TEST(String16Test, ReplaceAll) {
+ String16 tmp("Verify verify Verify");
+ tmp.replaceAll(u'r', u'!');
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+}
+
+TEST(String16Test, Compare) {
+ String16 tmp("Verify me");
+ EXPECT_EQ(String16(u"Verify me"), tmp);
+}
+
+TEST(String16Test, StaticString) {
+ String16 nonStaticString("NonStatic");
+ StaticString16 staticString(u"Static");
+
+ EXPECT_TRUE(staticString.isStaticString());
+ EXPECT_FALSE(nonStaticString.isStaticString());
+}
+
+TEST(String16Test, StaticStringCopy) {
+ StaticString16 tmp(u"Verify me");
+ String16 another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_TRUE(tmp.isStaticString());
+ EXPECT_TRUE(another.isStaticString());
+}
+
+TEST(String16Test, StaticStringMove) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(std::move(tmp));
+ EXPECT_STR16EQ(u"Verify me", another);
+ EXPECT_TRUE(another.isStaticString());
+}
+
+TEST(String16Test, StaticStringSize) {
+ StaticString16 tmp(u"Verify me");
+ EXPECT_EQ(9U, tmp.size());
+}
+
+TEST(String16Test, StaticStringSetTo) {
+ StaticString16 tmp(u"Verify me");
+ tmp.setTo(u"New content");
+ EXPECT_EQ(11U, tmp.size());
+ EXPECT_STR16EQ(u"New content", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringAppend) {
+ StaticString16 tmp(u"Verify me");
+ tmp.append(String16("Hello"));
+ EXPECT_EQ(14U, tmp.size());
+ EXPECT_STR16EQ(u"Verify meHello", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringInsert) {
+ StaticString16 tmp(u"Verify me");
+ tmp.insert(6, u"Insert");
+ EXPECT_EQ(15U, tmp.size());
+ EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringRemove) {
+ StaticString16 tmp(u"Verify me");
+ tmp.remove(2, 6);
+ EXPECT_EQ(2U, tmp.size());
+ EXPECT_STR16EQ(u" m", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringMakeLower) {
+ StaticString16 tmp(u"Verify me!");
+ tmp.makeLower();
+ EXPECT_EQ(10U, tmp.size());
+ EXPECT_STR16EQ(u"verify me!", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringReplaceAll) {
+ StaticString16 tmp(u"Verify verify Verify");
+ tmp.replaceAll(u'r', u'!');
+ EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+ EXPECT_FALSE(tmp.isStaticString());
+}
+
+TEST(String16Test, StaticStringCompare) {
+ StaticString16 tmp(u"Verify me");
+ EXPECT_EQ(String16(u"Verify me"), tmp);
+}
+
+TEST(String16Test, StringSetToStaticString) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(u"nonstatic");
+ another = tmp;
+ EXPECT_STR16EQ(u"Verify me", tmp);
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, StringMoveFromStaticString) {
+ StaticString16 tmp(u"Verify me");
+ String16 another(std::move(tmp));
+ EXPECT_STR16EQ(u"Verify me", another);
+}
+
+TEST(String16Test, EmptyStringIsStatic) {
+ String16 tmp("");
+ EXPECT_TRUE(tmp.isStaticString());
+}
+
+} // namespace android
diff --git a/libutils/Vector_benchmark.cpp b/libutils/Vector_benchmark.cpp
new file mode 100644
index 0000000..c23d499
--- /dev/null
+++ b/libutils/Vector_benchmark.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/benchmark.h>
+#include <utils/Vector.h>
+#include <vector>
+
+void BM_fill_android_vector(benchmark::State& state) {
+ android::Vector<char> v;
+ while (state.KeepRunning()) {
+ v.push('A');
+ }
+}
+BENCHMARK(BM_fill_android_vector);
+
+void BM_fill_std_vector(benchmark::State& state) {
+ std::vector<char> v;
+ while (state.KeepRunning()) {
+ v.push_back('A');
+ }
+}
+BENCHMARK(BM_fill_std_vector);
+
+void BM_prepend_android_vector(benchmark::State& state) {
+ android::Vector<char> v;
+ while (state.KeepRunning()) {
+ v.insertAt('A', 0);
+ }
+}
+BENCHMARK(BM_prepend_android_vector);
+
+void BM_prepend_std_vector(benchmark::State& state) {
+ std::vector<char> v;
+ while (state.KeepRunning()) {
+ v.insert(v.begin(), 'A');
+ }
+}
+BENCHMARK(BM_prepend_std_vector);
+
+BENCHMARK_MAIN();
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index afbc2ed..adc3e7d 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -37,13 +37,17 @@
class String8;
+template <size_t N>
+class StaticString16;
+
// DO NOT USE: please use std::u16string
//! This is a string holding UTF-16 characters.
class String16
{
public:
- /* use String16(StaticLinkage) if you're statically linking against
+ /*
+ * Use String16(StaticLinkage) if you're statically linking against
* libutils and declaring an empty static String16, e.g.:
*
* static String16 sAStaticEmptyString(String16::kEmptyString);
@@ -123,8 +127,76 @@
inline operator const char16_t*() const;
-private:
- const char16_t* mString;
+ // Static and non-static String16 behave the same for the users, so
+ // this method isn't of much use for the users. It is public for testing.
+ bool isStaticString() const;
+
+ private:
+ /*
+ * A flag indicating the type of underlying buffer.
+ */
+ static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;
+
+ /*
+ * alloc() returns void* so that SharedBuffer class is not exposed.
+ */
+ static void* alloc(size_t size);
+ static char16_t* allocFromUTF8(const char* u8str, size_t u8len);
+ static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);
+
+ /*
+ * edit() and editResize() return void* so that SharedBuffer class
+ * is not exposed.
+ */
+ void* edit();
+ void* editResize(size_t new_size);
+
+ void acquire();
+ void release();
+
+ size_t staticStringSize() const;
+
+ const char16_t* mString;
+
+protected:
+ /*
+ * Data structure used to allocate static storage for static String16.
+ *
+ * Note that this data structure and SharedBuffer are used interchangably
+ * as the underlying data structure for a String16. Therefore, the layout
+ * of this data structure must match the part in SharedBuffer that is
+ * visible to String16.
+ */
+ template <size_t N>
+ struct StaticData {
+ // The high bit of 'size' is used as a flag.
+ static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!");
+ constexpr StaticData() : size(N - 1), data{0} {}
+ const uint32_t size;
+ char16_t data[N];
+
+ constexpr StaticData(const StaticData<N>&) = default;
+ };
+
+ /*
+ * Helper function for constructing a StaticData object.
+ */
+ template <size_t N>
+ static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {
+ StaticData<N> r;
+ // The 'size' field is at the same location where mClientMetadata would
+ // be for a SharedBuffer. We do NOT set kIsSharedBufferAllocated flag
+ // here.
+ for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];
+ return r;
+ }
+
+ template <size_t N>
+ explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+public:
+ template <size_t N>
+ explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
};
// String16 can be trivially moved using memcpy() because moving does not
@@ -132,6 +204,42 @@
ANDROID_TRIVIAL_MOVE_TRAIT(String16)
// ---------------------------------------------------------------------------
+
+/*
+ * A StaticString16 object is a specialized String16 object. Instead of holding
+ * the string data in a ref counted SharedBuffer object, it holds data in a
+ * buffer within StaticString16 itself. Note that this buffer is NOT ref
+ * counted and is assumed to be available for as long as there is at least a
+ * String16 object using it. Therefore, one must be extra careful to NEVER
+ * assign a StaticString16 to a String16 that outlives the StaticString16
+ * object.
+ *
+ * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.
+ *
+ * A StaticString16 SHOULD NEVER APPEAR IN APIs. USE String16 INSTEAD.
+ */
+template <size_t N>
+class StaticString16 : public String16 {
+public:
+ constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}
+
+ constexpr StaticString16(const StaticString16<N>& other)
+ : String16(mData), mData(other.mData) {}
+
+ constexpr StaticString16(const StaticString16<N>&&) = delete;
+
+ // There is no reason why one would want to 'new' a StaticString16. Delete
+ // it to discourage misuse.
+ static void* operator new(std::size_t) = delete;
+
+private:
+ const StaticData<N> mData;
+};
+
+template <typename F>
+StaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;
+
+// ---------------------------------------------------------------------------
// No user servicable parts below.
inline int compare_type(const String16& lhs, const String16& rhs)
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 733b60f..cc19481 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,8 +24,6 @@
"libbinder",
"libcutils",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"liblog",
"libprotobuf-cpp-lite",
"libsysutils",
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 1666cfb..e553af1 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -42,7 +42,6 @@
"android.hardware.gatekeeper@1.0",
"libbase",
"libhidlbase",
- "libhidltransport",
"libgatekeeper",
"libutils",
"liblog",
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index d107b78..3a9beaf 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -101,7 +101,6 @@
"libutils",
"libhardware",
"libhidlbase",
- "libhidltransport",
"libtrusty",
"libkeymaster_messages",
"libkeymaster3device",
@@ -132,7 +131,6 @@
"libutils",
"libhardware",
"libhidlbase",
- "libhidltransport",
"libtrusty",
"libkeymaster_messages",
"libkeymaster4",
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 3afa7a9..6a339a1 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -5,7 +5,6 @@
shared_libs: [
"libbase",
"libhidlbase",
- "libhidltransport",
"liblog",
"libutils",
"libhardware",