This class adds a wrapper to the runtime dependent functions. Therefore,
the behavior of update on device stays the same, while simulators can
have their own implementations. Also change the caller side of the
registered updater functions to call these runtime wrappers.
Bug: 131911365
Test: unit tests pass, sideload an update on cuttlefish
Change-Id: Ib3ab67132991d67fc132f27120e4152439d16ac5
bootloader_message.h currently divides /misc into four segments. The
space between 2K and 16K is reserved for vendor use (e.g. bootloader
persists flags). This CL adds a vendor tool "misc_writer", to allow
writing data to the vendor space in /misc, before getting a dedicated
HAL for accessing /misc partition (b/131775112).
Targets need to explicitly include the module, then invoke the
executable to write data. For example, the following command will write
3-byte data ("0xABCDEF") to offset 4 in vendor space (i.e. 2048 + 4 in
/misc).
$ /vendor/bin/misc_writer --vendor-space-offset 4 --hex-string 0xABCDEF
Bug: 132906936
Test: Run recovery_unit_test on crosshatch.
Test: Call the command via init.hardware.rc on crosshatch. Check that
the call finishes successfully. Then check the contents written to
/misc (`dd bs=1 skip=2048 if=/dev/block/sda2 count=32 | xxd`).
Change-Id: I79548fc63fc79b705a0320868690569c3106949f
The UpdaterInfo class is merely a collection of pointers and POD types.
We can replace it with a Updater class that has the ownership of the
resources. This also makes this class extensible as we plan to add more
functionality in the host simulator.
Bug: 131911365
Test: unit tests pass, run an update on cuttlefish and check last_install
Change-Id: I07ca5963bbee8ae3cb85ccc184464910aa73d4e4
Adds a fuse data provider that parses the metadata from a block map,
reads the data from the given ranges of the block device; and provides
the data to the fuse.
Bug: 127071893
Test: unit tests pass, install a package from block map
Change-Id: Ie9925ee9144e98642505b3f5e1a4a186d2b21ed0
The results from these functions have boolean semantics. They're
returning `int` prior to this CL, with some of them mixing 0 and
InstallResult. Note that SetUpNonAbUpdateCommands() was returning
INSTALL_CORRUPT / INSTALL_ERROR / 0 prior to this change, but all the
callers handle INSTALL_CORRUPT and INSTALL_ERROR the same way.
This CL changes them to return bool instead.
Test: `mmma -j bootable/recovery`
Test: TreeHugger
Test: Sideload on taimen.
Change-Id: Ic1b5dbf79aaca68b53ab8ea2c8ba3d19f988c571
Most of the tests in component/ are in fact unit tests. And it doesn't
look practically beneficial to distinguish between the two:
- They have the same test setup;
- We always run both (recovery_unit_test and recovery_component_test)
at the same time;
- Breaking any of them would be equally bad.
This CL merges the tests in recovery_component_test into
recovery_unit_test to save the effort to maintain both.
Test: Run recovery_unit_test on marlin (via `adb sync data`).
Test: `atest recovery_unit_test`
Change-Id: I93ff32e7219cd83425a4bcfe5613978a8dd48d75
Prior to this CL, GetWipePartitionList was declared in install.h
(libinstall) but defined in recovery.cpp (librecovery). This CL
addresses the issue by refactoring wipe-device related functions into
install/wipe_device.cpp.
Test: atest recovery_component_test
Change-Id: I7ebe04ccfda3d793e085403560a0a202752d9ee3
Also remove the AndroidTest.xml file, which is no longer needed (the
`data` property in Android.bp takes care of that). The AndroidTest.xml
file would otherwise block `atest recovery_host_test` from running.
Test: TreeHugger; check the test result.
Change-Id: If545878a1f3ae627986e19a94b42162f133b9098
The legacy png files have an empty line in the end. And the recovery
used to match any missing locale, e.g. "he" with that line and gets an
empty image.
Since the empty image is barely useful, we should just error out and
fall back to the default locale.
This reversed the unit test check added in d17a688525
Bug: 128934634
Test: run locale test with "he" and legacy images, recovery reports
error and doesn't crash even without default locale fall back
Change-Id: Ibdb7dd0b42348de5e392c834cce67ff02be85c24
This leaves the FD implementation details to subclasses. In particular,
it allows minadbd to do additional works with the FD after sideloading.
Bug: 128415917
Test: atest recovery_component_test
Test: atest minadbd_test
Test: Sideload package on taimen.
Change-Id: I106bbaad05201227bbc5fe28890bbbb06fdcb67e
Build libinstall as a shared library. Also drop the dependency on the
global variables in common.h.
Test: unit tests pass, sideload an OTA
Change-Id: I30a20047768ce00689fc0e7851c1c5d712a365a0
We will reuse them to implement the fuse provider from block maps.
Test: unit tests pass, sideload an OTA
Change-Id: Iaa409d19569c4ccc0bb24e12518044fcddb45c69
This helps to expose librecovery_ui for device specific RecoveryUi.
Bug: 76436783
Test: mma, unit tests pass
Change-Id: Ic6c3d301d5833e4a592e6ea9d9d059bc4e4919be
(cherry picked from commit b5108c372c)
It's no longer needed with the newly added FuseDataProvider class. Also
cleans up the parameters for run_fuse_sideload.
Bug: 127071893
Test: unit tests pass, run a sideload
Change-Id: I1ccd6798d187cfc6ac9f559ffb3f3edf08dad55c
The fuse data provider for adb/sdcard shares common code and structures.
This cl creates a FuseDataProvider base class and provides
implementations for adb and sdcard.
In the follow cls, we can kill the provider_vtab struct; and also add
another implementation to parse a block map file and provides data.
Test: unit tests pass, sideload a package, apply a package from sdcard
Change-Id: If8311666a52a2e3c0fbae0ee9688fa6d01e4ad09
This is another implementation of the Package class. And we will later
need it when reading the package from FUSE.
Bug: 127071893
Test: unit tests pass, sideload a file package on sailfish
Change-Id: I3de5d5ef60b29c8b73517d6de3498459d7d95975
We have already switched to the protobuf format for new builds, and
the downgrade packages will require a data wipe. So it should be safe
to drop the support for text format.
This also helps to save the issue when users sideload a package with a
pending OTA, because the new CareMap contains the fingerprint of the
intended build.
Bug: 128536706
Test: unit tests pass, run update_verifier with legacy CareMap
Change-Id: I1c4d0e54ec591f16cc0a65dac76767725ff9e7c4
The wipe package used to open the zip file directly from the content
string. Switch to use the interface from the new package class instead.
Bug: 127071893
Test: unit tests pass
Change-Id: I990e7f00c5148710722d17140bab2e343eea3b6b
Creates a new class handle the package in memory and package read from fd.
Define the new interface functions, and make approximate changes to the
verify and install functions.
Bug: 127071893
Test: unit tests pass, sideload a package
Change-Id: I66ab00654df92471184536fd147b237a86e9c5b5
This gives us finer control over the partitions to wipe on the host
side.
Bug: 127492427
Test: unit tests pass, install a wipe package on sailfish
Change-Id: I612f8bac743a310f28e365b490ef388b278cfccb
The intended string constructor is supposed be
basic_string(size_type count, CharT ch). But the parameter is
accidentally reversed when calling the constructor in install_test.
Test: A failed unit test pass
Change-Id: Id9765bfa7d2368ff0d7fbeea45c9c8357864e060
This reverts commit 9ce1d14ef6.
Reason for revert: AOSP is fixed with new vendor image
Change-Id: Ie5a9748acdae22a2b9862cb2ecedda7031f77264
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Because set_sched_policy is moved into libprocessgroup an additional
dependency is requred for recovery_component_test to build.
Exempt-From-Owner-Approval: janitorial
Bug: 111307099
Test: builds, boots
Merged-In: I7cf75e473ee1e2837940606c71d15be26db0c3f2
Change-Id: I7cf75e473ee1e2837940606c71d15be26db0c3f2
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Seems they're racing with SurfaceFlinger in acquiring the display, which
occasionally takes down the device and leads to test failures.
Bug: 120601844
Test: Run recovery_unit_test on marlin. ScreenRecoveryUITest not
triggered.
Change-Id: I80b21595247a87fc1f2f95aa68df59f58bdf0257
GRSurface::Create() doesn't need to rely on caller specifying the buffer
size, as it can compute that info based on the given args.
This CL also uses `size_t` for all the parameters in
GRSurface::Create().
Test: Run recovery_unit_test on marlin.
Test: Build and boot into blueline recovery. `Run graphics test`.
Test: Build and boot into blueline charger mode.
Change-Id: Idec9381079196abf13553a475006fefcfca10950
This is a bug introduced while refactoring init_display_surface(), in
[1]. As a result, user of res_create_multi_display_surface(), which is
effectively `charger` right now, crashes due to buffer overrun.
This CL fixes the wrong arg and adds a sanity test for
res_create_multi_display_surface(). The testdata (battery_scale.png) is
copied from system/core/healthd/images/battery_scale.png.
[1] commit 44820ac1e3.
Bug: 119122296
Test: Run recovery_unit_test on marlin.
Test: Build and boot into charger mode on blueline. Verify that
`charger` no longer crashes.
Change-Id: Ib6d083e1512a9c3c6eb63874d26d22658921d693
We can adjust the image width with respect to the maximum width of the
wrapped text. This will remove some black margins and reduce the final
size of the images, especially for those with short strings, e.g.
"recovery_error".
Also, add an option to centrally align the text; and fix a boundary
check in the recovery resource test.
Bug: 74397117
Test: Generate and check the image
Change-Id: Ib6cf61a9c99c4aeede16751dc0adfa23ce3f5424
Bug: http://b/119560349
Bug: http://b/112277682
Currently, any binary links in a PGO-enabled static library also needs
to opt into PGO. With b/119560349, this should be done automatically by
the build system. Until then, use the non-PGO version of libhwbinder in
recovery_component_test.
Test: m ANDROID_PGO_INSTRUMENT=all
Change-Id: Ic6e44c1cb6d6f13e60e11a46fd7e5ef54238942b
For any patching command, the resulting data should always exactly fill
up the given target range.
Test: Run recovery_component_test on marlin.
Change-Id: Ib3cc1fc5c11094e2eab3fe370753db51c7c4135c
testdata/jarsigned.zip and testdata/unsigned.zip became dead since
commit 432918603f ("Refactor existing
tests to use gtest").
testdata/patch.bsdiff became dead when applypatch/applypatch.sh was
deleted (commit c3ef089dfa).
Test: Run recovery_unit_test and recovery_component_test.
Change-Id: Ie1a7f8850878593fcb7d4554759a539271ffb207
Clone() allows duplicating the image that's stored in the GRSurface.
Test: Run recovery_unit_test.
Change-Id: Ia50d507c6200f2de5f17143775de805247a60e1f
This prepares for the removal of the default and copy ctors, by making
GRSurface::Create() as the only way to get GRSurface instances.
Test: mmma -j bootable/recovery
Test: Run recovery_unit_test on marlin.
Change-Id: I0c34c3f3967e252deb020907c83acbac8a8f36b9
The two functions check_wipe_package() and check_newer_ab_build() were
using the same flow; and checked the same device properties against the
metadata file in the package. These properties include: ota_type,
pre-device, and serial number.
Therefore, we can consolidate the checks to a single function; and
continue to check the fingerprint and timestamp only for AB updates.
This change also addresses the need to accept multiple serial number in
the wipe package.
Bug: 118401208
Test: unit tests pass
Change-Id: Ia6bc48fb6effcae059a2ff2cf71764b4136b4c00
This function is used to parse the result of dumpKeys. It's no longer
needed as we are now parsing the public keys from the zipfile.
Bug: 116655889
Test: unit tests pass
Change-Id: I817906e451664058c644f4329ff499bbe4587ebb
For RSA keys, check if it has a 2048 bits modulus, and its public
exponent is 3 or 65537. For EC keys, check if the field size is 256
bits for its curve.
Bug: 116655889
Test: unit tests pass
Change-Id: I5c00f4d2b61c98c434f0b49db232155d5d0770ec
This fixes the build error as the initializer list no longer work
without the proper constructor for c++ class.
Bug: 74397117
Test: unit tests pass
Change-Id: If3ff508a1a01ad5326413dab8e05bacae8a946c8
These functions take the given GRSurface instances as inputs, which
shouldn't be altered.
Test: mmma -j bootable/recovery
Test: Run recovery_unit_test.
Test: `Run graphics test` on marlin.
Change-Id: I51bf408e85faae2b497d4f148ab1dec22dd16c93
This CL adds GRSurface::Create() and dtor for managing the allocated
memory in GRSurface class. It also adds GRSurface::data() that hides the
underlying implementation, with both of const and non-const overloads.
This allows `const GRSurface&` to be more useful - previously it only
ensured a const member variable of `data`, instead of a read-only buffer
it points to.
It also marks the parameters in gr_texticon() and gr_blit() as const, as
they're incoming source that shouldn't be altered. It corrects the type
of gr_draw, which is the sink to be painted on (an earlier attempt was
made in [1], but didn't get the full picture correctly).
[1] https://android-review.googlesource.com/c/platform/bootable/recovery/+/704757/
Test: mmma -j bootable/recovery
Test: recovery_unit_test on marlin
Test: Run graphics test on marlin (fbdev).
Test: Run graphics test on blueline (drm).
Change-Id: I7904df084cd6c08fa04a9da97d01b4b1a6e3a20c
Add a function in screenUI to display the pre-generated graphs for
rescue party. If these graphs are not valid, falls back to display
the old text strings.
Right now we haven't generated the localized graphs yet, so the UI
always shows the TextMenu.
Bug: 116655889
Test: check rescue party under recovery
Change-Id: I0558cb536b659cdc25c8b7946d3a39820935b003
Add a function to parse the zip archive and load the certificate from
all the zip entries with the suffix "x509.pem".
Bug: 116655889
Test: unittests pass
Change-Id: I93bf7aef7462c0623e89fc2d466d7af2d3a758bc
As we plan to show localized rescue party dialogs under recovery mode
with pre-generated images, it becomes necessary to show the menu headers
and items with images.
This cl converts the menu class to a interface and derived TextMenu &
GraphicMenu classes. And the GraphicMenu uses GRSurfaces* as the menu header
and a list of GRSurfaces* as menu items.
Moreover, factor out the Draw* functions in the ScreenUI into a separate
DrawInterface. Therefore, the Menu class can access these draw functions and
use them to implement the DrawHeaders & DrawItems neatly.
Bug: 74397117
Test: unittests pass, boot into recovery and check menu
Change-Id: I95cee30f3e5eb666eb6fbcdfc873a7260fc177c1
We used to convert a pem certificate file to some intermediate plain
text format; and parse that format under recovery mode. This is
uncessary since the x509.pem can be directly parsed with openssl
functions.
Add the function to load the public key from one x509.pem file and
corresponding unit tests. And we will add more cls to extract the pem
files from otacert.zip later.
Bug: 116655889
Test: verify package with 5 supported certficate versions
Change-Id: Ibc6c696c534567f005db75143cc4ef8d4bdea6a0
The update_verifier now compares the fingerprint of a partition before
performing the blocks read. If the fingerprint of the current system property
mismatches the one embedded in the care_map, verification of this partition
will be skipped. This is useful for the possible system only updates in the
future.
Bug: 114778109
Test: unit tests pass
Change-Id: Iea309148a05109b5810dfb533d94260d77ab8540
dependency."
This reverts commit 26b86bb1dc.
Reason for revert: Dependencies which broke tests due to exclusion from
LOCAL_JNI_SHARED_LIBS have been added.
Change-Id: I364acfe8bd0526fb26f69cc29fb3545fc3e79764
This reverts commit a71c10b464.
Reason for revert: This breaks framework tests and blocks presubmit
Change-Id: Iae94878889c1e71e2da5336fe75af88dfd3bfcfa
The refactor separates out the parsing of care_map and the actual
verification of the partitions. Moreover, it skips the verification in case
of a format error in the care map.
Also, the parsing of care_map now uses the suffix of the file to
tell if it has the protobuf format or the plain text format.
Bug: 115740187
Test: unit test pass
Change-Id: I7aa32004db02af1deb7bfdc6f5bd7921eb7883e5
The recovery-persist used to look for the related recovery logs in
persist storage, and copy them under /data/misc/recovery during the
normal boot process.
As we also want to find out the sideload information from last_install,
it makes more sense to move the parse & report of non-a/b metrics to
recovery-persist. Thus we can avoid the race condition of the file
system between the native code and RecoverySystem.
Bug: 114278989
Test: unit test pass, check the event buffer for metrics report
Change-Id: I32d7b2b831bc74a61a70af9a2f0b8a7e9b3e36ee
applypatch() was initially designed for file-based OTA, operating on
individual files. It was later extended to allow patching eMMC targets
as a whole, in favor of block-based updates.
As we have deprecated file-based OTA since Oreo, part of the code in
applypatch() has become obsolete. This CL refactors the related
functions, by removing the obsolete logic and focusing on eMMC targets.
Since this CL substantially changes applypatch APIs, it adds new
functions to avoid unintentionally mixing them together. In particular,
it removes `applypatch()`, `applypatch_check()`, `applypatch_flash()`,
and adds `PatchPartition()`, `PatchPartitionCheck()`, `FlashPartition()`
and `CheckPartition()`. It also replaces the old Edify functions
`apply_patch()` and `apply_patch_check()` with `patch_partition()` and
`patch_partition_check()` respectively.
This CL requires matching changes to OTA generation script (in the same
topic).
Bug: 110106408
Test: Run recovery_unit_test and recovery_component_test on marlin.
Test: `m dist` with non-A/B target. Verify
/system/bin/install-recovery.sh on device.
Test: `m dist` with non-A/B target using BOARD_USES_FULL_RECOVERY_IMAGE.
Verify /system/bin/install-recovery.sh on device.
Test: Install an incremental OTA with the new updater and scripts.
Change-Id: Ia34a90114bb227f4216eb478c22dc98c8194cb7f
Now it's less beneficial to inject I/O faults since we don't see many of
them. Remove the library that mocks I/O failures. And switch to android::base
I/O when possible.
Bug: 113032079
Test: unit tests pass
Change-Id: I9f2a92b7ba80f4da6ff9e2abc27f2680138f942c
They serve the same purpose as {boot,recovery}.img, except that they're
not structured to be imgdiff'd. Remove the two files and replace all the
uses with {boot,recovery}.img instead.
Bug: 110106408
Test: Run recovery_{unit,component}_test on marlin.
Change-Id: I8e71187d5b0c142ad932f33717f6fae364b43abc
This would be the top-level class that represents and holds the info
parsed from a transfer list file.
Bug: 112151972
Test: Run recovery_unit_test and recovery_component_test on marlin.
Change-Id: I83b54df9d1411542eeeb8ef4a2db167e97f989c3
This file was somehow missed when converting from Android.mk to
Android.bp. This CL addresses the issue by picking up all the .cpp files
in the test source dir.
Test: Run recovery_{unit,component}_test on marlin. Check the reported
number of tests (94 for unit test and 157 for component test).
Change-Id: I50435b07fcb8602ef7b3f7e7d3a69e10b6a5932d
Also separate libupdater_defaults out to be shareable.
It turns out the `data` property in `cc_test` doesn't follow symlinks as
LOCAL_TEST_DATA does in Android.mk. This CL creates a filegroup in
top-level Android.bp in order to pick up the testdata for ResourcesTest.
Test: `mmma -j bootable/recovery` with aosp_marlin-userdebug
Test: Run recovery_{unit,component,manual}_test on marlin.
Test: Run recovery_host_test.
Change-Id: I4532ab25aeb83c0b0baa8051d5fe34ba7b910a35
Bug: 112494634
Test: Build and boot into recovery image on taimen. Verify that
`Apply updates from ADB` keeps working.
Test: Run recovery_component_test on marlin.
Change-Id: I9ee8834053fda79a4fd77bfa83eab3cc51a90dff
Add hash_tree_info to represent the hash tree computation arguments in
the transfer commands 'compute_hash_tree'. Also add its parsing code in
the Command class.
Bug: 25170618
Test: unit tests pass
Change-Id: Ie8607968377968e8fb3e58d1af0b8ca315e145be
recovery is_battery_ok function uses get_health_service(),
which calls IHealth::getService("default") then
IHealth::getService("backup").
- An OEM can provide the default instance by installing
android.hardware.health@2.0-impl-<device>.so to recovery
partition.
- If that's not found, the "backup" instance is provided
to the recovery partition by default.
Test: call is_battery_ok() in recovery, successfully
get battery information.
Bug: 80132328
Change-Id: Ibfee80636325a07bc20b24d044d007a60b3dd7c2
This relands the previously reverted CL in commit
c70446ce7b ("Build and use minadbd as a
shared library."). `recovery` has been built with Soong, so the previous
concern (unintentionally installing `libminadbd_services.so` to normal
system image) no longer holds.
Note that `reocvery` can't use `libminadbd_services.a`, as functions
like `daemon_service_to_fd()` (needed by `libadbd.so`) won't be linked
into `recovery`.
This CL moves the dependency of `libminadbd_services` from `librecovery`
into `recovery`, as only the latter actually relies on it (via
`recovery_main.cpp`). Note that we no longer need to list the transitive
dependency on `libadbd` or `libasyncio`.
Bug: 112494634
Test: `mmma -j bootable/recovery`
Test: Build and boot into recovery with aosp_taimen-userdebug. Verify that
sideloading keeps working.
Test: `build/soong/build_test.bash --dist`
Change-Id: Ic086470b86d6770bede317e0f5534f608fa7b7d2
Test: mmma -j bootable/recovery
Test: Run recovery_unit_test and recovery_component_test on marlin.
Change-Id: I2617b87d13c585addf0ed2fbae8c3ce443ea7200
It doesn't change the functionality of the test, but allows easier
conversion to Soong.
Test: Run recovery_component_test on marlin.
Change-Id: Ic7419dc86c6b220531ae44e49ab4657394a5d7fa
This reverts commit 4fd4f89591.
The reverted CL has a side effect that unintentionally installs
a copy of libminadbd_services.so to the system image. This breaks the
check with `build/soong/build_test.bash --dist` which detects and
prevents vendor modules from installing files to the system image.
Prior to fully converting `recovery` to be built with Soong, we have to
statically link recovery-specific modules into `recovery`.
Bug: 110380063
Test: `m -j bootimage` with aosp_marlin-userdebug
Test: Run minadbd_test on marlin.
Test: Boot into recovery and verify that sideloading still works.
Test: `build/soong/build_test.bash --dist`
Change-Id: I290d55c82e17aa60a5afdf2ff7f896afc4dae8b3
The former `minadbd` module is now built as a shared library
(`libminadbd_services.so`) that serves sideloading under recovery, with
a dynamic dependency on `libadbd.so`.
This allows sharing and reusing libadbd code on device (both of `adbd`
and `recovery` now uses `libadbd.so`). As a result, it reduces the size
of `recovery` binary from 1407360-byte to (1272880 + 33032)-byte
(aosp_marlin-userdebug).
Bug: 78793464
Test: `m -j bootimage` with aosp_marlin-userdebug
Test: Run minadbd_test on marlin.
Test: Boot into recovery and verify that sideloading still works.
Change-Id: I6c2bc3d351d5af71220a9b9f956c8c039e52c781
We used to statically link the device-specific recovery UI extension
(`TARGET_RECOVERY_UI_LIB`) into `recovery`. Such a logic can't be easily
migrated to Soong, as modules specified by `TARGET_RECOVERY_UI_LIB` may
not be built with Soong.
Instead of porting all the device-specific codes over, this CL builds
and installs the UI lib as a shared library with Android.mk. `recovery`
dlopen(3)'s and dlsym(3)'s `make_device` to invoke the device-specific
UI lib on start.
Note that in order to make dlopen(3) actually working, we have to switch
`recovery` to be dynamically linked (we will make the move later
anyway).
Bug: 110380063
Test: Build and boot into marlin recovery image. Check that
device-specific recovery UI is successfully loaded.
Change-Id: Ia9861c7559a95f3f50676534540c0cb87cae4574
The new command is part of the transfer.list and allows us to compute the hash
tree on non-ab devices.
The required arguments for the hash_tree computation are:
hash_tree_ranges
source_ranges
hash_algorithm
salt_hex
root_hash
Bug: 25170618
Test: unit tests pass; run simulator with compute_hash_tree
Change-Id: I8ff0d582cc8adabb8a060db7845f38b35b28e62c
Normally calling a UI method will block
indefinitely until the UI is actually
used. This creates a method to interrupt
the UI, causing waitKey to return -2. This
in turn, will cause ShowMenu to return -2.
This allows switching between recovery and
fastbootd via usb commands.
Test: adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
Bug: 78793464
Change-Id: I4c6c9aa18d79070877841a5c9818acf723fa6096