• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.android.server.backup.restore;
18 
19 import static com.android.server.backup.BackupManagerService.DEBUG;
20 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
21 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS;
22 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.backup.BackupAgent;
27 import android.app.backup.BackupAnnotations.BackupDestination;
28 import android.app.backup.IBackupManagerMonitor;
29 import android.app.backup.IRestoreObserver;
30 import android.app.backup.IRestoreSession;
31 import android.app.backup.RestoreSet;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.content.pm.PackageManagerInternal;
36 import android.os.Binder;
37 import android.os.Handler;
38 import android.os.Message;
39 import android.util.Slog;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.server.LocalServices;
43 import com.android.server.backup.BackupWakeLock;
44 import com.android.server.backup.Flags;
45 import com.android.server.backup.TransportManager;
46 import com.android.server.backup.UserBackupManagerService;
47 import com.android.server.backup.internal.OnTaskFinishedListener;
48 import com.android.server.backup.params.RestoreGetSetsParams;
49 import com.android.server.backup.params.RestoreParams;
50 import com.android.server.backup.transport.TransportConnection;
51 import com.android.server.backup.utils.BackupEligibilityRules;
52 
53 import java.util.List;
54 import java.util.function.BiFunction;
55 
56 /**
57  * Restore session.
58  */
59 public class ActiveRestoreSession extends IRestoreSession.Stub {
60     private static final String TAG = "RestoreSession";
61     private static final String DEVICE_NAME_FOR_D2D_SET = "D2D";
62 
63     private final TransportManager mTransportManager;
64     private final String mTransportName;
65     private final UserBackupManagerService mBackupManagerService;
66     private final int mUserId;
67     private final BackupEligibilityRules mBackupEligibilityRules;
68     @Nullable private final String mPackageName;
69     public List<RestoreSet> mRestoreSets = null;
70     boolean mEnded = false;
71     boolean mTimedOut = false;
72 
ActiveRestoreSession( UserBackupManagerService backupManagerService, @Nullable String packageName, String transportName, BackupEligibilityRules backupEligibilityRules)73     public ActiveRestoreSession(
74             UserBackupManagerService backupManagerService,
75             @Nullable String packageName,
76             String transportName,
77             BackupEligibilityRules backupEligibilityRules) {
78         mBackupManagerService = backupManagerService;
79         mPackageName = packageName;
80         mTransportManager = backupManagerService.getTransportManager();
81         mTransportName = transportName;
82         mUserId = backupManagerService.getUserId();
83         mBackupEligibilityRules = backupEligibilityRules;
84     }
85 
markTimedOut()86     public void markTimedOut() {
87         mTimedOut = true;
88     }
89 
90     // --- Binder interface ---
getAvailableRestoreSets(IRestoreObserver observer, IBackupManagerMonitor monitor)91     public synchronized int getAvailableRestoreSets(IRestoreObserver observer,
92             IBackupManagerMonitor monitor) {
93         mBackupManagerService.getContext().enforceCallingOrSelfPermission(
94                 android.Manifest.permission.BACKUP,
95                 "getAvailableRestoreSets");
96         if (observer == null) {
97             throw new IllegalArgumentException("Observer must not be null");
98         }
99 
100         if (mEnded) {
101             throw new IllegalStateException("Restore session already ended");
102         }
103 
104         if (mTimedOut) {
105             Slog.i(TAG, "Session already timed out");
106             return -1;
107         }
108 
109         final long oldId = Binder.clearCallingIdentity();
110         try {
111             TransportConnection transportConnection =
112                     mTransportManager.getTransportClient(
113                                     mTransportName, "RestoreSession.getAvailableRestoreSets()");
114             if (transportConnection == null) {
115                 Slog.w(TAG, "Null transport client getting restore sets");
116                 return -1;
117             }
118 
119             // We know we're doing legit work now, so halt the timeout
120             // until we're done.  It gets started again when the result
121             // comes in.
122             mBackupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
123 
124             BackupWakeLock wakelock = mBackupManagerService.getWakeLock();
125             wakelock.acquire();
126 
127             // Prevent lambda from leaking 'this'
128             TransportManager transportManager = mTransportManager;
129             OnTaskFinishedListener listener = caller -> {
130                     transportManager.disposeOfTransportClient(transportConnection, caller);
131                     wakelock.release();
132             };
133             Message msg = mBackupManagerService.getBackupHandler().obtainMessage(
134                     MSG_RUN_GET_RESTORE_SETS,
135                     new RestoreGetSetsParams(transportConnection, this, observer, monitor,
136                             listener));
137             mBackupManagerService.getBackupHandler().sendMessage(msg);
138             return 0;
139         } catch (Exception e) {
140             Slog.e(TAG, "Error in getAvailableRestoreSets", e);
141             return -1;
142         } finally {
143             Binder.restoreCallingIdentity(oldId);
144         }
145     }
146 
restoreAll(long token, IRestoreObserver observer, IBackupManagerMonitor monitor)147     public synchronized int restoreAll(long token, IRestoreObserver observer,
148             IBackupManagerMonitor monitor) {
149         mBackupManagerService.getContext().enforceCallingOrSelfPermission(
150                 android.Manifest.permission.BACKUP,
151                 "performRestore");
152 
153         Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
154                     + " observer=" + observer);
155 
156         if (mEnded) {
157             throw new IllegalStateException("Restore session already ended");
158         }
159 
160         if (mTimedOut) {
161             Slog.i(TAG, "Session already timed out");
162             return -1;
163         }
164 
165         if (mRestoreSets == null) {
166             Slog.e(TAG, "Ignoring restoreAll() with no restore set");
167             return -1;
168         }
169 
170         if (mPackageName != null) {
171             Slog.e(TAG, "Ignoring restoreAll() on single-package session");
172             return -1;
173         }
174 
175         if (!mTransportManager.isTransportRegistered(mTransportName)) {
176             Slog.e(TAG, "Transport " + mTransportName + " not registered");
177             return -1;
178         }
179 
180         synchronized (mBackupManagerService.getQueueLock()) {
181             for (int i = 0; i < mRestoreSets.size(); i++) {
182                 if (token == mRestoreSets.get(i).token) {
183                     final long oldId = Binder.clearCallingIdentity();
184                     RestoreSet restoreSet = mRestoreSets.get(i);
185                     try {
186                         return sendRestoreToHandlerLocked(
187                                 (transportClient, listener) ->
188                                         RestoreParams.createForRestoreAll(
189                                                 transportClient,
190                                                 observer,
191                                                 monitor,
192                                                 token,
193                                                 listener,
194                                                 getBackupEligibilityRules(restoreSet)),
195                                 "RestoreSession.restoreAll()");
196                     } finally {
197                         Binder.restoreCallingIdentity(oldId);
198                     }
199                 }
200             }
201         }
202 
203         Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
204         return -1;
205     }
206 
207     // Restores of more than a single package are treated as 'system' restores
restorePackages(long token, @Nullable IRestoreObserver observer, @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor)208     public synchronized int restorePackages(long token, @Nullable IRestoreObserver observer,
209             @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor) {
210         mBackupManagerService.getContext().enforceCallingOrSelfPermission(
211                 android.Manifest.permission.BACKUP,
212                 "performRestore");
213 
214         StringBuilder b = new StringBuilder(128);
215         b.append("restorePackages token=");
216         b.append(Long.toHexString(token));
217         b.append(" observer=");
218         if (observer == null) {
219             b.append("null");
220         } else {
221             b.append(observer.toString());
222         }
223         b.append(" monitor=");
224         if (monitor == null) {
225             b.append("null");
226         } else {
227             b.append(monitor.toString());
228         }
229         b.append(" packages=");
230         if (packages == null) {
231             b.append("null");
232         } else {
233             b.append('{');
234             boolean first = true;
235             for (String s : packages) {
236                 if (!first) {
237                     b.append(", ");
238                 } else {
239                     first = false;
240                 }
241                 b.append(s);
242             }
243             b.append('}');
244         }
245         Slog.d(TAG, b.toString());
246 
247         if (mEnded) {
248             throw new IllegalStateException("Restore session already ended");
249         }
250 
251         if (mTimedOut) {
252             Slog.i(TAG, "Session already timed out");
253             return -1;
254         }
255 
256         if (mRestoreSets == null) {
257             Slog.e(TAG, "Ignoring restoreAll() with no restore set");
258             return -1;
259         }
260 
261         if (mPackageName != null) {
262             Slog.e(TAG, "Ignoring restoreAll() on single-package session");
263             return -1;
264         }
265 
266         if (!mTransportManager.isTransportRegistered(mTransportName)) {
267             Slog.e(TAG, "Transport " + mTransportName + " not registered");
268             return -1;
269         }
270 
271         synchronized (mBackupManagerService.getQueueLock()) {
272             for (int i = 0; i < mRestoreSets.size(); i++) {
273                 if (token == mRestoreSets.get(i).token) {
274                     final long oldId = Binder.clearCallingIdentity();
275                     RestoreSet restoreSet = mRestoreSets.get(i);
276                     try {
277                         return sendRestoreToHandlerLocked(
278                                 (transportClient, listener) ->
279                                         RestoreParams.createForRestorePackages(
280                                                 transportClient,
281                                                 observer,
282                                                 monitor,
283                                                 token,
284                                                 packages,
285                                                 /* isSystemRestore */ packages.length > 1,
286                                                 listener,
287                                                 getBackupEligibilityRules(restoreSet)),
288                                 "RestoreSession.restorePackages(" + packages.length + " packages)");
289                     } finally {
290                         Binder.restoreCallingIdentity(oldId);
291                     }
292                 }
293             }
294         }
295 
296         Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
297         return -1;
298     }
299 
300     @VisibleForTesting
getBackupEligibilityRules(RestoreSet restoreSet)301     BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
302         // TODO(b/182986784): Remove device name comparison once a designated field for operation
303         //  type is added to RestoreSet object.
304         int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device)
305                 ? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD;
306 
307         if (!Flags.enableSkippingRestoreLaunchedApps()) {
308             return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
309         }
310 
311         boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags
312                 & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0;
313 
314         return new BackupEligibilityRules(mBackupManagerService.getPackageManager(),
315                 LocalServices.getService(PackageManagerInternal.class),
316                 mUserId,
317                 mBackupManagerService.getContext(),
318                 backupDestination,
319                 skipRestoreForLaunchedApps);
320     }
321 
restorePackage(String packageName, IRestoreObserver observer, IBackupManagerMonitor monitor)322     public synchronized int restorePackage(String packageName, IRestoreObserver observer,
323             IBackupManagerMonitor monitor) {
324         Slog.d(TAG, "restorePackage pkg=" + packageName + " obs=" + observer
325                     + "monitor=" + monitor);
326 
327         if (mEnded) {
328             throw new IllegalStateException("Restore session already ended");
329         }
330 
331         if (mTimedOut) {
332             Slog.i(TAG, "Session already timed out");
333             return -1;
334         }
335 
336         if (mPackageName != null) {
337             if (!mPackageName.equals(packageName)) {
338                 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
339                         + " on session for package " + mPackageName);
340                 return -1;
341             }
342         }
343 
344         final PackageInfo app;
345         try {
346             app = mBackupManagerService.getPackageManager().getPackageInfoAsUser(
347                     packageName, 0, mUserId);
348         } catch (NameNotFoundException nnf) {
349             Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
350             return -1;
351         }
352 
353         // If the caller is not privileged and is not coming from the target
354         // app's uid, throw a permission exception back to the caller.
355         int perm = mBackupManagerService.getContext().checkPermission(
356                 android.Manifest.permission.BACKUP,
357                 Binder.getCallingPid(), Binder.getCallingUid());
358         if ((perm == PackageManager.PERMISSION_DENIED) &&
359                 (app.applicationInfo.uid != Binder.getCallingUid())) {
360             Slog.w(TAG, "restorePackage: bad packageName=" + packageName
361                     + " or calling uid=" + Binder.getCallingUid());
362             throw new SecurityException("No permission to restore other packages");
363         }
364 
365         if (!mTransportManager.isTransportRegistered(mTransportName)) {
366             Slog.e(TAG, "Transport " + mTransportName + " not registered");
367             return -1;
368         }
369 
370         // So far so good; we're allowed to try to restore this package.
371         final long oldId = Binder.clearCallingIdentity();
372         try {
373             // Check whether there is data for it in the current dataset, falling back
374             // to the ancestral dataset if not.
375             long token = mBackupManagerService.getAvailableRestoreToken(packageName);
376             Slog.d(TAG, "restorePackage pkg=" + packageName
377                         + " token=" + Long.toHexString(token));
378 
379             // If we didn't come up with a place to look -- no ancestral dataset and
380             // the app has never been backed up from this device -- there's nothing
381             // to do but return failure.
382             if (token == 0) {
383                 Slog.w(TAG, "No data available for this package; not restoring");
384                 return -1;
385             }
386 
387             return sendRestoreToHandlerLocked(
388                     (transportClient, listener) ->
389                             RestoreParams.createForSinglePackage(
390                                     transportClient,
391                                     observer,
392                                     monitor,
393                                     token,
394                                     app,
395                                     listener,
396                                     mBackupEligibilityRules),
397                     "RestoreSession.restorePackage(" + packageName + ")");
398         } finally {
399             Binder.restoreCallingIdentity(oldId);
400         }
401     }
402 
setRestoreSets(List<RestoreSet> restoreSets)403     public void setRestoreSets(List<RestoreSet> restoreSets) {
404         mRestoreSets = restoreSets;
405     }
406 
407     /**
408      * Returns 0 if operation sent or -1 otherwise.
409      */
sendRestoreToHandlerLocked( BiFunction<TransportConnection, OnTaskFinishedListener, RestoreParams> restoreParamsBuilder, String callerLogString)410     private int sendRestoreToHandlerLocked(
411             BiFunction<TransportConnection, OnTaskFinishedListener,
412                     RestoreParams> restoreParamsBuilder, String callerLogString) {
413         TransportConnection transportConnection =
414                 mTransportManager.getTransportClient(mTransportName, callerLogString);
415         if (transportConnection == null) {
416             Slog.e(TAG, "Transport " + mTransportName + " got unregistered");
417             return -1;
418         }
419 
420         // Stop the session timeout until we finalize the restore
421         Handler backupHandler = mBackupManagerService.getBackupHandler();
422         backupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
423 
424         BackupWakeLock wakelock = mBackupManagerService.getWakeLock();
425         wakelock.acquire();
426         if (DEBUG) {
427             Slog.d(TAG, callerLogString);
428         }
429 
430         // Prevent lambda from leaking 'this'
431         TransportManager transportManager = mTransportManager;
432         OnTaskFinishedListener listener = caller -> {
433                 transportManager.disposeOfTransportClient(transportConnection, caller);
434                 wakelock.release();
435         };
436         Message msg = backupHandler.obtainMessage(MSG_RUN_RESTORE);
437         msg.obj = restoreParamsBuilder.apply(transportConnection, listener);
438         backupHandler.sendMessage(msg);
439         return 0;
440     }
441 
442     // Posted to the handler to tear down a restore session in a cleanly synchronized way
443     public class EndRestoreRunnable implements Runnable {
444 
445         UserBackupManagerService mBackupManager;
446         ActiveRestoreSession mSession;
447 
EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session)448         public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) {
449             mBackupManager = manager;
450             mSession = session;
451         }
452 
run()453         public void run() {
454             // clean up the session's bookkeeping
455             synchronized (mSession) {
456                 mSession.mEnded = true;
457             }
458 
459             // clean up the BackupManagerImpl side of the bookkeeping
460             // and cancel any pending timeout message
461             mBackupManager.clearRestoreSession(mSession);
462         }
463     }
464 
endRestoreSession()465     public synchronized void endRestoreSession() {
466         Slog.d(TAG, "endRestoreSession");
467 
468         if (mTimedOut) {
469             Slog.i(TAG, "Session already timed out");
470             return;
471         }
472 
473         if (mEnded) {
474             throw new IllegalStateException("Restore session already ended");
475         }
476 
477         mBackupManagerService.getBackupHandler().post(
478                 new EndRestoreRunnable(mBackupManagerService, this));
479     }
480 }
481