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