From ffed57a7a3e5e82969b5998f759d09fbb6edd4e2 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 23 Apr 2018 17:51:45 -0700 Subject: [PATCH 01/17] Dump debug information for apply_patch unit tests The apply patch test should have a deterministic way to append patch data. Add debug logs to dump the length and SHA1 of each step to further track down the flakiness. Also redirect the debug logging to stdout in case the logcat becomes too chatty. Bug: 67849209 Test: Run recovery_component_test Change-Id: I42bafef2d9dee599719ae57840b3d8c00d243ebd --- applypatch/applypatch.cpp | 5 +++++ applypatch/imgpatch.cpp | 5 +++++ tests/component/applypatch_test.cpp | 13 ++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 7104abd6..db7530be 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -630,6 +630,11 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr SHA_CTX ctx; SHA1_Init(&ctx); SinkFn sink = [&memory_sink_str, &ctx](const unsigned char* data, size_t len) { + if (len != 0) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(data, len, digest); + LOG(DEBUG) << "Appending " << len << " bytes data, sha1: " << short_sha1(digest); + } SHA1_Update(&ctx, data, len); memory_sink_str.append(reinterpret_cast(data), len); return len; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 2e4faaad..9794a487 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -182,6 +182,8 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& printf("Failed to apply bsdiff patch.\n"); return -1; } + + LOG(DEBUG) << "Processed chunk type normal"; } else if (type == CHUNK_RAW) { const char* raw_header = patch_header + pos; pos += 4; @@ -201,6 +203,8 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& return -1; } pos += data_len; + + LOG(DEBUG) << "Processed chunk type raw"; } else if (type == CHUNK_DEFLATE) { // deflate chunks have an additional 60 bytes in their chunk header. const char* deflate_header = patch_header + pos; @@ -276,6 +280,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& return -1; } + LOG(DEBUG) << "Processed chunk type deflate"; } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index f19f28c6..292d76e4 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -46,7 +47,7 @@ using namespace std::string_literals; static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { - ASSERT_NE(nullptr, sha1); + ASSERT_TRUE(sha1 != nullptr); std::string data; ASSERT_TRUE(android::base::ReadFileToString(fname, &data)); @@ -68,6 +69,14 @@ static void mangle_file(const std::string& fname) { ASSERT_TRUE(android::base::WriteStringToFile(content, fname)); } +static void test_logger(android::base::LogId /* id */, android::base::LogSeverity severity, + const char* /* tag */, const char* /* file */, unsigned int /* line */, + const char* message) { + if (severity >= android::base::GetMinimumLogSeverity()) { + fprintf(stdout, "%s\n", message); + } +} + class ApplyPatchTest : public ::testing::Test { public: virtual void SetUp() override { @@ -109,6 +118,8 @@ class ApplyPatchModesTest : public ::testing::Test { protected: void SetUp() override { CacheLocation::location().set_cache_temp_source(cache_source.path); + android::base::InitLogging(nullptr, &test_logger); + android::base::SetMinimumLogSeverity(android::base::LogSeverity::DEBUG); } TemporaryFile cache_source; From 3b4d516151c622b4015fb14af4ea3223ed57f2e0 Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Wed, 8 Jun 2016 13:51:41 -0700 Subject: [PATCH 02/17] Wipe the metadata partition when we wipe data. Bug: 78469201 Test: Wipe from recovery menu, check that wipe is logged correctly and boot works as expected. Change-Id: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 --- recovery.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 910cd829..ceb19413 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -108,6 +108,7 @@ static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe"; static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; static const char *DATA_ROOT = "/data"; +static const char* METADATA_ROOT = "/metadata"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; @@ -752,11 +753,19 @@ static bool wipe_data(Device* device) { modified_flash = true; ui->Print("\n-- Wiping data...\n"); - bool success = - device->PreWipeData() && - erase_volume("/data") && - (has_cache ? erase_volume("/cache") : true) && - device->PostWipeData(); + bool success = device->PreWipeData(); + if (success) { + success &= erase_volume(DATA_ROOT); + if (has_cache) { + success &= erase_volume(CACHE_ROOT); + } + if (volume_for_mount_point(METADATA_ROOT) != nullptr) { + success &= erase_volume(METADATA_ROOT); + } + } + if (success) { + success &= device->PostWipeData(); + } ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; } From 188f3a5bf8ff0f28e12a5977b7b21200f088aca7 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 24 Apr 2018 14:15:14 -0700 Subject: [PATCH 03/17] updater: Remove an unneeded strdup(). StringValue() makes a copy of the arg. Test: mmma bootable/recovery Change-Id: I1002994e7496c840aa05c785019bdeca281f467e --- updater/blockimg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index e72ddd31..e7d213a7 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1697,7 +1697,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, for (size_t i = 0; i < cmdcount; ++i) { if (cmd_map.find(commands[i].name) != cmd_map.end()) { LOG(ERROR) << "Error: command [" << commands[i].name << "] already exists in the cmd map."; - return StringValue(strdup("")); + return StringValue(""); } cmd_map[commands[i].name] = &commands[i]; } From 723056a83f8c8b15af02d9c302862dbb2304ea8c Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Wed, 8 Jun 2016 13:51:41 -0700 Subject: [PATCH 04/17] Wipe the metadata partition when we wipe data. Bug: 78469201 Test: Wipe from recovery menu, check that wipe is logged correctly and boot works as expected. Merged-In: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 Change-Id: I5bc8ef1b83d78de8b5edba6cc17882edcc744356 --- recovery.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index d887d07f..07ec5cfb 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -108,6 +108,7 @@ static const char *CONVERT_FBE_DIR = "/tmp/convert_fbe"; static const char *CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; static const char *DATA_ROOT = "/data"; +static const char* METADATA_ROOT = "/metadata"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; @@ -752,11 +753,19 @@ static bool wipe_data(Device* device) { modified_flash = true; ui->Print("\n-- Wiping data...\n"); - bool success = - device->PreWipeData() && - erase_volume("/data") && - (has_cache ? erase_volume("/cache") : true) && - device->PostWipeData(); + bool success = device->PreWipeData(); + if (success) { + success &= erase_volume(DATA_ROOT); + if (has_cache) { + success &= erase_volume(CACHE_ROOT); + } + if (volume_for_mount_point(METADATA_ROOT) != nullptr) { + success &= erase_volume(METADATA_ROOT); + } + } + if (success) { + success &= device->PostWipeData(); + } ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; } From f4d280ce40ba0aa50d6b2c70d8031ddf7678d179 Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Tue, 17 Apr 2018 13:20:22 -0700 Subject: [PATCH 05/17] sample_updater: add non-streaming demo SampleUpdater app was tested manually on a device. There are unit tests for utility classes. SampleUpdater app demonstrates how to use Android Update Engine to apply A/B (seamless) update. This CL contains demo of non-stream update using async update_engine, which is accessed directly from an activity. This app also shows logs from update_engine on the UI. Instructions can be found in `README.md`. - Create a UI with list of configs, current version, control buttons and a progress bar - Add PayloadSpec and PayloadSpecs for working with update zip file - Add UpdateConfig for working with json config files - Add applying non-streaming update Test: tested manually and unit tests for utilities Change-Id: I05d4a46ad9cf8b334c9c60c7dd4da486dac0400a Signed-off-by: Zhomart Mukhamejanov --- sample_updater/.gitignore | 9 + sample_updater/Android.mk | 11 +- sample_updater/AndroidManifest.xml | 27 +- sample_updater/README.md | 73 +++- sample_updater/res/layout/activity_main.xml | 171 +++++++++- .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes sample_updater/res/raw/sample.json | 22 ++ sample_updater/res/values/strings.xml | 21 ++ .../update/ui/SystemUpdateActivity.java | 68 ---- .../systemupdatersample/PayloadSpec.java | 122 +++++++ .../systemupdatersample/UpdateConfig.java | 183 ++++++++++ .../systemupdatersample/ui/MainActivity.java | 314 ++++++++++++++++++ .../updates/AbNonStreamingUpdate.java | 52 +++ .../util/PackagePropertyFiles.java | 42 +++ .../util/PayloadSpecs.java | 117 +++++++ .../util/UpdateConfigs.java | 82 +++++ .../util/UpdateEngineErrorCodes.java | 84 +++++ .../util/UpdateEngineStatuses.java | 51 +++ sample_updater/tests/Android.mk | 32 ++ sample_updater/tests/AndroidManifest.xml | 31 ++ sample_updater/tests/build.properties | 1 + .../systemupdatersample/UpdateConfigTest.java | 79 +++++ .../ui/MainActivityTest.java | 48 +++ .../util/PayloadSpecsTest.java | 117 +++++++ .../util/UpdateConfigsTest.java | 63 ++++ 26 files changed, 1723 insertions(+), 97 deletions(-) create mode 100644 sample_updater/.gitignore create mode 100644 sample_updater/res/mipmap-hdpi/ic_launcher.png create mode 100644 sample_updater/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 sample_updater/res/raw/sample.json create mode 100644 sample_updater/res/values/strings.xml delete mode 100644 sample_updater/src/com/android/update/ui/SystemUpdateActivity.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/PayloadSpec.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/UpdateConfig.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/ui/MainActivity.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/updates/AbNonStreamingUpdate.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/util/PackagePropertyFiles.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/util/PayloadSpecs.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/util/UpdateConfigs.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/util/UpdateEngineErrorCodes.java create mode 100644 sample_updater/src/com/example/android/systemupdatersample/util/UpdateEngineStatuses.java create mode 100644 sample_updater/tests/Android.mk create mode 100644 sample_updater/tests/AndroidManifest.xml create mode 100644 sample_updater/tests/build.properties create mode 100644 sample_updater/tests/src/com/example/android/systemupdatersample/UpdateConfigTest.java create mode 100644 sample_updater/tests/src/com/example/android/systemupdatersample/ui/MainActivityTest.java create mode 100644 sample_updater/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java create mode 100644 sample_updater/tests/src/com/example/android/systemupdatersample/util/UpdateConfigsTest.java diff --git a/sample_updater/.gitignore b/sample_updater/.gitignore new file mode 100644 index 00000000..487263f7 --- /dev/null +++ b/sample_updater/.gitignore @@ -0,0 +1,9 @@ +*~ +*.bak +*.pyc +*.pyc-2.4 +Thumbs.db +*.iml +.idea/ +gen/ +.vscode diff --git a/sample_updater/Android.mk b/sample_updater/Android.mk index 2b0fcbee..2786de44 100644 --- a/sample_updater/Android.mk +++ b/sample_updater/Android.mk @@ -15,13 +15,18 @@ # LOCAL_PATH := $(call my-dir) - include $(CLEAR_VARS) -LOCAL_PACKAGE_NAME := SystemUpdateApp +LOCAL_PACKAGE_NAME := SystemUpdaterSample LOCAL_SDK_VERSION := system_current -LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_TAGS := samples + +# TODO: enable proguard and use proguard.flags file +LOCAL_PROGUARD_ENABLED := disabled LOCAL_SRC_FILES := $(call all-java-files-under, src) include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/sample_updater/AndroidManifest.xml b/sample_updater/AndroidManifest.xml index 66414b5d..5bbb21c8 100644 --- a/sample_updater/AndroidManifest.xml +++ b/sample_updater/AndroidManifest.xml @@ -15,17 +15,22 @@ --> + package="com.example.android.systemupdatersample"> - - - - - - - - + + + + + + + + + + - diff --git a/sample_updater/README.md b/sample_updater/README.md index a06c52d4..467ef5d4 100644 --- a/sample_updater/README.md +++ b/sample_updater/README.md @@ -1 +1,72 @@ -# System update sample app. +# SystemUpdaterSample + +This app demonstrates how to use Android system updates APIs to install +[OTA updates](https://source.android.com/devices/tech/ota/). It contains a sample +client for `update_engine` to install A/B (seamless) updates and a sample of +applying non-A/B updates using `recovery`. + +A/B (seamless) update is available since Android Nougat (API 24), but this sample +targets the latest android. + + +## Running on a device + +The commands expected to be run from `$ANDROID_BUILD_TOP`. + +1. Compile the app `$ mmma bootable/recovery/sample_updater`. +2. Install the app to the device using `$ adb install `. +3. Add update config files. + + +## Update Config file + +Directory can be found in logs or on UI. Usually json config files are located in +`/data/user/0/com.example.android.systemupdatersample/files/configs/`. Example file +is located at `res/raw/sample.json`. + + +## Development + +- [x] Create a UI with list of configs, current version, + control buttons, progress bar and log viewer +- [x] Add `PayloadSpec` and `PayloadSpecs` for working with + update zip file +- [x] Add `UpdateConfig` for working with json config files +- [x] Add applying non-streaming update +- [ ] Add applying streaming update +- [ ] Prepare streaming update (partially downloading package) +- [ ] Add tests for `MainActivity` +- [ ] Add stop/reset the update +- [ ] Verify system partition checksum for package +- [ ] HAL compatibility check +- [ ] Change partition demo +- [ ] Add non-A/B updates demo + + +## Running tests + +1. Build `$ mmma bootable/recovery/sample_updater/` +2. Install app + `$ adb install $OUT/system/app/SystemUpdaterSample/SystemUpdaterSample.apk` +3. Install tests + `$ adb install $OUT/testcases/SystemUpdaterSampleTests/SystemUpdaterSampleTests.apk` +4. Run tests + `$ adb shell am instrument -w com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner` +5. Run a test file + ``` + $ adb shell am instrument \ + -w com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner \ + -c com.example.android.systemupdatersample.util.PayloadSpecsTest + ``` + + +## Getting access to `update_engine` API and read/write access to `/data` + +Run adb shell as a root, and set SELinux mode to permissive (0): + +```txt +$ adb root +$ adb shell +# setenforce 0 +# getenforce +``` diff --git a/sample_updater/res/layout/activity_main.xml b/sample_updater/res/layout/activity_main.xml index bd7d6867..3cd77210 100644 --- a/sample_updater/res/layout/activity_main.xml +++ b/sample_updater/res/layout/activity_main.xml @@ -1,20 +1,163 @@ - + 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. +--> + android:orientation="vertical" + android:padding="4dip" + android:gravity="center_horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + + + + + + + + + + + + + + + + + + +