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 android.content.rollback;
  
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.annotation.RequiresPermission;
  import android.annotation.SystemApi;
  import android.annotation.SystemService;
  import android.annotation.TestApi;
  import android.content.Context;
  import android.content.IntentSender;
  import android.content.pm.PackageInstaller;
  import android.content.pm.ParceledListSlice;
  import android.content.pm.VersionedPackage;
  import android.os.RemoteException;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.List;
  
  /**
   * Offers the ability to rollback packages after upgrade.
   * <p>
   * For packages installed with rollbacks enabled, the RollbackManager can be
   * used to initiate rollback of those packages for a limited time period after
   * upgrade.
   *
   * @see PackageInstaller.SessionParams#setEnableRollback(boolean)
   * @hide
   */
  @SystemApi @TestApi
  @SystemService(Context.ROLLBACK_SERVICE)
  public final class RollbackManager {
      private final String mCallerPackageName;
      private final IRollbackManager mBinder;
  
      /**
       * Lifetime duration of rollback packages in millis. A rollback will be available for
       * at most that duration of time after a package is installed with
       * {@link PackageInstaller.SessionParams#setEnableRollback(boolean)}.
       *
       * <p>If flag value is negative, the default value will be assigned.
       *
       * @see RollbackManager
       *
       * Flag type: {@code long}
       * Namespace: NAMESPACE_ROLLBACK_BOOT
       *
       * @hide
       */
      @TestApi
      public static final String PROPERTY_ROLLBACK_LIFETIME_MILLIS =
              "rollback_lifetime_in_millis";
  
      /** {@hide} */
      public RollbackManager(Context context, IRollbackManager binder) {
          mCallerPackageName = context.getPackageName();
          mBinder = binder;
      }
  
      /**
       * Returns a list of all currently available rollbacks.
       *
       * @throws SecurityException if the caller does not have appropriate permissions.
       */
      @RequiresPermission(anyOf = {
              android.Manifest.permission.MANAGE_ROLLBACKS,
              android.Manifest.permission.TEST_MANAGE_ROLLBACKS
      })
      @NonNull
      public List<RollbackInfo> getAvailableRollbacks() {
          try {
              return mBinder.getAvailableRollbacks().getList();
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Gets the list of all recently committed rollbacks.
       * This is for the purposes of preventing re-install of a bad version of a
       * package and monitoring the status of a staged rollback.
       * <p>
       * Returns an empty list if there are no recently committed rollbacks.
       * <p>
       * To avoid having to keep around complete rollback history forever on a
       * device, the returned list of rollbacks is only guaranteed to include
       * rollbacks that are still relevant. A rollback is no longer considered
       * relevant if the package is subsequently uninstalled or upgraded
       * (without the possibility of rollback) to a higher version code than was
       * rolled back from.
       *
       * @return the recently committed rollbacks
       * @throws SecurityException if the caller does not have appropriate permissions.
       */
      @RequiresPermission(anyOf = {
              android.Manifest.permission.MANAGE_ROLLBACKS,
              android.Manifest.permission.TEST_MANAGE_ROLLBACKS
      })
      public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() {
          try {
              return mBinder.getRecentlyExecutedRollbacks().getList();
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Status of a rollback commit. Will be one of
       * {@link #STATUS_SUCCESS}, {@link #STATUS_FAILURE},
       * {@link #STATUS_FAILURE_ROLLBACK_UNAVAILABLE}, {@link #STATUS_FAILURE_INSTALL}
       *
       * @see Intent#getIntExtra(String, int)
       */
      public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
  
      /**
       * Detailed string representation of the status, including raw details that
       * are useful for debugging.
       *
       * @see Intent#getStringExtra(String)
       */
      public static final String EXTRA_STATUS_MESSAGE =
              "android.content.rollback.extra.STATUS_MESSAGE";
  
      /**
       * Status result of committing a rollback.
       *
       * @hide
       */
      @IntDef(prefix = "STATUS_", value = {
              STATUS_SUCCESS,
              STATUS_FAILURE,
              STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
              STATUS_FAILURE_INSTALL,
      })
      @Retention(RetentionPolicy.SOURCE)
      public @interface Status {};
  
      /**
       * The rollback was successfully committed.
       */
      public static final int STATUS_SUCCESS = 0;
  
      /**
       * The rollback could not be committed due to some generic failure.
       *
       * @see #EXTRA_STATUS_MESSAGE
       */
      public static final int STATUS_FAILURE = 1;
  
      /**
       * The rollback could not be committed because it was no longer available.
       *
       * @see #EXTRA_STATUS_MESSAGE
       */
      public static final int STATUS_FAILURE_ROLLBACK_UNAVAILABLE = 2;
  
      /**
       * The rollback failed to install successfully.
       *
       * @see #EXTRA_STATUS_MESSAGE
       */
      public static final int STATUS_FAILURE_INSTALL = 3;
  
      /**
       * Commit the rollback with given id, rolling back all versions of the
       * packages to the last good versions previously installed on the device
       * as specified in the corresponding RollbackInfo object. The
       * rollback will fail if any of the installed packages or available
       * rollbacks are inconsistent with the versions specified in the given
       * rollback object, which can happen if a package has been updated or a
       * rollback expired since the rollback object was retrieved from
       * {@link #getAvailableRollbacks()}.
       *
       * @param rollbackId ID of the rollback to commit
       * @param causePackages package versions to record as the motivation for this
       *                      rollback.
       * @param statusReceiver where to deliver the results. Intents sent to
       *                       this receiver contain {@link #EXTRA_STATUS}
       *                       and {@link #EXTRA_STATUS_MESSAGE}.
       * @throws SecurityException if the caller does not have appropriate permissions.
       */
      @RequiresPermission(anyOf = {
              android.Manifest.permission.MANAGE_ROLLBACKS,
              android.Manifest.permission.TEST_MANAGE_ROLLBACKS
      })
      public void commitRollback(int rollbackId, @NonNull List<VersionedPackage> causePackages,
              @NonNull IntentSender statusReceiver) {
          try {
              mBinder.commitRollback(rollbackId, new ParceledListSlice(causePackages),
                      mCallerPackageName, statusReceiver);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Reload all persisted rollback data from device storage.
       * This API is meant to test that rollback state is properly preserved
       * across device reboot, by simulating what happens on reboot without
       * actually rebooting the device.
       *
       * @throws SecurityException if the caller does not have appropriate permissions.
       *
       * @hide
       */
      @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
      @TestApi
      public void reloadPersistedData() {
          try {
              mBinder.reloadPersistedData();
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Expire the rollback data for a given package.
       * This API is meant to facilitate testing of rollback logic for
       * expiring rollback data. Removes rollback data for available and
       * recently committed rollbacks that contain the given package.
       *
       * @param packageName the name of the package to expire data for.
       * @throws SecurityException if the caller does not have appropriate permissions.
       *
       * @hide
       */
      @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
      @TestApi
      public void expireRollbackForPackage(@NonNull String packageName) {
          try {
              mBinder.expireRollbackForPackage(packageName);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  }
  