content: /*
   * Copyright (C) 2018 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.
   */
  
  package com.android.server.rollback;
  
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.content.rollback.RollbackInfo;
  
  import java.io.File;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.text.ParseException;
  import java.time.Instant;
  import java.util.ArrayList;
  
  
  /**
   * Information about a rollback available for a set of atomically installed
   * packages.
   */
  class RollbackData {
      @IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
              ROLLBACK_STATE_ENABLING,
              ROLLBACK_STATE_AVAILABLE,
              ROLLBACK_STATE_COMMITTED,
      })
      @Retention(RetentionPolicy.SOURCE)
      @interface RollbackState {}
  
      /**
       * The rollback is in the process of being enabled. It is not yet
       * available for use.
       */
      static final int ROLLBACK_STATE_ENABLING = 0;
  
      /**
       * The rollback is currently available.
       */
      static final int ROLLBACK_STATE_AVAILABLE = 1;
  
      /**
       * The rollback has been committed.
       */
      static final int ROLLBACK_STATE_COMMITTED = 3;
  
      /**
       * The rollback info for this rollback.
       */
      public final RollbackInfo info;
  
      /**
       * The directory where the rollback data is stored.
       */
      public final File backupDir;
  
      /**
       * The time when the upgrade occurred, for purposes of expiring
       * rollback data.
       *
       * The timestamp is not applicable for all rollback states, but we make
       * sure to keep it non-null to avoid potential errors there.
       */
      public @NonNull Instant timestamp;
  
      /**
       * The session ID for the staged session if this rollback data represents a staged session,
       * {@code -1} otherwise.
       */
      public final int stagedSessionId;
  
      /**
       * The current state of the rollback.
       * ENABLING, AVAILABLE, or COMMITTED.
       */
      public @RollbackState int state;
  
      /**
       * The id of the post-reboot apk session for a staged install, if any.
       */
      public int apkSessionId = -1;
  
      /**
       * True if we are expecting the package manager to call restoreUserData
       * for this rollback because it has just been committed but the rollback
       * has not yet been fully applied.
       */
      // NOTE: All accesses to this field are from the RollbackManager handler thread.
      public boolean restoreUserDataInProgress = false;
  
      /**
       * Constructs a new, empty RollbackData instance.
       *
       * @param rollbackId the id of the rollback.
       * @param backupDir the directory where the rollback data is stored.
       * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
       */
      RollbackData(int rollbackId, File backupDir, int stagedSessionId) {
          this.info = new RollbackInfo(rollbackId,
                  /* packages */ new ArrayList<>(),
                  /* isStaged */ stagedSessionId != -1,
                  /* causePackages */ new ArrayList<>(),
                  /* committedSessionId */ -1);
          this.backupDir = backupDir;
          this.stagedSessionId = stagedSessionId;
          this.state = ROLLBACK_STATE_ENABLING;
          this.timestamp = Instant.now();
      }
  
      /**
       * Constructs a RollbackData instance with full rollback data information.
       */
      RollbackData(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
              @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) {
          this.info = info;
          this.backupDir = backupDir;
          this.timestamp = timestamp;
          this.stagedSessionId = stagedSessionId;
          this.state = state;
          this.apkSessionId = apkSessionId;
          this.restoreUserDataInProgress = restoreUserDataInProgress;
      }
  
      /**
       * Whether the rollback is for rollback of a staged install.
       */
      public boolean isStaged() {
          return info.isStaged();
      }
  
      static String rollbackStateToString(@RollbackState int state) {
          switch (state) {
              case RollbackData.ROLLBACK_STATE_ENABLING: return "enabling";
              case RollbackData.ROLLBACK_STATE_AVAILABLE: return "available";
              case RollbackData.ROLLBACK_STATE_COMMITTED: return "committed";
          }
          throw new AssertionError("Invalid rollback state: " + state);
      }
  
      static @RollbackState int rollbackStateFromString(String state)
              throws ParseException {
          switch (state) {
              case "enabling": return RollbackData.ROLLBACK_STATE_ENABLING;
              case "available": return RollbackData.ROLLBACK_STATE_AVAILABLE;
              case "committed": return RollbackData.ROLLBACK_STATE_COMMITTED;
          }
          throw new ParseException("Invalid rollback state: " + state, 0);
      }
  
      public String getStateAsString() {
          return rollbackStateToString(state);
      }
  }
  