Merge "Grant adb auth under recovery for unlocked & userdebug devices"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f6ef906..73379cd 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -88,3 +88,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/product_services)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
+$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "charger" -print0 | xargs -0 rm -f)
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 431fea1..c820395 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -154,6 +154,7 @@
// rebooting or after rolling back), or merge the OTA.
bool FinishedSnapshotWrites();
+ private:
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
bool InitiateMerge();
@@ -181,6 +182,15 @@
// GetUpdateState will return None, and a new update can begin.
UpdateState ProcessUpdateState();
+ public:
+ // Initiate the merge if necessary, then wait for the merge to finish.
+ // See InitiateMerge() and ProcessUpdateState() for details.
+ // Returns:
+ // - None if no merge to initiate
+ // - MergeCompleted if merge is completed
+ // - other states indicating an error has occurred
+ UpdateState InitiateMergeAndWait();
+
// Find the status of the current update, if any.
//
// |progress| depends on the returned status:
@@ -238,12 +248,13 @@
FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
FRIEND_TEST(SnapshotTest, MapSnapshot);
FRIEND_TEST(SnapshotTest, Merge);
- FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+ FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
friend class SnapshotTest;
friend class SnapshotUpdateTest;
+ friend class FlashAfterUpdateTest;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
@@ -341,6 +352,9 @@
// condition was detected and handled.
bool HandleCancelledUpdate(LockedFile* lock);
+ // Helper for HandleCancelledUpdate. Assumes booting from new slot.
+ bool HandleCancelledUpdateOnNewSlot(LockedFile* lock);
+
// Remove artifacts created by the update process, such as snapshots, and
// set the update state to None.
bool RemoveAllUpdateState(LockedFile* lock);
@@ -359,7 +373,19 @@
bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
void AcknowledgeMergeSuccess(LockedFile* lock);
void AcknowledgeMergeFailure();
- bool IsCancelledSnapshot(const std::string& snapshot_name);
+ std::unique_ptr<LpMetadata> ReadCurrentMetadata();
+
+ enum class MetadataPartitionState {
+ // Partition does not exist.
+ None,
+ // Partition is flashed.
+ Flashed,
+ // Partition is created by OTA client.
+ Updated,
+ };
+ // Helper function to check the state of a partition as described in metadata.
+ MetadataPartitionState GetMetadataPartitionState(const LpMetadata& metadata,
+ const std::string& name);
// Note that these require the name of the device containing the snapshot,
// which may be the "inner" device. Use GetsnapshotDeviecName().
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 63d97d0..2c516a2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -568,6 +568,27 @@
}
}
+ auto metadata = ReadCurrentMetadata();
+ for (auto it = snapshots.begin(); it != snapshots.end();) {
+ switch (GetMetadataPartitionState(*metadata, *it)) {
+ case MetadataPartitionState::Flashed:
+ LOG(WARNING) << "Detected re-flashing for partition " << *it
+ << ". Skip merging it.";
+ [[fallthrough]];
+ case MetadataPartitionState::None: {
+ LOG(WARNING) << "Deleting snapshot for partition " << *it;
+ if (!DeleteSnapshot(lock.get(), *it)) {
+ LOG(WARNING) << "Cannot delete snapshot for partition " << *it
+ << ". Skip merging it anyways.";
+ }
+ it = snapshots.erase(it);
+ } break;
+ case MetadataPartitionState::Updated: {
+ ++it;
+ } break;
+ }
+ }
+
// Point of no return - mark that we're starting a merge. From now on every
// snapshot must be a merge target.
if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
@@ -855,8 +876,15 @@
std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
+ std::unique_ptr<LpMetadata> current_metadata;
+
if (!IsSnapshotDevice(dm_name)) {
- if (IsCancelledSnapshot(name)) {
+ if (!current_metadata) {
+ current_metadata = ReadCurrentMetadata();
+ }
+
+ if (!current_metadata ||
+ GetMetadataPartitionState(*current_metadata, name) != MetadataPartitionState::Updated) {
DeleteSnapshot(lock, name);
return UpdateState::Cancelled;
}
@@ -877,7 +905,8 @@
}
// This check is expensive so it is only enabled for debugging.
- DCHECK(!IsCancelledSnapshot(name));
+ DCHECK((current_metadata = ReadCurrentMetadata()) &&
+ GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
std::string target_type;
DmTargetSnapshot::Status status;
@@ -1106,13 +1135,17 @@
if (device_->GetSlotSuffix() != old_slot) {
// We're booted into the target slot, which means we just rebooted
// after applying the update.
- return false;
+ if (!HandleCancelledUpdateOnNewSlot(lock)) {
+ return false;
+ }
}
// The only way we can get here is if:
// (1) The device rolled back to the previous slot.
// (2) This function was called prematurely before rebooting the device.
// (3) fastboot set_active was used.
+ // (4) The device updates to the new slot but re-flashed *all* partitions
+ // in the new slot.
//
// In any case, delete the snapshots. It may be worth using the boot_control
// HAL to differentiate case (2).
@@ -1120,18 +1153,66 @@
return true;
}
-bool SnapshotManager::IsCancelledSnapshot(const std::string& snapshot_name) {
+std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
const auto& opener = device_->GetPartitionOpener();
uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto super_device = device_->GetSuperDevice(slot);
auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
if (!metadata) {
LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
- return false;
+ return nullptr;
}
- auto partition = android::fs_mgr::FindPartition(*metadata.get(), snapshot_name);
- if (!partition) return false;
- return (partition->attributes & LP_PARTITION_ATTR_UPDATED) == 0;
+ return metadata;
+}
+
+SnapshotManager::MetadataPartitionState SnapshotManager::GetMetadataPartitionState(
+ const LpMetadata& metadata, const std::string& name) {
+ auto partition = android::fs_mgr::FindPartition(metadata, name);
+ if (!partition) return MetadataPartitionState::None;
+ if (partition->attributes & LP_PARTITION_ATTR_UPDATED) {
+ return MetadataPartitionState::Updated;
+ }
+ return MetadataPartitionState::Flashed;
+}
+
+bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) {
+ std::vector<std::string> snapshots;
+ if (!ListSnapshots(lock, &snapshots)) {
+ LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed "
+ << "after applying an update. Assuming no snapshots.";
+ // Let HandleCancelledUpdate resets UpdateState.
+ return true;
+ }
+
+ // Attempt to detect re-flashing on each partition.
+ // - If all partitions are re-flashed, we can proceed to cancel the whole update.
+ // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are
+ // deleted. Caller is responsible for merging the rest of the snapshots.
+ // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots.
+ auto metadata = ReadCurrentMetadata();
+ if (!metadata) return false;
+ bool all_snapshot_cancelled = true;
+ for (const auto& snapshot_name : snapshots) {
+ if (GetMetadataPartitionState(*metadata, snapshot_name) ==
+ MetadataPartitionState::Updated) {
+ LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name
+ << " is in use.";
+ all_snapshot_cancelled = false;
+ continue;
+ }
+ // Delete snapshots for partitions that are re-flashed after the update.
+ LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << ".";
+ if (!DeleteSnapshot(lock, snapshot_name)) {
+ // This is an error, but it is okay to leave the snapshot in the short term.
+ // However, if all_snapshot_cancelled == false after exiting the loop, caller may
+ // initiate merge for this unused snapshot, which is likely to fail.
+ LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name;
+ }
+ }
+ if (!all_snapshot_cancelled) return false;
+
+ LOG(INFO) << "All partitions are re-flashed after update, removing all update states.";
+ return true;
}
bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
@@ -2089,5 +2170,27 @@
return AutoUnmountDevice::New(device_->GetMetadataDir());
}
+UpdateState SnapshotManager::InitiateMergeAndWait() {
+ LOG(INFO) << "Waiting for any previous merge request to complete. "
+ << "This can take up to several minutes.";
+ auto state = ProcessUpdateState();
+ if (state == UpdateState::None) {
+ LOG(INFO) << "Can't find any snapshot to merge.";
+ return state;
+ }
+ if (state == UpdateState::Unverified) {
+ if (!InitiateMerge()) {
+ LOG(ERROR) << "Failed to initiate merge.";
+ return state;
+ }
+ // All other states can be handled by ProcessUpdateState.
+ LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
+ state = ProcessUpdateState();
+ }
+
+ LOG(INFO) << "Merge finished with state \"" << state << "\".";
+ return state;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 5728582..3c3d9a6 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -23,6 +23,7 @@
#include <iostream>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -56,6 +57,7 @@
using android::fs_mgr::GetPartitionName;
using android::fs_mgr::Interval;
using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::SlotSuffixForSlotNumber;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::DynamicPartitionGroup;
using chromeos_update_engine::PartitionUpdate;
@@ -445,61 +447,6 @@
ASSERT_EQ(test_string, buffer);
}
-TEST_F(SnapshotTest, MergeCannotRemoveCow) {
- ASSERT_TRUE(AcquireLock());
-
- static const uint64_t kDeviceSize = 1024 * 1024;
- SnapshotStatus status;
- status.set_name("test-snapshot");
- status.set_device_size(kDeviceSize);
- status.set_snapshot_size(kDeviceSize);
- status.set_cow_file_size(kDeviceSize);
- ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
- ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
- std::string base_device, cow_device, snap_device;
- ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
- ASSERT_TRUE(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-img", "");
- unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
- ASSERT_GE(fd, 0);
-
- // Release the lock.
- lock_ = nullptr;
-
- ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
- test_device->set_slot_suffix("_b");
- ASSERT_TRUE(sm->InitiateMerge());
-
- // 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.
- ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot"));
-
- // Map snapshot should fail now, because we're in a merge-complete state.
- ASSERT_TRUE(AcquireLock());
- ASSERT_TRUE(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 = {};
- lock_ = nullptr;
-
- ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
-}
-
TEST_F(SnapshotTest, FirstStageMountAndMerge) {
ASSERT_TRUE(AcquireLock());
@@ -680,9 +627,9 @@
// Initialize source partition metadata using |manifest_|.
src_ = MetadataBuilder::New(*opener_, "super", 0);
ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
- ASSERT_NE(nullptr, src_);
// Add sys_b which is like system_other.
- auto partition = src_->AddPartition("sys_b", 0);
+ ASSERT_TRUE(src_->AddGroup("group_b", kGroupSize));
+ auto partition = src_->AddPartition("sys_b", "group_b", 0);
ASSERT_NE(nullptr, partition);
ASSERT_TRUE(src_->ResizePartition(partition, 1_MiB));
auto metadata = src_->Export();
@@ -731,8 +678,12 @@
if (!hash.has_value()) {
return AssertionFailure() << "Cannot read partition " << name << ": " << path;
}
- if (hashes_[name] != *hash) {
- return AssertionFailure() << "Content of " << name << " has changed after the merge";
+ auto it = hashes_.find(name);
+ if (it == hashes_.end()) {
+ return AssertionFailure() << "No existing hash for " << name << ". Bad test code?";
+ }
+ if (it->second != *hash) {
+ return AssertionFailure() << "Content of " << name << " has changed";
}
return AssertionSuccess();
}
@@ -847,8 +798,7 @@
}
// Initiate the merge and wait for it to be completed.
- ASSERT_TRUE(init->InitiateMerge());
- ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+ ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
// Check that the target partitions have the same content after the merge.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1052,8 +1002,7 @@
// Initiate the merge and wait for it to be completed.
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
- ASSERT_TRUE(new_sm->InitiateMerge());
- ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
+ ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait());
// Execute the second update.
ASSERT_TRUE(new_sm->BeginUpdate());
@@ -1162,6 +1111,67 @@
ASSERT_TRUE(sm->FinishedSnapshotWrites());
}
+TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
+ // Make source partitions as big as possible to force COW image to be created.
+ SetSize(sys_, 5_MiB);
+ SetSize(vnd_, 5_MiB);
+ SetSize(prd_, 5_MiB);
+ src_ = MetadataBuilder::New(*opener_, "super", 0);
+ src_->RemoveGroupAndPartitions(group_->name() + "_a");
+ src_->RemoveGroupAndPartitions(group_->name() + "_b");
+ ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
+ auto metadata = src_->Export();
+ ASSERT_NE(nullptr, metadata);
+ ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
+
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ // Add operations for sys. The whole device is written.
+ auto e = sys_->add_operations()->add_dst_extents();
+ e->set_start_block(0);
+ e->set_num_blocks(GetSize(sys_) / manifest_.block_size());
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img"
+ // won't be set.
+ auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+ // 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.sys_b-cow-img", "");
+ unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(fd, 0);
+
+ // COW cannot be removed due to open fd, so expect a soft failure.
+ ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait());
+
+ // Simulate shutting down the device.
+ fd.reset();
+ ASSERT_TRUE(UnmapAll());
+
+ // init does first stage mount again.
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+ // sys_b should be mapped as a dm-linear device directly.
+ ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
+
+ // Merge should be able to complete now.
+ ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
+}
+
class MetadataMountedTest : public SnapshotUpdateTest {
public:
void SetUp() override {
@@ -1220,6 +1230,121 @@
EXPECT_FALSE(IsMetadataMounted());
}
+class FlashAfterUpdateTest : public SnapshotUpdateTest,
+ public WithParamInterface<std::tuple<uint32_t, bool>> {
+ public:
+ AssertionResult InitiateMerge(const std::string& slot_suffix) {
+ auto sm = SnapshotManager::New(new TestDeviceInfo(fake_super, slot_suffix));
+ if (!sm->CreateLogicalAndSnapshotPartitions("super")) {
+ return AssertionFailure() << "Cannot CreateLogicalAndSnapshotPartitions";
+ }
+ if (!sm->InitiateMerge()) {
+ return AssertionFailure() << "Cannot initiate merge";
+ }
+ return AssertionSuccess();
+ }
+};
+
+TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
+ // OTA client blindly unmaps all partitions that are possibly mapped.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
+ }
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ if (std::get<1>(GetParam()) /* merge */) {
+ ASSERT_TRUE(InitiateMerge("_b"));
+ // Simulate shutting down the device after merge has initiated.
+ ASSERT_TRUE(UnmapAll());
+ }
+
+ auto flashed_slot = std::get<0>(GetParam());
+ auto flashed_slot_suffix = SlotSuffixForSlotNumber(flashed_slot);
+
+ // Simulate flashing |flashed_slot|. This clears the UPDATED flag.
+ auto flashed_builder = MetadataBuilder::New(*opener_, "super", flashed_slot);
+ flashed_builder->RemoveGroupAndPartitions(group_->name() + flashed_slot_suffix);
+ flashed_builder->RemoveGroupAndPartitions(kCowGroupName);
+ ASSERT_TRUE(FillFakeMetadata(flashed_builder.get(), manifest_, flashed_slot_suffix));
+
+ // Deliberately remove a partition from this build so that
+ // InitiateMerge do not switch state to "merging". This is possible in
+ // practice because the list of dynamic partitions may change.
+ ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix));
+ flashed_builder->RemovePartition("prd" + flashed_slot_suffix);
+
+ auto flashed_metadata = flashed_builder->Export();
+ ASSERT_NE(nullptr, flashed_metadata);
+ ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot));
+
+ std::string path;
+ for (const auto& name : {"sys", "vnd"}) {
+ ASSERT_TRUE(CreateLogicalPartition(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = flashed_slot,
+ .partition_name = name + flashed_slot_suffix,
+ .timeout_ms = 1s,
+ .partition_opener = opener_.get(),
+ },
+ &path));
+ ASSERT_TRUE(WriteRandomData(path));
+ auto hash = GetHash(path);
+ ASSERT_TRUE(hash.has_value());
+ hashes_[name + flashed_slot_suffix] = *hash;
+ }
+
+ // Simulate shutting down the device after flash.
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate reboot. After reboot, init does first stage mount.
+ auto init = SnapshotManager::NewForFirstStageMount(
+ new TestDeviceInfo(fake_super, flashed_slot_suffix));
+ ASSERT_NE(init, nullptr);
+ if (init->NeedSnapshotsInFirstStageMount()) {
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+ } else {
+ for (const auto& name : {"sys", "vnd"}) {
+ ASSERT_TRUE(CreateLogicalPartition(
+ CreateLogicalPartitionParams{
+ .block_device = fake_super,
+ .metadata_slot = flashed_slot,
+ .partition_name = name + flashed_slot_suffix,
+ .timeout_ms = 1s,
+ .partition_opener = opener_.get(),
+ },
+ &path));
+ }
+ }
+
+ // Check that the target partitions have the same content.
+ for (const auto& name : {"sys", "vnd"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name + flashed_slot_suffix));
+ }
+
+ // There should be no snapshot to merge.
+ auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
+ ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait());
+
+ // Next OTA calls CancelUpdate no matter what.
+ ASSERT_TRUE(new_sm->CancelUpdate());
+}
+
+INSTANTIATE_TEST_SUITE_P(, FlashAfterUpdateTest, Combine(Values(0, 1), Bool()),
+ [](const TestParamInfo<FlashAfterUpdateTest::ParamType>& info) {
+ return "Flash"s + (std::get<0>(info.param) ? "New"s : "Old"s) +
+ "Slot"s + (std::get<1>(info.param) ? "After"s : "Before"s) +
+ "Merge"s;
+ });
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index d65320c..1bc0357 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -60,24 +60,11 @@
android::base::InitLogging(argv, &android::base::StdioLogger);
}
- auto sm = SnapshotManager::New();
+ auto state = SnapshotManager::New()->InitiateMergeAndWait();
- auto state = sm->GetUpdateState();
if (state == UpdateState::None) {
- LOG(INFO) << "Can't find any snapshot to merge.";
return true;
}
- if (state == UpdateState::Unverified) {
- if (!sm->InitiateMerge()) {
- LOG(ERROR) << "Failed to initiate merge.";
- return false;
- }
- }
-
- // All other states can be handled by ProcessUpdateState.
- LOG(INFO) << "Waiting for any merge to complete. This can take up to 1 minute.";
- state = SnapshotManager::New()->ProcessUpdateState();
-
if (state == UpdateState::MergeCompleted) {
auto end = std::chrono::steady_clock::now();
auto passed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
diff --git a/healthd/Android.bp b/healthd/Android.bp
index e04f70f..14d46b3 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -89,6 +89,7 @@
cc_library_static {
name: "libhealthd_charger_nops",
+ recovery_available: true,
srcs: [
"healthd_mode_charger_nops.cpp",
@@ -104,17 +105,19 @@
],
static_libs: [
- "android.hardware.health@2.0-impl",
+ "libhealthloop",
+ "libhealth2impl",
],
shared_libs: [
- "android.hardware.health@2.0",
+ "android.hardware.health@2.1",
"libutils",
],
}
sysprop_library {
name: "charger_sysprop",
+ recovery_available: true,
srcs: ["charger.sysprop"],
property_owner: "Platform",
api_packages: ["android.sysprop"],
@@ -141,16 +144,16 @@
export_include_dirs: [".", "include"],
static_libs: [
- "android.hardware.health@2.0-impl",
"android.hardware.health@1.0-convert",
"libcharger_sysprop",
- "libhealthstoragedefault",
"libhealthd_draw",
+ "libhealthloop",
+ "libhealth2impl",
"libminui",
],
shared_libs: [
- "android.hardware.health@2.0",
+ "android.hardware.health@2.1",
"libbase",
"libcutils",
"liblog",
@@ -164,3 +167,76 @@
"AnimationParser.cpp",
],
}
+
+cc_defaults {
+ name: "charger_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ // common
+ "android.hardware.health@2.0",
+ "android.hardware.health@2.1",
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+
+ // system charger only
+ "libpng",
+ ],
+
+ static_libs: [
+ // common
+ "android.hardware.health@1.0-convert",
+ "libbatterymonitor",
+ "libcharger_sysprop",
+ "libhealthd_charger_nops",
+ "libhealthloop",
+ "libhealth2impl",
+
+ // system charger only
+ "libhealthd_draw",
+ "libhealthd_charger",
+ "libminui",
+ "libsuspend",
+ ],
+}
+
+cc_binary {
+ name: "charger",
+ defaults: ["charger_defaults"],
+ recovery_available: true,
+ srcs: [
+ "charger.cpp",
+ "charger_utils.cpp",
+ ],
+
+ target: {
+ recovery: {
+ // No UI and libsuspend for recovery charger.
+ cflags: [
+ "-DCHARGER_FORCE_NO_UI=1",
+ ],
+ exclude_shared_libs: [
+ "libpng",
+ ],
+ exclude_static_libs: [
+ "libhealthd_draw",
+ "libhealthd_charger",
+ "libminui",
+ "libsuspend",
+ ],
+ }
+ }
+}
+
+cc_test {
+ name: "charger_test",
+ defaults: ["charger_defaults"],
+ srcs: ["charger_test.cpp"],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 66ff399..4b09cf8 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,112 +2,10 @@
LOCAL_PATH := $(call my-dir)
-### charger ###
-include $(CLEAR_VARS)
ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
LOCAL_CHARGER_NO_UI := true
endif
-LOCAL_SRC_FILES := \
- charger.cpp \
-
-LOCAL_MODULE := charger
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS := -Werror
-
-CHARGER_STATIC_LIBRARIES := \
- android.hardware.health@2.0-impl \
- android.hardware.health@1.0-convert \
- libbinderthreadstate \
- libcharger_sysprop \
- libhidlbase \
- libhealthstoragedefault \
- libminui \
- libvndksupport \
- libhealthd_charger \
- libhealthd_charger_nops \
- libhealthd_draw \
- libbatterymonitor \
-
-CHARGER_SHARED_LIBRARIES := \
- android.hardware.health@2.0 \
- libbase \
- libcutils \
- libjsoncpp \
- libpng \
- libprocessgroup \
- liblog \
- libutils \
-
-CHARGER_SHARED_LIBRARIES += libsuspend
-
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
-LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
-
-LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-
-# Symlink /charger to /system/bin/charger
-LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
- && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
-
-include $(BUILD_EXECUTABLE)
-
-### charger.recovery ###
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- charger.cpp \
-
-LOCAL_MODULE := charger.recovery
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
-LOCAL_MODULE_STEM := charger
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_FORCE_NO_UI=1
-
-# charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
-# any UI support.
-LOCAL_STATIC_LIBRARIES := \
- android.hardware.health@2.0-impl \
- android.hardware.health@1.0-convert \
- libbinderthreadstate \
- libcharger_sysprop \
- libhidlbase \
- libhealthstoragedefault \
- libvndksupport \
- libhealthd_charger_nops \
- libbatterymonitor \
-
-# These shared libs will be installed to recovery image because of the dependency in `recovery`
-# module.
-LOCAL_SHARED_LIBRARIES := \
- android.hardware.health@2.0 \
- libbase \
- libcutils \
- liblog \
- libutils \
-
-# The use of LOCAL_HAL_STATIC_LIBRARIES prevents from building this module with Android.bp.
-LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-
-include $(BUILD_EXECUTABLE)
-
-### charger_test ###
-include $(CLEAR_VARS)
-LOCAL_MODULE := charger_test
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
-LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
-LOCAL_SRC_FILES := \
- charger_test.cpp \
-
-include $(BUILD_EXECUTABLE)
-
-CHARGER_STATIC_LIBRARIES :=
-CHARGER_SHARED_LIBRARIES :=
-
### charger_res_images ###
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
diff --git a/healthd/animation.h b/healthd/animation.h
index 9476c91..d02d7a7 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -75,7 +75,7 @@
bool run;
- frame* frames;
+ frame* frames = nullptr;
int cur_frame;
int num_frames;
int first_frame_repeats; // Number of times to repeat the first frame in the current cycle
@@ -85,6 +85,8 @@
int cur_level; // current battery level being animated (0-100)
int cur_status; // current battery status - see BatteryService.h for BATTERY_STATUS_*
+
+ ~animation() { delete frames; }
};
}
diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp
index a7e2161..e0bde68 100644
--- a/healthd/charger_test.cpp
+++ b/healthd/charger_test.cpp
@@ -21,13 +21,22 @@
#include <condition_variable>
#include <fstream>
#include <iostream>
+#include <memory>
#include <mutex>
#include <streambuf>
#include <string>
#include <thread>
#include <vector>
-#include <health2/Health.h>
+#include <health/utils.h>
+#include <health2impl/Health.h>
+
+#include "healthd_mode_charger.h"
+
+using android::hardware::health::InitHealthdConfig;
+using android::hardware::health::V2_1::HealthInfo;
+using android::hardware::health::V2_1::IHealth;
+using android::hardware::health::V2_1::implementation::Health;
#define LOG_THIS(fmt, ...) \
ALOGE(fmt, ##__VA_ARGS__); \
@@ -129,22 +138,23 @@
config->screen_on = NULL;
}
-int healthd_board_battery_update(struct android::BatteryProperties*) {
- getUpdateNotifier().set(true /* updated */);
+class TestHealth : public Health {
+ protected:
+ using Health::Health;
+ void UpdateHealthInfo(HealthInfo*) override { getUpdateNotifier().set(true /* updated */); }
+};
- // return 0 to log periodic polled battery status to kernel log
- return 0;
-}
-
-extern int healthd_charger_main(int argc, char** argv);
-
-int main(int argc, char** argv) {
- using android::hardware::health::V2_0::implementation::Health;
-
+int main(int /*argc*/, char** /*argv*/) {
const char* dumpFile = "/data/local/tmp/dump.txt";
+ auto config = std::make_unique<healthd_config>();
+ InitHealthdConfig(config.get());
+ healthd_board_init(config.get());
+ sp<IHealth> passthrough = new TestHealth(std::move(config));
+
std::thread bgThread([=] {
- healthd_charger_main(argc, argv);
+ android::Charger charger(passthrough);
+ charger.StartLoop();
});
// wait for healthd_init to finish
@@ -153,7 +163,7 @@
exit(1);
}
- Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */);
+ passthrough->debug(createHidlHandle(dumpFile), {} /* options */);
std::string content = openToString(dumpFile);
int status = expectContains(content, {
diff --git a/healthd/charger_utils.cpp b/healthd/charger_utils.cpp
new file mode 100644
index 0000000..0cf9df5
--- /dev/null
+++ b/healthd/charger_utils.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "charger_utils.h"
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <health/utils.h>
+#include <health2impl/Health.h>
+#include <hidl/ServiceManagement.h>
+
+using android::hardware::getPassthroughServiceManager;
+using android::hidl::base::V1_0::IBase;
+using android::hidl::manager::V1_0::IServiceManager;
+
+namespace android {
+namespace hardware {
+namespace health {
+sp<V2_1::IHealth> GetPassthroughHealthImpl() {
+ // Not using getService() because there is no hwservicemanager in charger mode.
+ sp<IServiceManager> pm = getPassthroughServiceManager();
+ if (pm == nullptr) {
+ LOG(WARNING) << "Cannot get passthrough service manager.";
+ return nullptr;
+ }
+ sp<IBase> base = pm->get(V2_0::IHealth::descriptor, "default");
+ if (base == nullptr) {
+ LOG(WARNING) << "Cannot find passthrough implementation of health 2.0 HAL for instance "
+ "'default' on the device.";
+ return nullptr;
+ }
+ sp<V2_1::IHealth> service = V2_1::IHealth::castFrom(base);
+ if (service == nullptr) {
+ LOG(WARNING)
+ << "Cannot cast passthrough implementation of health 2.0 HAL to 2.1 for instance "
+ "'default' on the device.";
+ return nullptr;
+ }
+ return service;
+}
+
+sp<V2_1::IHealth> GetPassthroughHealth() {
+ auto impl = GetPassthroughHealthImpl();
+ if (impl == nullptr) {
+ LOG(WARNING) << "Charger uses system defaults.";
+ auto config = std::make_unique<healthd_config>();
+ InitHealthdConfig(config.get());
+ impl = new V2_1::implementation::Health(std::move(config));
+ }
+ return impl;
+}
+
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/healthd/charger_utils.h b/healthd/charger_utils.h
new file mode 100644
index 0000000..f96e827
--- /dev/null
+++ b/healthd/charger_utils.h
@@ -0,0 +1,27 @@
+/*
+ * 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 <android/hardware/health/2.1/IHealth.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+sp<V2_1::IHealth> GetPassthroughHealth();
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index d676083..7d844c9 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "healthd_mode_charger.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -28,7 +30,7 @@
#include <time.h>
#include <unistd.h>
-#include <functional>
+#include <optional>
#include <android-base/file.h>
#include <android-base/macros.h>
@@ -47,16 +49,30 @@
#include "AnimationParser.h"
#include "charger.sysprop.h"
+#include "charger_utils.h"
#include "healthd_draw.h"
-#include <health2/Health.h>
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <health/utils.h>
+#include <health2impl/HalHealthLoop.h>
+#include <health2impl/Health.h>
#include <healthd/healthd.h>
using namespace android;
+using android::hardware::Return;
+using android::hardware::health::GetPassthroughHealth;
+using android::hardware::health::HealthLoop;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_1::IHealth;
+using IHealth_2_0 = android::hardware::health::V2_0::IHealth;
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
// main healthd loop
extern int healthd_main(void);
+// minui globals
char* locale;
#ifndef max
@@ -85,6 +101,8 @@
#define LOGW(x...) KLOG_WARNING("charger", x);
#define LOGV(x...) KLOG_DEBUG("charger", x);
+namespace android {
+
// Resources in /product/etc/res overrides resources in /res.
// If the device is using the Generic System Image (GSI), resources may exist in
// both paths.
@@ -93,28 +111,6 @@
static constexpr const char* product_animation_root = "/product/etc/res/images/";
static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
-struct key_state {
- bool pending;
- bool down;
- int64_t timestamp;
-};
-
-struct charger {
- bool have_battery_state;
- bool charger_connected;
- bool screen_blanked;
- int64_t next_screen_transition;
- int64_t next_key_check;
- int64_t next_pwr_check;
- int64_t wait_batt_level_timestamp;
-
- key_state keys[KEY_MAX + 1];
-
- animation* batt_anim;
- GRSurface* surf_unknown;
- int boot_min_cap;
-};
-
static const animation BASE_ANIMATION = {
.text_clock =
{
@@ -153,51 +149,51 @@
.cur_status = BATTERY_STATUS_UNKNOWN,
};
-static animation::frame default_animation_frames[] = {
- {
- .disp_time = 750,
- .min_level = 0,
- .max_level = 19,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_level = 0,
- .max_level = 39,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_level = 0,
- .max_level = 59,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_level = 0,
- .max_level = 79,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_level = 80,
- .max_level = 95,
- .surface = NULL,
- },
- {
- .disp_time = 750,
- .min_level = 0,
- .max_level = 100,
- .surface = NULL,
- },
-};
+void Charger::InitDefaultAnimationFrames() {
+ owned_frames_ = {
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 19,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 39,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 59,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 79,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 80,
+ .max_level = 95,
+ .surface = NULL,
+ },
+ {
+ .disp_time = 750,
+ .min_level = 0,
+ .max_level = 100,
+ .surface = NULL,
+ },
+ };
+}
-static animation battery_animation = BASE_ANIMATION;
+Charger::Charger(const sp<IHealth>& service)
+ : HalHealthLoop("charger", service), batt_anim_(BASE_ANIMATION) {}
-static charger charger_state;
-static healthd_config* healthd_config;
-static android::BatteryProperties* batt_prop;
-static std::unique_ptr<HealthdDraw> healthd_draw;
+Charger::~Charger() {}
/* current time in milliseconds */
static int64_t curr_time_ms() {
@@ -284,123 +280,125 @@
anim->run = false;
}
-static void update_screen_state(charger* charger, int64_t now) {
- animation* batt_anim = charger->batt_anim;
+void Charger::UpdateScreenState(int64_t now) {
int disp_time;
- if (!batt_anim->run || now < charger->next_screen_transition) return;
+ if (!batt_anim_.run || now < next_screen_transition_) return;
// If battery level is not ready, keep checking in the defined time
- if (batt_prop == nullptr ||
- (batt_prop->batteryLevel == 0 && batt_prop->batteryStatus == BATTERY_STATUS_UNKNOWN)) {
- if (charger->wait_batt_level_timestamp == 0) {
+ if (health_info_.batteryLevel == 0 && health_info_.batteryStatus == BatteryStatus::UNKNOWN) {
+ if (wait_batt_level_timestamp_ == 0) {
// Set max delay time and skip drawing screen
- charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
+ wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
return;
- } else if (now <= charger->wait_batt_level_timestamp) {
+ } else if (now <= wait_batt_level_timestamp_) {
// Do nothing, keep waiting
return;
}
// If timeout and battery level is still not ready, draw unknown battery
}
- if (healthd_draw == nullptr) {
- if (healthd_config && healthd_config->screen_on) {
- if (!healthd_config->screen_on(batt_prop)) {
+ if (healthd_draw_ == nullptr) {
+ std::optional<bool> out_screen_on;
+ service()->shouldKeepScreenOn([&](Result res, bool screen_on) {
+ if (res == Result::SUCCESS) {
+ *out_screen_on = screen_on;
+ }
+ });
+ if (out_screen_on.has_value()) {
+ if (!*out_screen_on) {
LOGV("[%" PRId64 "] leave screen off\n", now);
- batt_anim->run = false;
- charger->next_screen_transition = -1;
- if (charger->charger_connected) request_suspend(true);
+ batt_anim_.run = false;
+ next_screen_transition_ = -1;
+ if (charger_online()) request_suspend(true);
return;
}
}
- healthd_draw.reset(new HealthdDraw(batt_anim));
+ healthd_draw_.reset(new HealthdDraw(&batt_anim_));
if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
- healthd_draw->blank_screen(true);
- charger->screen_blanked = true;
+ healthd_draw_->blank_screen(true);
+ screen_blanked_ = true;
}
}
/* animation is over, blank screen and leave */
- if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
- reset_animation(batt_anim);
- charger->next_screen_transition = -1;
- healthd_draw->blank_screen(true);
- charger->screen_blanked = true;
+ if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
+ reset_animation(&batt_anim_);
+ next_screen_transition_ = -1;
+ healthd_draw_->blank_screen(true);
+ screen_blanked_ = true;
LOGV("[%" PRId64 "] animation done\n", now);
- if (charger->charger_connected) request_suspend(true);
+ if (charger_online()) request_suspend(true);
return;
}
- disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
+ disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
- if (charger->screen_blanked) {
- healthd_draw->blank_screen(false);
- charger->screen_blanked = false;
+ if (screen_blanked_) {
+ healthd_draw_->blank_screen(false);
+ screen_blanked_ = false;
}
/* animation starting, set up the animation */
- if (batt_anim->cur_frame == 0) {
+ if (batt_anim_.cur_frame == 0) {
LOGV("[%" PRId64 "] animation starting\n", now);
- if (batt_prop) {
- batt_anim->cur_level = batt_prop->batteryLevel;
- batt_anim->cur_status = batt_prop->batteryStatus;
- if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
- /* find first frame given current battery level */
- for (int i = 0; i < batt_anim->num_frames; i++) {
- if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
- batt_anim->cur_level <= batt_anim->frames[i].max_level) {
- batt_anim->cur_frame = i;
- break;
- }
+ batt_anim_.cur_level = health_info_.batteryLevel;
+ batt_anim_.cur_status = (int)health_info_.batteryStatus;
+ if (health_info_.batteryLevel >= 0 && batt_anim_.num_frames != 0) {
+ /* find first frame given current battery level */
+ for (int i = 0; i < batt_anim_.num_frames; i++) {
+ if (batt_anim_.cur_level >= batt_anim_.frames[i].min_level &&
+ batt_anim_.cur_level <= batt_anim_.frames[i].max_level) {
+ batt_anim_.cur_frame = i;
+ break;
}
-
- if (charger->charger_connected) {
- // repeat the first frame first_frame_repeats times
- disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
- batt_anim->first_frame_repeats;
- } else {
- disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim->num_cycles;
- }
-
- LOGV("cur_frame=%d disp_time=%d\n", batt_anim->cur_frame, disp_time);
}
+
+ if (charger_online()) {
+ // repeat the first frame first_frame_repeats times
+ disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time *
+ batt_anim_.first_frame_repeats;
+ } else {
+ disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim_.num_cycles;
+ }
+
+ LOGV("cur_frame=%d disp_time=%d\n", batt_anim_.cur_frame, disp_time);
}
}
/* draw the new frame (@ cur_frame) */
- healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
+ healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);
/* if we don't have anim frames, we only have one image, so just bump
* the cycle counter and exit
*/
- if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
+ if (batt_anim_.num_frames == 0 || batt_anim_.cur_level < 0) {
LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
- charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
- batt_anim->cur_cycle++;
+ next_screen_transition_ = now + BATTERY_UNKNOWN_TIME;
+ batt_anim_.cur_cycle++;
return;
}
/* schedule next screen transition */
- charger->next_screen_transition = curr_time_ms() + disp_time;
+ next_screen_transition_ = curr_time_ms() + disp_time;
/* advance frame cntr to the next valid frame only if we are charging
* if necessary, advance cycle cntr, and reset frame cntr
*/
- if (charger->charger_connected) {
- batt_anim->cur_frame++;
+ if (charger_online()) {
+ batt_anim_.cur_frame++;
- while (batt_anim->cur_frame < batt_anim->num_frames &&
- (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
- batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
- batt_anim->cur_frame++;
+ while (batt_anim_.cur_frame < batt_anim_.num_frames &&
+ (batt_anim_.cur_level < batt_anim_.frames[batt_anim_.cur_frame].min_level ||
+ batt_anim_.cur_level > batt_anim_.frames[batt_anim_.cur_frame].max_level)) {
+ batt_anim_.cur_frame++;
}
- if (batt_anim->cur_frame >= batt_anim->num_frames) {
- batt_anim->cur_cycle++;
- batt_anim->cur_frame = 0;
+ if (batt_anim_.cur_frame >= batt_anim_.num_frames) {
+ batt_anim_.cur_cycle++;
+ batt_anim_.cur_frame = 0;
/* don't reset the cycle counter, since we use that as a signal
* in a test above to check if animation is over
@@ -411,29 +409,29 @@
* If we stop it immediately instead of going through this loop, then
* the animation would stop somewhere in the middle.
*/
- batt_anim->cur_frame = 0;
- batt_anim->cur_cycle++;
+ batt_anim_.cur_frame = 0;
+ batt_anim_.cur_cycle++;
}
}
-static int set_key_callback(charger* charger, int code, int value) {
+int Charger::SetKeyCallback(int code, int value) {
int64_t now = curr_time_ms();
int down = !!value;
if (code > KEY_MAX) return -1;
/* ignore events that don't modify our state */
- if (charger->keys[code].down == down) return 0;
+ if (keys_[code].down == down) return 0;
/* only record the down even timestamp, as the amount
* of time the key spent not being pressed is not useful */
- if (down) charger->keys[code].timestamp = now;
- charger->keys[code].down = down;
- charger->keys[code].pending = true;
+ if (down) keys_[code].timestamp = now;
+ keys_[code].down = down;
+ keys_[code].pending = true;
if (down) {
LOGV("[%" PRId64 "] key[%d] down\n", now, code);
} else {
- int64_t duration = now - charger->keys[code].timestamp;
+ int64_t duration = now - keys_[code].timestamp;
int64_t secs = duration / 1000;
int64_t msecs = duration - secs * 1000;
LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", now, code,
@@ -443,20 +441,19 @@
return 0;
}
-static void update_input_state(charger* charger, input_event* ev) {
+void Charger::UpdateInputState(input_event* ev) {
if (ev->type != EV_KEY) return;
- set_key_callback(charger, ev->code, ev->value);
+ SetKeyCallback(ev->code, ev->value);
}
-static void set_next_key_check(charger* charger, key_state* key, int64_t timeout) {
+void Charger::SetNextKeyCheck(key_state* key, int64_t timeout) {
int64_t then = key->timestamp + timeout;
- if (charger->next_key_check == -1 || then < charger->next_key_check)
- charger->next_key_check = then;
+ if (next_key_check_ == -1 || then < next_key_check_) next_key_check_ = then;
}
-static void process_key(charger* charger, int code, int64_t now) {
- key_state* key = &charger->keys[code];
+void Charger::ProcessKey(int code, int64_t now) {
+ key_state* key = &keys_[code];
if (code == KEY_POWER) {
if (key->down) {
@@ -469,7 +466,7 @@
LOGW("[%" PRId64 "] booting from charger mode\n", now);
property_set("sys.boot_from_charger_mode", "1");
} else {
- if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
+ if (batt_anim_.cur_level >= boot_min_cap_) {
LOGW("[%" PRId64 "] rebooting\n", now);
reboot(RB_AUTOBOOT);
} else {
@@ -483,18 +480,18 @@
/* if the key is pressed but timeout hasn't expired,
* make sure we wake up at the right-ish time to check
*/
- set_next_key_check(charger, key, POWER_ON_KEY_TIME);
+ SetNextKeyCheck(key, POWER_ON_KEY_TIME);
/* Turn on the display and kick animation on power-key press
* rather than on key release
*/
- kick_animation(charger->batt_anim);
+ kick_animation(&batt_anim_);
request_suspend(false);
}
} else {
/* if the power key got released, force screen state cycle */
if (key->pending) {
- kick_animation(charger->batt_anim);
+ kick_animation(&batt_anim_);
request_suspend(false);
}
}
@@ -503,36 +500,35 @@
key->pending = false;
}
-static void handle_input_state(charger* charger, int64_t now) {
- process_key(charger, KEY_POWER, now);
+void Charger::HandleInputState(int64_t now) {
+ ProcessKey(KEY_POWER, now);
- if (charger->next_key_check != -1 && now > charger->next_key_check)
- charger->next_key_check = -1;
+ if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
}
-static void handle_power_supply_state(charger* charger, int64_t now) {
+void Charger::HandlePowerSupplyState(int64_t now) {
int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
- if (!charger->have_battery_state) return;
+ if (!have_battery_state_) return;
- if (!charger->charger_connected) {
+ if (!charger_online()) {
request_suspend(false);
- if (charger->next_pwr_check == -1) {
+ if (next_pwr_check_ == -1) {
/* Last cycle would have stopped at the extreme top of battery-icon
* Need to show the correct level corresponding to capacity.
*
- * Reset next_screen_transition to update screen immediately.
+ * Reset next_screen_transition_ to update screen immediately.
* Reset & kick animation to show complete animation cycles
* when charger disconnected.
*/
timer_shutdown =
property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);
- charger->next_screen_transition = now - 1;
- reset_animation(charger->batt_anim);
- kick_animation(charger->batt_anim);
- charger->next_pwr_check = now + timer_shutdown;
+ next_screen_transition_ = now - 1;
+ reset_animation(&batt_anim_);
+ kick_animation(&batt_anim_);
+ next_pwr_check_ = now + timer_shutdown;
LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
- now, (int64_t)timer_shutdown, charger->next_pwr_check);
- } else if (now >= charger->next_pwr_check) {
+ now, (int64_t)timer_shutdown, next_pwr_check_);
+ } else if (now >= next_pwr_check_) {
LOGW("[%" PRId64 "] shutting down\n", now);
reboot(RB_POWER_OFF);
} else {
@@ -540,64 +536,60 @@
}
} else {
/* online supply present, reset shutdown timer if set */
- if (charger->next_pwr_check != -1) {
- /* Reset next_screen_transition to update screen immediately.
+ if (next_pwr_check_ != -1) {
+ /* Reset next_screen_transition_ to update screen immediately.
* Reset & kick animation to show complete animation cycles
* when charger connected again.
*/
request_suspend(false);
- charger->next_screen_transition = now - 1;
- reset_animation(charger->batt_anim);
- kick_animation(charger->batt_anim);
+ next_screen_transition_ = now - 1;
+ reset_animation(&batt_anim_);
+ kick_animation(&batt_anim_);
LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
}
- charger->next_pwr_check = -1;
+ next_pwr_check_ = -1;
}
}
-void healthd_mode_charger_heartbeat() {
- charger* charger = &charger_state;
+void Charger::Heartbeat() {
+ // charger* charger = &charger_state;
int64_t now = curr_time_ms();
- handle_input_state(charger, now);
- handle_power_supply_state(charger, now);
+ HandleInputState(now);
+ HandlePowerSupplyState(now);
/* do screen update last in case any of the above want to start
* screen transitions (animations, etc)
*/
- update_screen_state(charger, now);
+ UpdateScreenState(now);
}
-void healthd_mode_charger_battery_update(android::BatteryProperties* props) {
- charger* charger = &charger_state;
+void Charger::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {
+ set_charger_online(health_info);
- charger->charger_connected =
- props->chargerAcOnline || props->chargerUsbOnline || props->chargerWirelessOnline;
-
- if (!charger->have_battery_state) {
- charger->have_battery_state = true;
- charger->next_screen_transition = curr_time_ms() - 1;
+ if (!have_battery_state_) {
+ have_battery_state_ = true;
+ next_screen_transition_ = curr_time_ms() - 1;
request_suspend(false);
- reset_animation(charger->batt_anim);
- kick_animation(charger->batt_anim);
+ reset_animation(&batt_anim_);
+ kick_animation(&batt_anim_);
}
- batt_prop = props;
+ health_info_ = health_info.legacy.legacy;
+
+ AdjustWakealarmPeriods(charger_online());
}
-int healthd_mode_charger_preparetowait(void) {
- charger* charger = &charger_state;
+int Charger::PrepareToWait(void) {
int64_t now = curr_time_ms();
int64_t next_event = INT64_MAX;
int64_t timeout;
LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n",
- now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check);
+ now, next_screen_transition_, next_key_check_, next_pwr_check_);
- if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition;
- if (charger->next_key_check != -1 && charger->next_key_check < next_event)
- next_event = charger->next_key_check;
- if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
- next_event = charger->next_pwr_check;
+ if (next_screen_transition_ != -1) next_event = next_screen_transition_;
+ if (next_key_check_ != -1 && next_key_check_ < next_event) next_event = next_key_check_;
+ if (next_pwr_check_ != -1 && next_pwr_check_ < next_event) next_event = next_pwr_check_;
if (next_event != -1 && next_event != INT64_MAX)
timeout = max(0, next_event - now);
@@ -607,32 +599,32 @@
return (int)timeout;
}
-static int input_callback(charger* charger, int fd, unsigned int epevents) {
+int Charger::InputCallback(int fd, unsigned int epevents) {
input_event ev;
int ret;
ret = ev_get_input(fd, epevents, &ev);
if (ret) return -1;
- update_input_state(charger, &ev);
+ UpdateInputState(&ev);
return 0;
}
-static void charger_event_handler(uint32_t /*epevents*/) {
+static void charger_event_handler(HealthLoop* /*charger_loop*/, uint32_t /*epevents*/) {
int ret;
ret = ev_wait(-1);
if (!ret) ev_dispatch();
}
-animation* init_animation() {
+void Charger::InitAnimation() {
bool parse_success;
std::string content;
if (base::ReadFileToString(product_animation_desc_path, &content)) {
- parse_success = parse_animation_desc(content, &battery_animation);
- battery_animation.set_resource_root(product_animation_root);
+ parse_success = parse_animation_desc(content, &batt_anim_);
+ batt_anim_.set_resource_root(product_animation_root);
} else if (base::ReadFileToString(animation_desc_path, &content)) {
- parse_success = parse_animation_desc(content, &battery_animation);
+ parse_success = parse_animation_desc(content, &batt_anim_);
} else {
LOGW("Could not open animation description at %s\n", animation_desc_path);
parse_success = false;
@@ -640,41 +632,36 @@
if (!parse_success) {
LOGW("Could not parse animation description. Using default animation.\n");
- battery_animation = BASE_ANIMATION;
- battery_animation.animation_file.assign("charger/battery_scale");
- battery_animation.frames = default_animation_frames;
- battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
+ batt_anim_ = BASE_ANIMATION;
+ batt_anim_.animation_file.assign("charger/battery_scale");
+ InitDefaultAnimationFrames();
+ batt_anim_.frames = owned_frames_.data();
+ batt_anim_.num_frames = owned_frames_.size();
}
- if (battery_animation.fail_file.empty()) {
- battery_animation.fail_file.assign("charger/battery_fail");
+ if (batt_anim_.fail_file.empty()) {
+ batt_anim_.fail_file.assign("charger/battery_fail");
}
LOGV("Animation Description:\n");
- LOGV(" animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
- battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
- battery_animation.num_frames);
- LOGV(" fail_file: '%s'\n", battery_animation.fail_file.c_str());
- LOGV(" clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
- battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
- battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
- battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
- LOGV(" percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
- battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
- battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
- battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
- for (int i = 0; i < battery_animation.num_frames; i++) {
- LOGV(" frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
- battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
+ LOGV(" animation: %d %d '%s' (%d)\n", batt_anim_.num_cycles, batt_anim_.first_frame_repeats,
+ batt_anim_.animation_file.c_str(), batt_anim_.num_frames);
+ LOGV(" fail_file: '%s'\n", batt_anim_.fail_file.c_str());
+ LOGV(" clock: %d %d %d %d %d %d '%s'\n", batt_anim_.text_clock.pos_x,
+ batt_anim_.text_clock.pos_y, batt_anim_.text_clock.color_r, batt_anim_.text_clock.color_g,
+ batt_anim_.text_clock.color_b, batt_anim_.text_clock.color_a,
+ batt_anim_.text_clock.font_file.c_str());
+ LOGV(" percent: %d %d %d %d %d %d '%s'\n", batt_anim_.text_percent.pos_x,
+ batt_anim_.text_percent.pos_y, batt_anim_.text_percent.color_r,
+ batt_anim_.text_percent.color_g, batt_anim_.text_percent.color_b,
+ batt_anim_.text_percent.color_a, batt_anim_.text_percent.font_file.c_str());
+ for (int i = 0; i < batt_anim_.num_frames; i++) {
+ LOGV(" frame %.2d: %d %d %d\n", i, batt_anim_.frames[i].disp_time,
+ batt_anim_.frames[i].min_level, batt_anim_.frames[i].max_level);
}
-
- return &battery_animation;
}
-void healthd_mode_charger_init(struct healthd_config* config) {
- using android::hardware::health::V2_0::implementation::Health;
-
+void Charger::Init(struct healthd_config* config) {
int ret;
- charger* charger = &charger_state;
int i;
int epollfd;
@@ -682,22 +669,22 @@
LOGW("--------------- STARTING CHARGER MODE ---------------\n");
- ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
+ ret = ev_init(
+ std::bind(&Charger::InputCallback, this, std::placeholders::_1, std::placeholders::_2));
if (!ret) {
epollfd = ev_get_epollfd();
- healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
+ RegisterEvent(epollfd, &charger_event_handler, EVENT_WAKEUP_FD);
}
- animation* anim = init_animation();
- charger->batt_anim = anim;
+ InitAnimation();
- ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
+ ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
- ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
+ ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load built in battery_fail image\n");
- charger->surf_unknown = NULL;
+ surf_unknown_ = NULL;
}
}
@@ -705,49 +692,41 @@
int scale_count;
int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
// chunk). We are using hard-coded frame.disp_time instead.
- ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
- &scale_frames);
+ ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
+ &scale_fps, &scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
- anim->num_frames = 0;
- anim->num_cycles = 1;
- } else if (scale_count != anim->num_frames) {
+ batt_anim_.num_frames = 0;
+ batt_anim_.num_cycles = 1;
+ } else if (scale_count != batt_anim_.num_frames) {
LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
- anim->num_frames);
- anim->num_frames = 0;
- anim->num_cycles = 1;
+ batt_anim_.num_frames);
+ batt_anim_.num_frames = 0;
+ batt_anim_.num_cycles = 1;
} else {
- for (i = 0; i < anim->num_frames; i++) {
- anim->frames[i].surface = scale_frames[i];
+ for (i = 0; i < batt_anim_.num_frames; i++) {
+ batt_anim_.frames[i].surface = scale_frames[i];
}
}
- ev_sync_key_state(
- std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
+ ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
+ std::placeholders::_2));
- charger->next_screen_transition = -1;
- charger->next_key_check = -1;
- charger->next_pwr_check = -1;
- charger->wait_batt_level_timestamp = 0;
+ next_screen_transition_ = -1;
+ next_key_check_ = -1;
+ next_pwr_check_ = -1;
+ wait_batt_level_timestamp_ = 0;
- // Initialize Health implementation (which initializes the internal BatteryMonitor).
- Health::initInstance(config);
+ // Retrieve healthd_config from the existing health HAL.
+ HalHealthLoop::Init(config);
- healthd_config = config;
- charger->boot_min_cap = config->boot_min_cap;
+ boot_min_cap_ = config->boot_min_cap;
}
-static struct healthd_mode_ops charger_ops = {
- .init = healthd_mode_charger_init,
- .preparetowait = healthd_mode_charger_preparetowait,
- .heartbeat = healthd_mode_charger_heartbeat,
- .battery_update = healthd_mode_charger_battery_update,
-};
+} // namespace android
int healthd_charger_main(int argc, char** argv) {
int ch;
- healthd_mode_ops = &charger_ops;
-
while ((ch = getopt(argc, argv, "cr")) != -1) {
switch (ch) {
case 'c':
@@ -763,5 +742,6 @@
}
}
- return healthd_main();
+ Charger charger(GetPassthroughHealth());
+ return charger.StartLoop();
}
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 2f0c9f2..370ca86 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -16,4 +16,72 @@
#pragma once
+#include <linux/input.h>
+
+#include <memory>
+#include <vector>
+
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <android/hardware/health/2.1/IHealth.h>
+#include <health2impl/HalHealthLoop.h>
+
+#include "animation.h"
+
+class GRSurface;
+class HealthdDraw;
+
+namespace android {
+struct key_state {
+ bool pending;
+ bool down;
+ int64_t timestamp;
+};
+
+class Charger : public ::android::hardware::health::V2_1::implementation::HalHealthLoop {
+ public:
+ using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+ using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+
+ Charger(const sp<android::hardware::health::V2_1::IHealth>& service);
+ ~Charger();
+
+ protected:
+ // HealthLoop overrides.
+ void Heartbeat() override;
+ int PrepareToWait() override;
+ void Init(struct healthd_config* config) override;
+ // HalHealthLoop overrides
+ void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
+
+ private:
+ void InitDefaultAnimationFrames();
+ void UpdateScreenState(int64_t now);
+ int SetKeyCallback(int code, int value);
+ void UpdateInputState(input_event* ev);
+ void SetNextKeyCheck(key_state* key, int64_t timeout);
+ void ProcessKey(int code, int64_t now);
+ void HandleInputState(int64_t now);
+ void HandlePowerSupplyState(int64_t now);
+ int InputCallback(int fd, unsigned int epevents);
+ void InitAnimation();
+
+ bool have_battery_state_ = false;
+ bool screen_blanked_ = false;
+ int64_t next_screen_transition_ = 0;
+ int64_t next_key_check_ = 0;
+ int64_t next_pwr_check_ = 0;
+ int64_t wait_batt_level_timestamp_ = 0;
+
+ key_state keys_[KEY_MAX + 1];
+
+ animation batt_anim_;
+ GRSurface* surf_unknown_ = nullptr;
+ int boot_min_cap_ = 0;
+
+ HealthInfo_1_0 health_info_ = {};
+ std::unique_ptr<HealthdDraw> healthd_draw_;
+ std::vector<animation::frame> owned_frames_;
+};
+} // namespace android
+
int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
index bcc04d5..13e7348 100644
--- a/healthd/healthd_mode_charger_nops.cpp
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -16,45 +16,14 @@
#include "healthd_mode_charger_nops.h"
-#include <health2/Health.h>
-#include <healthd/healthd.h>
+#include <health2impl/HalHealthLoop.h>
-#include <stdlib.h>
-#include <string.h>
+#include "charger_utils.h"
-using namespace android;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config* config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(struct android::BatteryProperties* props);
-
-static struct healthd_mode_ops healthd_nops = {
- .init = healthd_mode_nop_init,
- .preparetowait = healthd_mode_nop_preparetowait,
- .heartbeat = healthd_mode_nop_heartbeat,
- .battery_update = healthd_mode_nop_battery_update,
-};
-
-static void healthd_mode_nop_init(struct healthd_config* config) {
- using android::hardware::health::V2_0::implementation::Health;
- Health::initInstance(config);
-}
-
-static int healthd_mode_nop_preparetowait(void) {
- return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {}
-
-static void healthd_mode_nop_battery_update(struct android::BatteryProperties* /*props*/) {}
+using android::hardware::health::GetPassthroughHealth;
+using android::hardware::health::V2_1::implementation::HalHealthLoop;
int healthd_charger_nops(int /* argc */, char** /* argv */) {
- healthd_mode_ops = &healthd_nops;
- return healthd_main();
+ HalHealthLoop charger("charger", GetPassthroughHealth());
+ return charger.StartLoop();
}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index fd2d766..ac44796 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -239,11 +239,16 @@
}
Modprobe m({"/lib/modules"});
- if (!m.LoadListedModules()) {
- LOG(FATAL) << "Failed to load kernel modules";
+ auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
+ if (!m.LoadListedModules(!want_console)) {
+ if (want_console) {
+ LOG(ERROR) << "Failed to load kernel modules, starting console";
+ } else {
+ LOG(FATAL) << "Failed to load kernel modules";
+ }
}
- if (ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline)) {
+ if (want_console) {
StartConsole();
}
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 5dd5cf1..9b33a1c 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -26,6 +26,7 @@
// android/api-level.h
#define __ANDROID_API_P__ 28
+#define __ANDROID_API_Q__ 29
#define __ANDROID_API_R__ 30
// sys/system_properties.h
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index e7808a9..e6a341d 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -560,6 +560,11 @@
str_args[0] = "/system/bin/watchdogd";
}
}
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+ if (str_args[0] == "/charger") {
+ str_args[0] = "/system/bin/charger";
+ }
+ }
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
return {};
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 8947256..a8fcc87 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -61,8 +61,8 @@
TEST(util, ReadFileSymbolicLink) {
errno = 0;
- // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- auto file_contents = ReadFile("/charger");
+ // lrw------- 1 root root 23 2008-12-31 19:00 default.prop -> system/etc/prop.default
+ auto file_contents = ReadFile("/default.prop");
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents);
EXPECT_EQ("open() failed: Too many symbolic links encountered",
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 421d826..333fc55 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -26,7 +26,7 @@
public:
Modprobe(const std::vector<std::string>&);
- bool LoadListedModules();
+ bool LoadListedModules(bool strict = true);
bool LoadWithAliases(const std::string& module_name, bool strict,
const std::string& parameters = "");
bool Remove(const std::string& module_name);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 3c78ec9..6b9107f 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -360,13 +360,15 @@
return true;
}
-bool Modprobe::LoadListedModules() {
+bool Modprobe::LoadListedModules(bool strict) {
+ auto ret = true;
for (const auto& module : module_load_) {
if (!LoadWithAliases(module, true)) {
- return false;
+ ret = false;
+ if (strict) break;
}
}
- return true;
+ return ret;
}
bool Modprobe::Remove(const std::string& module_name) {
diff --git a/libstats/OWNERS b/libstats/OWNERS
index ed06fbc..7855774 100644
--- a/libstats/OWNERS
+++ b/libstats/OWNERS
@@ -1,4 +1,7 @@
-bookatz@google.com
joeo@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
yaochen@google.com
-yanglu@google.com
+yro@google.com
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index af70f1d..e4839b4 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -15,11 +15,12 @@
*/
#include <errno.h>
-#include <error.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <getopt.h>
#include <inttypes.h>
+#include <libgen.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -64,6 +65,20 @@
static uint64_t total_compressed_length = 0;
static size_t file_count = 0;
+static const char* g_progname;
+
+static void die(int error, const char* fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: ", g_progname);
+ vfprintf(stderr, fmt, ap);
+ if (error != 0) fprintf(stderr, ": %s", strerror(error));
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
static bool ShouldInclude(const std::string& name) {
// Explicitly excluded?
if (!excludes.empty()) {
@@ -155,7 +170,7 @@
char* line = nullptr;
size_t n;
if (getline(&line, &n, stdin) == -1) {
- error(1, 0, "(EOF/read error; assuming [N]one...)");
+ die(0, "(EOF/read error; assuming [N]one...)");
overwrite_mode = kNever;
return false;
}
@@ -183,10 +198,10 @@
uint8_t* buffer = new uint8_t[entry.uncompressed_length];
int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
if (err < 0) {
- error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+ die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
}
if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
- error(1, errno, "failed to write %s to stdout", name.c_str());
+ die(errno, "failed to write %s to stdout", name.c_str());
}
delete[] buffer;
}
@@ -194,7 +209,7 @@
static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
// Bad filename?
if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
- error(1, 0, "bad filename %s", name.c_str());
+ die(0, "bad filename %s", name.c_str());
}
// Where are we actually extracting to (for human-readable output)?
@@ -207,7 +222,7 @@
// Ensure the directory hierarchy exists.
if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
- error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
+ die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
}
// An entry in a zip file can just be a directory itself.
@@ -218,7 +233,7 @@
struct stat sb;
if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
}
- error(1, errno, "couldn't extract directory %s", dst.c_str());
+ die(errno, "couldn't extract directory %s", dst.c_str());
}
return;
}
@@ -231,12 +246,12 @@
// Either overwrite_mode is kAlways or the user consented to this specific case.
fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
}
- if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
+ if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
// Actually extract into the file.
if (!flag_q) printf(" inflating: %s\n", dst.c_str());
int err = ExtractEntryToFile(zah, &entry, fd);
- if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+ if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
close(fd);
}
@@ -345,7 +360,7 @@
void* cookie;
int err = StartIteration(zah, &cookie);
if (err != 0) {
- error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+ die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
}
ZipEntry entry;
@@ -354,7 +369,7 @@
if (ShouldInclude(name)) ProcessOne(zah, entry, name);
}
- if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+ if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
EndIteration(cookie);
MaybeShowFooter();
@@ -420,14 +435,14 @@
int main(int argc, char* argv[]) {
// Who am I, and what am I doing?
- const char* base = basename(argv[0]);
- if (!strcmp(base, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
- if (!strcmp(base, "unzip")) {
+ g_progname = basename(argv[0]);
+ if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
+ if (!strcmp(g_progname, "unzip")) {
role = kUnzip;
- } else if (!strcmp(base, "zipinfo")) {
+ } else if (!strcmp(g_progname, "zipinfo")) {
role = kZipinfo;
} else {
- error(1, 0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
+ die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
}
static const struct option opts[] = {
@@ -484,19 +499,19 @@
}
}
- if (!archive_name) error(1, 0, "missing archive filename");
+ if (!archive_name) die(0, "missing archive filename");
// We can't support "-" to unzip from stdin because libziparchive relies on mmap.
ZipArchiveHandle zah;
int32_t err;
if ((err = OpenArchive(archive_name, &zah)) != 0) {
- error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+ die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
}
// Implement -d by changing into that directory.
// We'll create implicit directories based on paths in the zip file, but we
// require that the -d directory already exists.
- if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
+ if (flag_d && chdir(flag_d) == -1) die(errno, "couldn't chdir to %s", flag_d);
ProcessAll(zah);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 5241730..eac3f06 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -293,78 +293,26 @@
LOCAL_MODULE_STEM := ld.config.txt
include $(BUILD_PREBUILT)
-# Returns the unique installed basenames of a module, or module.so if there are
-# none. The guess is to handle cases like libc, where the module itself is
-# marked uninstallable but a symlink is installed with the name libc.so.
-# $(1): list of libraries
-# $(2): suffix to to add to each library (not used for guess)
-define module-installed-files-or-guess
-$(foreach lib,$(1),$(or $(strip $(sort $(notdir $(call module-installed-files,$(lib)$(2))))),$(lib).so))
-endef
-
#######################################
-# llndk.libraries.txt
-include $(CLEAR_VARS)
-LOCAL_MODULE := llndk.libraries.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(call module-installed-files-or-guess,$(LLNDK_LIBRARIES),)
-$(LOCAL_BUILT_MODULE):
- @echo "Generate: $@"
- @mkdir -p $(dir $@)
- $(hide) echo -n > $@
- $(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \
- echo $(lib) >> $@;)
+# {llndk,vndkcore,vndksp,vndkprivate,vndkcorevariant}.libraries.txt
+vndk_libraries_files := \
+ llndk.libraries.txt:$(SOONG_LLNDK_LIBRARIES_FILE)\
+ vndkcore.libraries.txt:$(SOONG_VNDKCORE_LIBRARIES_FILE)\
+ vndksp.libraries.txt:$(SOONG_VNDKSP_LIBRARIES_FILE)\
+ vndkprivate.libraries.txt:$(SOONG_VNDKPRIVATE_LIBRARIES_FILE)\
+ vndkcorevariant.libraries.txt:$(SOONG_VNDKCOREVARIANT_LIBRARIES_FILE)
-#######################################
-# vndksp.libraries.txt
-include $(CLEAR_VARS)
-LOCAL_MODULE := vndksp.libraries.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.com.android.vndk.current)
-$(LOCAL_BUILT_MODULE):
- @echo "Generate: $@"
- @mkdir -p $(dir $@)
- $(hide) echo -n > $@
- $(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
- echo $(lib) >> $@;)
-
-#######################################
-# vndkcore.libraries.txt
-include $(CLEAR_VARS)
-LOCAL_MODULE := vndkcore.libraries.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.com.android.vndk.current)
-$(LOCAL_BUILT_MODULE):
- @echo "Generate: $@"
- @mkdir -p $(dir $@)
- $(hide) echo -n > $@
- $(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_LIBRARIES), \
- echo $(lib) >> $@;)
-
-#######################################
-# vndkprivate.libraries.txt
-include $(CLEAR_VARS)
-LOCAL_MODULE := vndkprivate.libraries.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.com.android.vndk.current)
-$(LOCAL_BUILT_MODULE):
- @echo "Generate: $@"
- @mkdir -p $(dir $@)
- $(hide) echo -n > $@
- $(hide) $(foreach lib,$(PRIVATE_VNDK_PRIVATE_LIBRARIES), \
- echo $(lib) >> $@;)
+$(foreach pair,$(vndk_libraries_files),\
+ $(eval _filename := $(call word-colon,1,$(pair)))\
+ $(eval _prebuilt := $(call word-colon,2,$(pair)))\
+ $(eval include $(CLEAR_VARS))\
+ $(eval LOCAL_MODULE := $(_filename))\
+ $(eval LOCAL_MODULE_CLASS := ETC)\
+ $(eval LOCAL_PREBUILT_MODULE_FILE := $(_prebuilt))\
+ $(eval LOCAL_MODULE_PATH := $(TARGET_OUT_ETC))\
+ $(eval LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE)))\
+ $(eval include $(BUILD_PREBUILT)))
+vndk_libraries_files :=
#######################################
# sanitizer.libraries.txt
@@ -391,22 +339,6 @@
echo $(lib) >> $@;)
#######################################
-# vndkcorevariant.libraries.txt
-include $(CLEAR_VARS)
-LOCAL_MODULE := vndkcorevariant.libraries.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
-include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_VARIANT_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_USING_CORE_VARIANT_LIBRARIES),.vendor)
-$(LOCAL_BUILT_MODULE):
- @echo "Generate: $@"
- @mkdir -p $(dir $@)
- $(hide) echo -n > $@
- $(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_VARIANT_LIBRARIES), \
- echo $(lib) >> $@;)
-
-#######################################
# adb_debug.prop in debug ramdisk
include $(CLEAR_VARS)
LOCAL_MODULE := adb_debug.prop
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9b77ce2..66d60fa 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -919,7 +919,7 @@
on init && property:ro.debuggable=1
start console
-on userspace-reboot
+on userspace-reboot-requested
# TODO(b/135984674): reset all necessary properties here.
setprop sys.init.userspace_reboot_in_progress 1
setprop sys.boot_completed 0