Update Manager

The Update Manager carries out updates for its VM. The Update Manager coordinates with the client, which is supplied by the system integrator. The client provides the update payload and coordinates the overall vehicle update. The update consists of these steps:

  1. READY: No update is in progress.
  2. PREPARE: The Update Manager writes the update to the disk, but it doesn't make it active.
  3. ACTIVATE: The Update Manager makes the prepared update active so that it takes effect after a reboot.
  4. COMMIT: The Update Manager makes the update permanent.

A simplified state machine illustrating the workflow of the Update Manager

Figure 1. High-level operation of the Update Manager.

The Update Manager supports system updates and service bundle updates, which are described on this page.

System updates

The Android OS uses two sets of partitions called slots, with only one slot active at a time. A system update writes a new set of partitions to the inactive slot, and the system switches the active slot on the next reboot. For more information about building system updates, see A/B system updates.

Service bundle updates

In AAOS SDV, service bundles package services together within APEXes. Service bundle updates stage new APEX files in temporary sessions. The system activates these updated bundles after a system reboot. For more information about building service bundle APEXes, see Service bundle updates.

State machine

Requests from the client and events from system services drive the Update Manager state machine. The Update Manager is designed to be resilient, recovering its state and resuming the update process even after unexpected reboots or crashes.

A state machine illustrating the workflow of the Update Manager

Figure 2. Update Manager state machine.

API

The Update Manager provides an API that drives the update process through the state machine. The API uses VSIDL, which supports clients residing in any VM supporting SDV Comms and provides request failure notification.

Security and access

The system restricts API access using authorization policies. Only the authorized client can make calls to the Update Manager.

Status updates

Because update operations can be long-running, the Update Manager provides asynchronous status updates. Clients must monitor the state by subscribing to status updates.

Subscription management

  • Subscribing: Clients subscribe to status updates by calling the SubscribeToStatusUpdates() method:

    rpc SubscribeToStatusUpdates(SubscribeToStatusUpdatesRequest)
        returns (SubscribeToStatusUpdatesResponse) {}
    
    message SubscribeToStatusUpdatesRequest {}
    
    message SubscribeToStatusUpdatesResponse {}
    

    Before calling SubscribeToStatusUpdates(), the client must start an RPC interface that implements UpdateManagerListenerService.

  • Unsubscribing: Clients can stop receiving status updates by calling UnsubscribeFromStatusUpdates():

    rpc UnsubscribeFromStatusUpdates(UnsubscribeFromStatusUpdatesRequest)
        returns (UnsubscribeFromStatusUpdatesResponse) {}
    
    message UnsubscribeFromStatusUpdatesRequest {}
    
    message UnsubscribeFromStatusUpdatesResponse {}
    

UpdateManagerListenerService

Clients that call SubscribeToStatusUpdates() on UpdateManagerService must implement the following service to receive status updates:

service UpdateManagerListenerService {
  /* Called when there is a status update from the Update Manager. */
  rpc OnStatusUpdate(OnStatusUpdateRequest) returns (OnStatusUpdateResponse) {}
}

message OnStatusUpdateRequest {
  oneof status_update {
    UpdateManagerStatus update_manager_status = 1;
    UpdateProgress update_progress = 2;
  }
}

message OnStatusUpdateResponse {}

OnStatusUpdateRequest delivers one of two possible messages using the status_update field:

  1. UpdateManagerStatus: Sent when the Update Manager's state changes.
  2. UpdateProgress: Sent to indicate the update progress during the PREPARE and ACTIVATE_PRE_REBOOT states because the process can take a significant amount of time.

Status and error messages

