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