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.BackupManagerService.MORE_DEBUG;
21 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
22 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_GET_RESTORE_SETS;
23 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.app.backup.BackupAgent;
28 import android.app.backup.BackupAnnotations.BackupDestination;
29 import android.app.backup.IBackupManagerMonitor;
30 import android.app.backup.IRestoreObserver;
31 import android.app.backup.IRestoreSession;
32 import android.app.backup.RestoreSet;
33 import android.content.pm.PackageInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManager.NameNotFoundException;
36 import android.content.pm.PackageManagerInternal;
37 import android.os.Binder;
38 import android.os.Handler;
39 import android.os.Message;
40 import android.util.Slog;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.server.LocalServices;
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             UserBackupManagerService.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         if (DEBUG) {
154             Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
155                     + " observer=" + observer);
156         }
157 
158         if (mEnded) {
159             throw new IllegalStateException("Restore session already ended");
160         }
161 
162         if (mTimedOut) {
163             Slog.i(TAG, "Session already timed out");
164             return -1;
165         }
166 
167         if (mRestoreSets == null) {
168             Slog.e(TAG, "Ignoring restoreAll() with no restore set");
169             return -1;
170         }
171 
172         if (mPackageName != null) {
173             Slog.e(TAG, "Ignoring restoreAll() on single-package session");
174             return -1;
175         }
176 
177         if (!mTransportManager.isTransportRegistered(mTransportName)) {
178             Slog.e(TAG, "Transport " + mTransportName + " not registered");
179             return -1;
180         }
181 
182         synchronized (mBackupManagerService.getQueueLock()) {
183             for (int i = 0; i < mRestoreSets.size(); i++) {
184                 if (token == mRestoreSets.get(i).token) {
185                     final long oldId = Binder.clearCallingIdentity();
186                     RestoreSet restoreSet = mRestoreSets.get(i);
187                     try {
188                         return sendRestoreToHandlerLocked(
189                                 (transportClient, listener) ->
190                                         RestoreParams.createForRestoreAll(
191                                                 transportClient,
192                                                 observer,
193                                                 monitor,
194                                                 token,
195                                                 listener,
196                                                 getBackupEligibilityRules(restoreSet)),
197                                 "RestoreSession.restoreAll()");
198                     } finally {
199                         Binder.restoreCallingIdentity(oldId);
200                     }
201                 }
202             }
203         }
204 
205         Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
206         return -1;
207     }
208 
209     // Restores of more than a single package are treated as 'system' restores
restorePackages(long token, @Nullable IRestoreObserver observer, @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor)210     public synchronized int restorePackages(long token, @Nullable IRestoreObserver observer,
211             @NonNull String[] packages, @Nullable IBackupManagerMonitor monitor) {
212         mBackupManagerService.getContext().enforceCallingOrSelfPermission(
213                 android.Manifest.permission.BACKUP,
214                 "performRestore");
215 
216         if (DEBUG) {
217             StringBuilder b = new StringBuilder(128);
218             b.append("restorePackages token=");
219             b.append(Long.toHexString(token));
220             b.append(" observer=");
221             if (observer == null) {
222                 b.append("null");
223             } else {
224                 b.append(observer.toString());
225             }
226             b.append(" monitor=");
227             if (monitor == null) {
228                 b.append("null");
229             } else {
230                 b.append(monitor.toString());
231             }
232             b.append(" packages=");
233             if (packages == null) {
234                 b.append("null");
235             } else {
236                 b.append('{');
237                 boolean first = true;
238                 for (String s : packages) {
239                     if (!first) {
240                         b.append(", ");
241                     } else {
242                         first = false;
243                     }
244                     b.append(s);
245                 }
246                 b.append('}');
247             }
248             Slog.d(TAG, b.toString());
249         }
250 
251         if (mEnded) {
252             throw new IllegalStateException("Restore session already ended");
253         }
254 
255         if (mTimedOut) {
256             Slog.i(TAG, "Session already timed out");
257             return -1;
258         }
259 
260         if (mRestoreSets == null) {
261             Slog.e(TAG, "Ignoring restoreAll() with no restore set");
262             return -1;
263         }
264 
265         if (mPackageName != null) {
266             Slog.e(TAG, "Ignoring restoreAll() on single-package session");
267             return -1;
268         }
269 
270         if (!mTransportManager.isTransportRegistered(mTransportName)) {
271             Slog.e(TAG, "Transport " + mTransportName + " not registered");
272             return -1;
273         }
274 
275         synchronized (mBackupManagerService.getQueueLock()) {
276             for (int i = 0; i < mRestoreSets.size(); i++) {
277                 if (token == mRestoreSets.get(i).token) {
278                     final long oldId = Binder.clearCallingIdentity();
279                     RestoreSet restoreSet = mRestoreSets.get(i);
280                     try {
281                         return sendRestoreToHandlerLocked(
282                                 (transportClient, listener) ->
283                                         RestoreParams.createForRestorePackages(
284                                                 transportClient,
285                                                 observer,
286                                                 monitor,
287                                                 token,
288                                                 packages,
289                                                 /* isSystemRestore */ packages.length > 1,
290                                                 listener,
291                                                 getBackupEligibilityRules(restoreSet)),
292                                 "RestoreSession.restorePackages(" + packages.length + " packages)");
293                     } finally {
294                         Binder.restoreCallingIdentity(oldId);
295                     }
296                 }
297             }
298         }
299 
300         Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
301         return -1;
302     }
303 
304     @VisibleForTesting
getBackupEligibilityRules(RestoreSet restoreSet)305     BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
306         // TODO(b/182986784): Remove device name comparison once a designated field for operation
307         //  type is added to RestoreSet object.
308         int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device)
309                 ? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD;
310 
311         if (!Flags.enableSkippingRestoreLaunchedApps()) {
312             return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
313         }
314 
315         boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags
316                 & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0;
317 
318         return new BackupEligibilityRules(mBackupManagerService.getPackageManager(),
319                 LocalServices.getService(PackageManagerInternal.class),
320                 mUserId,
321                 mBackupManagerService.getContext(),
322                 backupDestination,
323                 skipRestoreForLaunchedApps);
324     }
325 
restorePackage(String packageName, IRestoreObserver observer, IBackupManagerMonitor monitor)326     public synchronized int restorePackage(String packageName, IRestoreObserver observer,
327             IBackupManagerMonitor monitor) {
328         if (DEBUG) {
329             Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer
330                     + "monitor=" + monitor);
331         }
332 
333         if (mEnded) {
334             throw new IllegalStateException("Restore session already ended");
335         }
336 
337         if (mTimedOut) {
338             Slog.i(TAG, "Session already timed out");
339             return -1;
340         }
341 
342         if (mPackageName != null) {
343             if (!mPackageName.equals(packageName)) {
344                 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
345                         + " on session for package " + mPackageName);
346                 return -1;
347             }
348         }
349 
350         final PackageInfo app;
351         try {
352             app = mBackupManagerService.getPackageManager().getPackageInfoAsUser(
353                     packageName, 0, mUserId);
354         } catch (NameNotFoundException nnf) {
355             Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
356             return -1;
357         }
358 
359         // If the caller is not privileged and is not coming from the target
360         // app's uid, throw a permission exception back to the caller.
361         int perm = mBackupManagerService.getContext().checkPermission(
362                 android.Manifest.permission.BACKUP,
363                 Binder.getCallingPid(), Binder.getCallingUid());
364         if ((perm == PackageManager.PERMISSION_DENIED) &&
365                 (app.applicationInfo.uid != Binder.getCallingUid())) {
366             Slog.w(TAG, "restorePackage: bad packageName=" + packageName
367                     + " or calling uid=" + Binder.getCallingUid());
368             throw new SecurityException("No permission to restore other packages");
369         }
370 
371         if (!mTransportManager.isTransportRegistered(mTransportName)) {
372             Slog.e(TAG, "Transport " + mTransportName + " not registered");
373             return -1;
374         }
375 
376         // So far so good; we're allowed to try to restore this package.
377         final long oldId = Binder.clearCallingIdentity();
378         try {
379             // Check whether there is data for it in the current dataset, falling back
380             // to the ancestral dataset if not.
381             long token = mBackupManagerService.getAvailableRestoreToken(packageName);
382             if (DEBUG) {
383                 Slog.v(TAG, "restorePackage pkg=" + packageName
384                         + " token=" + Long.toHexString(token));
385             }
386 
387             // If we didn't come up with a place to look -- no ancestral dataset and
388             // the app has never been backed up from this device -- there's nothing
389             // to do but return failure.
390             if (token == 0) {
391                 if (DEBUG) {
392                     Slog.w(TAG, "No data available for this package; not restoring");
393                 }
394                 return -1;
395             }
396 
397             return sendRestoreToHandlerLocked(
398                     (transportClient, listener) ->
399                             RestoreParams.createForSinglePackage(
400                                     transportClient,
401                                     observer,
402                                     monitor,
403                                     token,
404                                     app,
405                                     listener,
406                                     mBackupEligibilityRules),
407                     "RestoreSession.restorePackage(" + packageName + ")");
408         } finally {
409             Binder.restoreCallingIdentity(oldId);
410         }
411     }
412 
setRestoreSets(List<RestoreSet> restoreSets)413     public void setRestoreSets(List<RestoreSet> restoreSets) {
414         mRestoreSets = restoreSets;
415     }
416 
417     /**
418      * Returns 0 if operation sent or -1 otherwise.
419      */
sendRestoreToHandlerLocked( BiFunction<TransportConnection, OnTaskFinishedListener, RestoreParams> restoreParamsBuilder, String callerLogString)420     private int sendRestoreToHandlerLocked(
421             BiFunction<TransportConnection, OnTaskFinishedListener,
422                     RestoreParams> restoreParamsBuilder, String callerLogString) {
423         TransportConnection transportConnection =
424                 mTransportManager.getTransportClient(mTransportName, callerLogString);
425         if (transportConnection == null) {
426             Slog.e(TAG, "Transport " + mTransportName + " got unregistered");
427             return -1;
428         }
429 
430         // Stop the session timeout until we finalize the restore
431         Handler backupHandler = mBackupManagerService.getBackupHandler();
432         backupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
433 
434         UserBackupManagerService.BackupWakeLock wakelock = mBackupManagerService.getWakelock();
435         wakelock.acquire();
436         if (MORE_DEBUG) {
437             Slog.d(TAG, callerLogString);
438         }
439 
440         // Prevent lambda from leaking 'this'
441         TransportManager transportManager = mTransportManager;
442         OnTaskFinishedListener listener = caller -> {
443                 transportManager.disposeOfTransportClient(transportConnection, caller);
444                 wakelock.release();
445         };
446         Message msg = backupHandler.obtainMessage(MSG_RUN_RESTORE);
447         msg.obj = restoreParamsBuilder.apply(transportConnection, listener);
448         backupHandler.sendMessage(msg);
449         return 0;
450     }
451 
452     // Posted to the handler to tear down a restore session in a cleanly synchronized way
453     public class EndRestoreRunnable implements Runnable {
454 
455         UserBackupManagerService mBackupManager;
456         ActiveRestoreSession mSession;
457 
EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session)458         public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) {
459             mBackupManager = manager;
460             mSession = session;
461         }
462 
run()463         public void run() {
464             // clean up the session's bookkeeping
465             synchronized (mSession) {
466                 mSession.mEnded = true;
467             }
468 
469             // clean up the BackupManagerImpl side of the bookkeeping
470             // and cancel any pending timeout message
471             mBackupManager.clearRestoreSession(mSession);
472         }
473     }
474 
endRestoreSession()475     public synchronized void endRestoreSession() {
476         if (DEBUG) {
477             Slog.d(TAG, "endRestoreSession");
478         }
479 
480         if (mTimedOut) {
481             Slog.i(TAG, "Session already timed out");
482             return;
483         }
484 
485         if (mEnded) {
486             throw new IllegalStateException("Restore session already ended");
487         }
488 
489         mBackupManagerService.getBackupHandler().post(
490                 new EndRestoreRunnable(mBackupManagerService, this));
491     }
492 }
493