Apply-update: Script to apply updates to device

<Development workflow for any project in the platform and build with 'm' to create the images>

Build:

$m apply_update

$ apply_update --help

Update the device to the current build in $OUT:
$ apply_update

Update the device, but skip flashing static partitions (see --help):
$ apply_update --skip-static-partitions

Update the device and wipe user data:
$ apply_update --wipe

Performance:

a: Default update without any paramenters: Update takes ~30-40 seconds.
b: With --skip-static-partitions its < 20 seconds

Bug: 371304627
Test: Test development workflow:

1: m libsnapshot && m -j
   apply_update

2: m snapshotctl && m -j
   apply_update --wipe

3: m virtmgr && m -j
   apply_update --skip-static-partitions

4: m crosvm && m -j
   apply_update --skip-static-partitions

Change-Id: Iff2c774ef83bc67601cc978e2e3ceea355a5482f
Signed-off-by: Akilesh Kailash <akailash@google.com>
diff --git a/fs_mgr/libsnapshot/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
index 829f5bc..b99da93 100644
--- a/fs_mgr/libsnapshot/scripts/Android.bp
+++ b/fs_mgr/libsnapshot/scripts/Android.bp
@@ -29,3 +29,8 @@
         "snapshot_proto_python",
     ],
 }
+
+sh_binary_host {
+    name: "apply_update",
+    src: "apply-update.sh",
+}
diff --git a/fs_mgr/libsnapshot/scripts/apply-update.sh b/fs_mgr/libsnapshot/scripts/apply-update.sh
index 90b0119..0b10721 100755
--- a/fs_mgr/libsnapshot/scripts/apply-update.sh
+++ b/fs_mgr/libsnapshot/scripts/apply-update.sh
@@ -1,77 +1,220 @@
 #!/bin/bash
 
-# This is a debug script to quicky test end-to-end flow
-# of snapshot updates without going through update-engine.
+# Copyright 2024 Google Inc. All rights reserved.
 #
-# Usage:
+# 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
 #
-#  To update both dynamic and static partitions:
+#     http://www.apache.org/licenses/LICENSE-2.0
 #
-# ./system/core/fs_mgr/libsnapshot/apply_update.sh [--update-static-partitions] [--wipe]
-#
-# --update-static-partitions: This will update bootloader and static A/B
-# partitions
-# --wipe: Allows data wipe as part of update flow
-#
-#  To update dynamic partitions only (this should be used when static
-#  partitions are present in both the slots):
-#
-#  ./system/core/fs_mgr/libsnapshot/apply_update.sh
-#
-#
+# 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.
 