The status messages include details about the state and any failures:

  • UpdateManagerStatus: Reports the current phase of the update and any reason for failure:

    message UpdateManagerStatus {
      /* The current state of the update */
      UpdateState update_state = 1;
      /* The reason for the failure, if the update failed */
      FailureReason failure_reason = 2;
    }
    
    enum UpdateState {
      /* The Update Manager is ready to accept a new update. */
      READY = 0;
      /* An update is being prepared. */
      PREPARE = 1;
      /* The update failed during the prepare step. Roll back the update to start a
       *   new update. */
      PREPARE_FAILURE = 2;
      /* The update was prepared successfully. */
      PREPARE_COMPLETE = 3;
      /* A rollback has started from the prepare step. When complete, transitions
       *   to READY. */
      PREPARE_ROLLBACK = 4;
      /* The prepared update is being activated. */
      ACTIVATE_PRE_REBOOT = 5;
      /* The update failed during the activate step before the reboot. Roll back the
       *   update to start a new update. */
      ACTIVATE_PRE_REBOOT_FAILURE = 6;
      /* The update was activated successfully. Reboot to complete activation. */
      ACTIVATE_PRE_REBOOT_COMPLETE = 7;
      /* A rollback has started from the activate pre-reboot step. When complete,
       *   transitions to READY. */
      ACTIVATE_PRE_REBOOT_ROLLBACK = 8;
      /* An update was successfully activated after the reboot. The update needs to
       *   be committed or rolled back to be completed. */
      ACTIVATE_POST_REBOOT_COMPLETE = 9;
      /* A rollback has started from the activate post-reboot step. */
      ACTIVATE_POST_REBOOT_ROLLBACK = 10;
      /* The rollback was completed successfully. Reboot to complete the
       *   rollback. */
      ACTIVATE_POST_REBOOT_ROLLBACK_COMPLETE = 11;
      /* The update is being committed. When complete, transitions to READY */
      COMMIT = 12;
      /* The prepare request is being cancelled */
      PREPARE_CANCEL = 13;
      /* The system update has started suspending */
      PREPARE_SUSPEND = 14;
      /* The system update has finished suspending */
      PREPARE_SUSPEND_COMPLETE = 15;
    }
    
    message FailureReason {
      /* The original error that triggered the failure */
      ErrorCode error_code = 1;
      /* Binder error message for most ErrorCodes. */
      string error_message = 2;
    }
    
    enum ErrorCode {
      Unspecified = 0;
      /* Error from UpdateEngine service */
      UpdateEngineError = 1;
      // Error from the ApexService
      ApexServiceError = 2;
      // Error from the BootControlService
      BootControlServiceError = 3;
      // Error from the VoldService
      VoldServiceError = 4;
    }
    
  • UpdateProgress: Reports the current status from Update Engine during system updates:

    message UpdateProgress {
      /* Matches the enum UpdateStatus from the UpdateEngine service. */
      int32 status_code = 1;
    
      /* A value ranging from 0.0 to 1.0, 1.0 representing the process is complete. */
      float percentage = 2;
    }
    
    

Get immediate status

You can retrieve the current state and payload at any time by calling GetStatus(). The SystemUpdatePayload and ServiceBundleUpdatePayload messages are described in Prepare.

rpc GetStatus(GetStatusRequest) returns (GetStatusResponse) {}

message GetStatusRequest {}

message GetStatusResponse {
  /* The status of the Update Manager */
  UpdateManagerStatus update_manager_status = 1;
  /* The current update payload, if one has been set through the Prepare() request */
  oneof payload {
    SystemUpdatePayload system_update_payload = 2;
    ServiceBundleUpdatePayload service_bundle_update_payload = 3;
  }
}

Prepare

The Prepare request starts the update process by staging the payload, but the system waits to activate the update:

rpc Prepare(PrepareRequest) returns (PrepareResponse) {}

message PrepareRequest {
  oneof payload {
    SystemUpdatePayload system_update_payload = 1;
    ServiceBundleUpdatePayload service_bundle_update_payload = 2;
  }
}

message PrepareResponse {}

If the request is valid, the state transitions to PREPARE, and the Update Manager starts the update.

The type of the payload determines the update type.

System update payload

A system update uses the SystemUpdatePayload data, which requires a path to an over-the-air (OTA) update image. The other parameters are parsed from the image, but you can specify them to override the parsed values.

message SystemUpdatePayload {
  /* Absolute path to the update image (e.g., /updates/ota_update.zip) */
  string payload = 1;

  /* Offset (in bytes) into the payload where the payload binary starts. */
  optional int64 payload_offset = 2;

  /* The amount of data (in bytes) to read from "payload" as the update payload. */
  optional int64 payload_size = 3;

  /* The header key value pairs to apply with the payload. */
  map<string, string> header_key_value_pairs = 4;
}

The Update Manager processes the OTA image (provided by the client in the /data/ota_package directory) to run post-install steps, but it doesn't switch the active boot slot. It then transitions to the PREPARE_COMPLETE state on success or the PREPARE_FAILURE state on failure.

System update suspension

You can pause the update process for system updates only. This request is valid only when in the PREPARE state.

  • Suspend: Pauses the update, transitioning the state through PREPARE_SUSPEND.

    rpc Suspend(SuspendRequest) returns (SuspendResponse) {}
    
    message SuspendRequest {}
    
    message SuspendResponse {}
    

    On success, the state transitions to PREPARE_SUSPEND_COMPLETE. On failure, the state transitions to PREPARE_FAILURE.

  • Resume: Continues the paused update. This is valid only in the PREPARE_SUSPEND_COMPLETE state.

    rpc Resume(ResumeRequest) returns (ResumeResponse) {}
    
    message ResumeRequest {}
    
    message ResumeResponse {}
    

    The state transitions back to the PREPARE state.

Service bundle update payload

A service bundle update uses the ServiceBundleUpdatePayload, which contains the paths to the APEX files and sets the activation attempt limit.

message ServiceBundleUpdatePayload {
  /* Absolute path to an .apex file. Multiple .apex files can be included. */
  repeated string apex_path = 1;

  /* Number of boots to attempt activation before aborting. Must be > 0. */
  int32 boot_attempts = 2;
}

