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