1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.rollback;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresPermission;
22 import android.annotation.SystemApi;
23 import android.annotation.SystemService;
24 import android.annotation.TestApi;
25 import android.content.Context;
26 import android.content.IntentSender;
27 import android.content.pm.ParceledListSlice;
28 import android.content.pm.VersionedPackage;
29 import android.os.RemoteException;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import java.util.List;
34 
35 /**
36  * Offers the ability to rollback packages after upgrade.
37  * <p>
38  * For packages installed with rollbacks enabled, the RollbackManager can be
39  * used to initiate rollback of those packages for a limited time period after
40  * upgrade.
41  *
42  * @see PackageInstaller.SessionParams#setEnableRollback(boolean)
43  * @hide
44  */
45 @SystemApi
46 @SystemService(Context.ROLLBACK_SERVICE)
47 public final class RollbackManager {
48     private final String mCallerPackageName;
49     private final IRollbackManager mBinder;
50 
51     /**
52      * Lifetime duration of rollback packages in millis. A rollback will be available for
53      * at most that duration of time after a package is installed with
54      * {@link PackageInstaller.SessionParams#setEnableRollback(boolean)}.
55      *
56      * <p>If flag value is negative, the default value will be assigned.
57      *
58      * @see RollbackManager
59      *
60      * Flag type: {@code long}
61      * Namespace: NAMESPACE_ROLLBACK_BOOT
62      *
63      * @hide
64      */
65     @TestApi
66     public static final String PROPERTY_ROLLBACK_LIFETIME_MILLIS =
67             "rollback_lifetime_in_millis";
68 
69     /** {@hide} */
RollbackManager(Context context, IRollbackManager binder)70     public RollbackManager(Context context, IRollbackManager binder) {
71         mCallerPackageName = context.getPackageName();
72         mBinder = binder;
73     }
74 
75     /**
76      * Returns a list of all currently available rollbacks.
77      *
78      * @throws SecurityException if the caller does not have appropriate permissions.
79      */
80     @RequiresPermission(anyOf = {
81             android.Manifest.permission.MANAGE_ROLLBACKS,
82             android.Manifest.permission.TEST_MANAGE_ROLLBACKS
83     })
84     @NonNull
getAvailableRollbacks()85     public List<RollbackInfo> getAvailableRollbacks() {
86         try {
87             return mBinder.getAvailableRollbacks().getList();
88         } catch (RemoteException e) {
89             throw e.rethrowFromSystemServer();
90         }
91     }
92 
93     /**
94      * Gets the list of all recently committed rollbacks.
95      * This is for the purposes of preventing re-install of a bad version of a
96      * package and monitoring the status of a staged rollback.
97      * <p>
98      * Returns an empty list if there are no recently committed rollbacks.
99      * <p>
100      * To avoid having to keep around complete rollback history forever on a
101      * device, the returned list of rollbacks is only guaranteed to include
102      * rollbacks that are still relevant. A rollback is no longer considered
103      * relevant if the package is subsequently uninstalled or upgraded
104      * (without the possibility of rollback) to a higher version code than was
105      * rolled back from.
106      *
107      * @return the recently committed rollbacks
108      * @throws SecurityException if the caller does not have appropriate permissions.
109      */
110     @RequiresPermission(anyOf = {
111             android.Manifest.permission.MANAGE_ROLLBACKS,
112             android.Manifest.permission.TEST_MANAGE_ROLLBACKS
113     })
getRecentlyCommittedRollbacks()114     public @NonNull List<RollbackInfo> getRecentlyCommittedRollbacks() {
115         try {
116             return mBinder.getRecentlyCommittedRollbacks().getList();
117         } catch (RemoteException e) {
118             throw e.rethrowFromSystemServer();
119         }
120     }
121 
122     /**
123      * Status of a rollback commit. Will be one of
124      * {@link #STATUS_SUCCESS}, {@link #STATUS_FAILURE},
125      * {@link #STATUS_FAILURE_ROLLBACK_UNAVAILABLE}, {@link #STATUS_FAILURE_INSTALL}
126      *
127      * @see Intent#getIntExtra(String, int)
128      */
129     public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
130 
131     /**
132      * Detailed string representation of the status, including raw details that
133      * are useful for debugging.
134      *
135      * @see Intent#getStringExtra(String)
136      */
137     public static final String EXTRA_STATUS_MESSAGE =
138             "android.content.rollback.extra.STATUS_MESSAGE";
139 
140     /**
141      * Status result of committing a rollback.
142      *
143      * @hide
144      */
145     @IntDef(prefix = "STATUS_", value = {
146             STATUS_SUCCESS,
147             STATUS_FAILURE,
148             STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
149             STATUS_FAILURE_INSTALL,
150     })
151     @Retention(RetentionPolicy.SOURCE)
152     public @interface Status {};
153 
154     /**
155      * The rollback was successfully committed.
156      */
157     public static final int STATUS_SUCCESS = 0;
158 
159     /**
160      * The rollback could not be committed due to some generic failure.
161      *
162      * @see #EXTRA_STATUS_MESSAGE
163      */
164     public static final int STATUS_FAILURE = 1;
165 
166     /**
167      * The rollback could not be committed because it was no longer available.
168      *
169      * @see #EXTRA_STATUS_MESSAGE
170      */
171     public static final int STATUS_FAILURE_ROLLBACK_UNAVAILABLE = 2;
172 
173     /**
174      * The rollback failed to install successfully.
175      *
176      * @see #EXTRA_STATUS_MESSAGE
177      */
178     public static final int STATUS_FAILURE_INSTALL = 3;
179 
180     /**
181      * Commit the rollback with given id, rolling back all versions of the
182      * packages to the last good versions previously installed on the device
183      * as specified in the corresponding RollbackInfo object. The
184      * rollback will fail if any of the installed packages or available
185      * rollbacks are inconsistent with the versions specified in the given
186      * rollback object, which can happen if a package has been updated or a
187      * rollback expired since the rollback object was retrieved from
188      * {@link #getAvailableRollbacks()}.
189      *
190      * @param rollbackId ID of the rollback to commit
191      * @param causePackages package versions to record as the motivation for this
192      *                      rollback.
193      * @param statusReceiver where to deliver the results. Intents sent to
194      *                       this receiver contain {@link #EXTRA_STATUS}
195      *                       and {@link #EXTRA_STATUS_MESSAGE}.
196      * @throws SecurityException if the caller does not have appropriate permissions.
197      */
198     @RequiresPermission(anyOf = {
199             android.Manifest.permission.MANAGE_ROLLBACKS,
200             android.Manifest.permission.TEST_MANAGE_ROLLBACKS
201     })
commitRollback(int rollbackId, @NonNull List<VersionedPackage> causePackages, @NonNull IntentSender statusReceiver)202     public void commitRollback(int rollbackId, @NonNull List<VersionedPackage> causePackages,
203             @NonNull IntentSender statusReceiver) {
204         try {
205             mBinder.commitRollback(rollbackId, new ParceledListSlice(causePackages),
206                     mCallerPackageName, statusReceiver);
207         } catch (RemoteException e) {
208             throw e.rethrowFromSystemServer();
209         }
210     }
211 
212     /**
213      * Reload all persisted rollback data from device storage.
214      * This API is meant to test that rollback state is properly preserved
215      * across device reboot, by simulating what happens on reboot without
216      * actually rebooting the device.
217      *
218      * Note rollbacks in the process of enabling will be lost after calling
219      * this method since they are not persisted yet. Don't call this method
220      * in the middle of the install process.
221      *
222      * @throws SecurityException if the caller does not have appropriate permissions.
223      *
224      * @hide
225      */
226     @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
227     @TestApi
reloadPersistedData()228     public void reloadPersistedData() {
229         try {
230             mBinder.reloadPersistedData();
231         } catch (RemoteException e) {
232             throw e.rethrowFromSystemServer();
233         }
234     }
235 
236     /**
237      * Expire the rollback data for a given package.
238      * This API is meant to facilitate testing of rollback logic for
239      * expiring rollback data. Removes rollback data for available and
240      * recently committed rollbacks that contain the given package.
241      *
242      * @param packageName the name of the package to expire data for.
243      * @throws SecurityException if the caller does not have appropriate permissions.
244      *
245      * @hide
246      */
247     @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
248     @TestApi
expireRollbackForPackage(@onNull String packageName)249     public void expireRollbackForPackage(@NonNull String packageName) {
250         try {
251             mBinder.expireRollbackForPackage(packageName);
252         } catch (RemoteException e) {
253             throw e.rethrowFromSystemServer();
254         }
255     }
256 
257     /**
258      * Block the RollbackManager for a specified amount of time.
259      * This API is meant to facilitate testing of race conditions in
260      * RollbackManager. Blocks RollbackManager from processing anything for
261      * the given number of milliseconds.
262      *
263      * @param millis number of milliseconds to block the RollbackManager for
264      * @throws SecurityException if the caller does not have appropriate permissions.
265      *
266      * @hide
267      */
268     @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS)
269     @TestApi
blockRollbackManager(long millis)270     public void blockRollbackManager(long millis) {
271         try {
272             mBinder.blockRollbackManager(millis);
273         } catch (RemoteException e) {
274             throw e.rethrowFromSystemServer();
275         }
276     }
277 }
278