The Update Manager verifies that the APEX files are signed correctly and stages them in a session. On success, the state transitions to PREPARE_COMPLETE. On failure, the state transitions to PREPARE_FAILURE.

Activate

The Activate request activates the prepared update on the next reboot.

rpc Activate(ActivateRequest) returns (ActivateResponse) {}

message ActivateRequest {}

message ActivateResponse {}

Pre-reboot

The Update Manager transitions to the ACTIVATE_PRE_REBOOT state while activating the update. The Update Manager also creates a user data checkpoint that takes effect when the system is rebooted.

The activation actions differ depending on the update type:

  • System update: The system runs post-install steps and sets the boot slot to switch on the next reboot.
  • Service bundle update: The system marks the staged APEX session as ready for activation.

If the system activates the update successfully, the state transitions to ACTIVATE_PRE_REBOOT_COMPLETE, which indicates that the VM is ready for reboot. Otherwise, the state transitions to ACTIVATE_PRE_REBOOT_FAILURE.

Post-reboot

After the reboot, the system starts in checkpoint mode. In this mode, the system doesn't commit any changes to the user data partition to permanent storage. If the system doesn't commit the update successfully, it reverts these changes to the initial state.

Boot limit

The system reverts the changes to the user data partition in either of these conditions:

  • The client sends an explicit Rollback() request and reboots the system.
  • The system boots too many times without the client calling Commit() or Rollback(). The boot limit differs for each update type:
    • System update: The BootControl implementation sets this. For example, the default limit in the implementation used by Cuttlefish devices is seven boots.
    • Service bundle update: The boot_attempts field of the ServiceBundleUpdatePayload provided by the client sets this when the Prepare() request is made.
Verification

The system then verifies the update:

  • System update: dm-verity checks the updated slot for corruption.
  • Service bundle update: apexd checks each APEX's signature and mounts the APEX's image to the file system.

If verification succeeds, the state transitions to ACTIVATE_POST_REBOOT_COMPLETE. The Update Manager waits for either Commit() or Rollback().

If verification fails, the system reboots. When the system reaches the boot limit, the system reverts to the original state:

  • System update: The system reverts to the original slot.
  • Service bundle update: The updated APEXes are dropped, and the original APEXes are reactivated.

If verification fails for any update type, the state transitions to ACTIVATE_PRE_REBOOT_FAILURE after the original state is restored.

Rollback

The Rollback request starts returning the system to its pre-update state.

rpc Rollback(RollbackRequest) returns (RollbackResponse) {}

message RollbackRequest {}

message RollbackResponse {}

You can make the Rollback() request during many states. Depending on the initial state, the system takes different actions, and different states follow:

State when rollback is issued State transition System action
(System update only) PREPARE, PREPARE_SUSPEND_COMPLETE PREPARE_CANCELPREPARE_ROLLBACKREADY System update: Cancels the system update.
PREPARE_FAILURE, PREPARE_COMPLETE PREPARE_ROLLBACKREADY System update: Cancels the system update.

Service bundle update: Aborts staged APEX session.
ACTIVATE_PRE_REBOOT_COMPLETE, ACTIVATE_PRE_REBOOT_FAILURE ACTIVATE_PRE_REBOOT_ROLLBACKREADY Disables the user data checkpoint.

System update: Cancels boot slot switch.

Service bundle update: Removes staged APEX.
ACTIVATE_POST_REBOOT_COMPLETE ACTIVATE_POST_REBOOT_ROLLBACKACTIVATE_POST_REBOOT_ROLLBACK_COMPLETE Disables the user data checkpoint.

System update: Switches the boot slot back.

Service bundle update: Reverts the active APEX sessions.

A final reboot is required for the rollback to take effect, after which the state transitions to READY.

Commit

This request indicates that the update completed successfully and applies all changes permanently.

rpc Commit(CommitRequest) returns (CommitResponse) {}

message CommitRequest {}

message CommitResponse {}

The state transitions to COMMIT while performing the commit actions:

  • System update: The Update Manager marks the boot as successful, which prevents future rollback and selects this slot on subsequent boots.
  • Service bundle update: The Update Manager marks the staged APEX session as successful, closing the update session and making the bundles permanently active.

After the system commits the update, the Update Manager commits the user data checkpoint. The system commits all new user data written during this boot to permanent storage. The Update Manager transitions back to READY, indicating that the update is complete.

Uninstall APEXes

The UninstallApex request removes the /data version of service bundles. You can call UninstallApex during any state, but the VM must reboot for the uninstall to take effect. Therefore, avoid calling UninstallApex while an update is in progress. UninstallApex doesn't remove preinstalled APEXes, wipe APEX data, or remove inactive APEX versions.

rpc UninstallApex(UninstallApexRequest) returns (UninstallApexResponse) {}

message UninstallApexRequest {
  message ApexInfo {
    string module_name = 1;
    int64 version_code = 2;
  }
  repeated ApexInfo apexes = 1;
}

message UninstallApexResponse {}