-rm -f $OUT/*.patch
+# apply_update.sh: Script to update the device in incremental way
 
-# Compare images and create snapshot patches. Currently, this
-# just compares two identical images in $OUT. In general, any source
-# and target images could be passed to create snapshot patches. However,
-# care must be taken to ensure source images are already present on the device.
-#
-# create_snapshot is a host side binary. Build it with `m create_snapshot`
-create_snapshot --source=$OUT/system.img --target=$OUT/system.img &
-create_snapshot --source=$OUT/product.img --target=$OUT/product.img &
-create_snapshot --source=$OUT/vendor.img --target=$OUT/vendor.img &
-create_snapshot --source=$OUT/system_ext.img --target=$OUT/system_ext.img &
-create_snapshot --source=$OUT/vendor_dlkm.img --target=$OUT/vendor_dlkm.img &
-create_snapshot --source=$OUT/system_dlkm.img --target=$OUT/system_dlkm.img &
+# Ensure OUT directory exists
+if [ -z "$OUT" ]; then
+  echo "Error: OUT environment variable not set." >&2
+  exit 1
+fi
 
-echo "Waiting for snapshot patch creation"
-wait $(jobs -p)
-echo "Snapshot patch creation completed"
+DEVICE_PATH="/data/verity-hash"
+HOST_PATH="$OUT/verity-hash"
 
-mv *.patch $OUT/
+# Create the log file path
+log_file="$HOST_PATH/snapshot.log"
+
+# Function to log messages to both console and log file
+log_message() {
+    message="$1"
+    echo "$message"  # Print to stdout
+    echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> "$log_file"  # Append to log file with timestamp
+}
+
+# Function to check for create_snapshot and build if needed
+ensure_create_snapshot() {
+  if ! command -v create_snapshot &> /dev/null; then
+    log_message "create_snapshot not found. Building..."
+    m create_snapshot
+    if [[ $? -ne 0 ]]; then
+      log_message "Error: Failed to build create_snapshot."
+      exit 1
+    fi
+  fi
+}
+
+ensure_create_snapshot
+
+# Function to flash static partitions
+flash_static_partitions() {
+  local wipe_flag="$1"
+
+  fastboot flash bootloader "$OUT"/bootloader.img
+  fastboot reboot bootloader
+  sleep 1
+  fastboot flash radio "$OUT"/radio.img
+  fastboot reboot bootloader
+  sleep 1
+  fastboot flashall --exclude-dynamic-partitions --disable-super-optimization --skip-reboot
+
+  if (( wipe_flag )); then
+      log_message "Wiping device..."
+      fastboot -w
+  fi
+  fastboot reboot
+}
+
+# Function to display the help message
+show_help() {
+  cat << EOF
+Usage: $0 [OPTIONS]
+
+This script updates an Android device with incremental flashing, optionally wiping data and flashing static partitions.
+
+Options:
+  --skip-static-partitions  Skip flashing static partitions (bootloader, radio, boot, vbmeta, dtbo and other static A/B partitions).
+                           * Requires manual update of static partitions on both A/B slots
+                             *before* using this flag.
+                           * Speeds up the update process and development iteration.
+                           * Ideal for development focused on the Android platform (AOSP,
+                             git_main).
+                           * Safe usage: First update static partitions on both slots, then
+                             use this flag for faster development iterations.
+                             Ex:
+                                1: Run this on both the slots - This will update the kernel and other static partitions:
+                                   $fastboot flashall --exclude-dynamic-partitions --disable-super-optimization --skip-reboot
+
+                                2: Update bootloader on both the slots:
+                                    $fastboot flash bootloader $OUT/bootloader.img --slot=all
+
+                                3: Update radio on both the slots:
+                                    $fastboot flash radio $OUT/radio.img --slot=all
+                            Now, the script can safely use this flag for update purpose.
+
+  --wipe                   Wipe user data during the update.
+  --help                   Display this help message.
+
+Environment Variables:
+  OUT                      Path to the directory containing build output.
+                           This is required for the script to function correctly.
+
+Examples:
+  <Development workflow for any project in the platform and build with 'm' to create the images>
+
+  Update the device:
+  $0
+
+  Update the device, but skip flashing static partitions (see above for the usage):
+  $0 --skip-static-partitions
+
+  Update the device and wipe user data:
+  $0 --wipe
+
+  Display this help message:
+  $0 --help
+EOF
+}
+
+skip_static_partitions=0
+wipe_flag=0
+help_flag=0
+
+# Parse arguments
+for arg in "$@"; do
+  case "$arg" in
+    --skip-static-partitions)
+      skip_static_partitions=1
+      ;;
+    --wipe)
+      wipe_flag=1
+      ;;
+    --help)
+      help_flag=1
+      ;;
+    *)
+      echo "Unknown argument: $arg" >&2
+      help_flag=1
+      ;;
+  esac
+done
+
+# Check if help flag is set
+if (( help_flag )); then
+  show_help
+  exit 0
+fi
+
+rm -rf $HOST_PATH
 
 adb root
 adb wait-for-device
-adb shell mkdir -p /data/update/
-adb push $OUT/*.patch /data/update/
 
-if [[ "$2" == "--wipe" ]]; then
-  adb shell snapshotctl apply-update /data/update/ -w
+adb shell rm -rf $DEVICE_PATH
+adb shell mkdir -p $DEVICE_PATH
+
+echo "Extracting device source hash from dynamic partitions"
+adb shell snapshotctl dump-verity-hash $DEVICE_PATH
+adb pull -q $DEVICE_PATH $OUT/
+
+log_message "Entering directory:"
+
+# Navigate to the verity-hash directory
+cd "$HOST_PATH" || { log_message "Error: Could not navigate to $HOST_PATH"; exit 1; }
+
+pwd
+
+# Iterate over all .pb files using a for loop
+for pb_file in *.pb; do
+  # Extract the base filename without the .pb extension
+  base_filename="${pb_file%.*}"
+
+  # Construct the source and target file names
+  source_file="$pb_file"
+  target_file="$OUT/$base_filename.img"
+
+  # Construct the create_snapshot command using an array
+  snapshot_args=(
+    "create_snapshot"
+    "--source" "$source_file"
+    "--target" "$target_file"
+    "--merkel_tree"
+  )
+
+  # Log the command about to be executed
+  log_message "Running: ${snapshot_args[*]}"
+
+  "${snapshot_args[@]}" >> "$log_file" 2>&1 &
+done
+
+log_message "Waiting for snapshot patch creation"
+
+# Wait for all background processes to complete
+wait $(jobs -p)
+
+log_message "Snapshot patches created successfully"
+
+adb push -q $HOST_PATH/*.patch $DEVICE_PATH
+
+log_message "Applying update"
+
+if (( wipe_flag )); then
+  adb shell snapshotctl apply-update $DEVICE_PATH -w
 else
-  adb shell snapshotctl apply-update /data/update/
+  adb shell snapshotctl apply-update $DEVICE_PATH
 fi
 
-# Check if the --update-static-partitions option is provided.
-# For quick developer workflow, there is no need to repeatedly
-# apply static partitions.
-if [[ "$1" == "--update-static-partitions" ]]; then
-  adb reboot bootloader
-  sleep 5
-  if [[ "$2" == "--wipe" ]]; then
-      fastboot -w
-  fi
-  fastboot flash bootloader $OUT/bootloader.img
-  sleep 1
-  fastboot reboot bootloader
-  sleep 1
-  fastboot flash radio $OUT/radio.img
-  sleep 1
-  fastboot reboot bootloader
-  sleep 1
-  fastboot flashall --exclude-dynamic-partitions --disable-super-optimization
+if (( skip_static_partitions )); then
+    log_message "Rebooting device - Skipping flashing static partitions"
+    adb reboot
 else
-  adb reboot
+    log_message "Rebooting device to bootloader"
+    adb reboot bootloader
+    log_message "Waiting to enter fastboot bootloader"
+    flash_static_partitions "$wipe_flag"
 fi
 
-echo "Update completed"
+log_message "Update completed"