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