Home | History | Annotate | Download | only in backup
      1 /*
      2  * Copyright (C) 2011 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 android.app.backup;
     18 
     19 import android.content.Context;
     20 import android.content.pm.ApplicationInfo;
     21 import android.content.pm.PackageManager;
     22 import android.os.ParcelFileDescriptor;
     23 import android.util.Log;
     24 
     25 import java.io.File;
     26 import java.io.FileInputStream;
     27 import java.io.FileOutputStream;
     28 import java.io.IOException;
     29 
     30 import libcore.io.ErrnoException;
     31 import libcore.io.Libcore;
     32 
     33 /**
     34  * Global constant definitions et cetera related to the full-backup-to-fd
     35  * binary format.  Nothing in this namespace is part of any API; it's all
     36  * hidden details of the current implementation gathered into one location.
     37  *
     38  * @hide
     39  */
     40 public class FullBackup {
     41     static final String TAG = "FullBackup";
     42 
     43     public static final String APK_TREE_TOKEN = "a";
     44     public static final String OBB_TREE_TOKEN = "obb";
     45     public static final String ROOT_TREE_TOKEN = "r";
     46     public static final String DATA_TREE_TOKEN = "f";
     47     public static final String DATABASE_TREE_TOKEN = "db";
     48     public static final String SHAREDPREFS_TREE_TOKEN = "sp";
     49     public static final String CACHE_TREE_TOKEN = "c";
     50     public static final String SHARED_STORAGE_TOKEN = "shared";
     51 
     52     public static final String APPS_PREFIX = "apps/";
     53     public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
     54 
     55     public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
     56     public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
     57     public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken";
     58 
     59     /**
     60      * @hide
     61      */
     62     static public native int backupToTar(String packageName, String domain,
     63             String linkdomain, String rootpath, String path, BackupDataOutput output);
     64 
     65     /**
     66      * Copy data from a socket to the given File location on permanent storage.  The
     67      * modification time and access mode of the resulting file will be set if desired.
     68      * If the {@code type} parameter indicates that the result should be a directory,
     69      * the socket parameter may be {@code null}; even if it is valid, no data will be
     70      * read from it in this case.
     71      * <p>
     72      * If the {@code mode} argument is negative, then the resulting output file will not
     73      * have its access mode or last modification time reset as part of this operation.
     74      *
     75      * @param data Socket supplying the data to be copied to the output file.  If the
     76      *    output is a directory, this may be {@code null}.
     77      * @param size Number of bytes of data to copy from the socket to the file.  At least
     78      *    this much data must be available through the {@code data} parameter.
     79      * @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data
     80      *    or {@link BackupAgent#TYPE_DIRECTORY} for a directory.
     81      * @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on
     82      *    the output file or directory.  If this parameter is negative then neither
     83      *    the mode nor the mtime parameters will be used.
     84      * @param mtime A timestamp in the standard Unix epoch that will be imposed as the
     85      *    last modification time of the output file.  if the {@code mode} parameter is
     86      *    negative then this parameter will be ignored.
     87      * @param outFile Location within the filesystem to place the data.  This must point
     88      *    to a location that is writeable by the caller, prefereably using an absolute path.
     89      * @throws IOException
     90      */
     91     static public void restoreFile(ParcelFileDescriptor data,
     92             long size, int type, long mode, long mtime, File outFile) throws IOException {
     93         if (type == BackupAgent.TYPE_DIRECTORY) {
     94             // Canonically a directory has no associated content, so we don't need to read
     95             // anything from the pipe in this case.  Just create the directory here and
     96             // drop down to the final metadata adjustment.
     97             if (outFile != null) outFile.mkdirs();
     98         } else {
     99             FileOutputStream out = null;
    100 
    101             // Pull the data from the pipe, copying it to the output file, until we're done
    102             try {
    103                 if (outFile != null) {
    104                     File parent = outFile.getParentFile();
    105                     if (!parent.exists()) {
    106                         // in practice this will only be for the default semantic directories,
    107                         // and using the default mode for those is appropriate.
    108                         // TODO: support the edge case of apps that have adjusted the
    109                         //       permissions on these core directories
    110                         parent.mkdirs();
    111                     }
    112                     out = new FileOutputStream(outFile);
    113                 }
    114             } catch (IOException e) {
    115                 Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e);
    116             }
    117 
    118             byte[] buffer = new byte[32 * 1024];
    119             final long origSize = size;
    120             FileInputStream in = new FileInputStream(data.getFileDescriptor());
    121             while (size > 0) {
    122                 int toRead = (size > buffer.length) ? buffer.length : (int)size;
    123                 int got = in.read(buffer, 0, toRead);
    124                 if (got <= 0) {
    125                     Log.w(TAG, "Incomplete read: expected " + size + " but got "
    126                             + (origSize - size));
    127                     break;
    128                 }
    129                 if (out != null) {
    130                     try {
    131                         out.write(buffer, 0, got);
    132                     } catch (IOException e) {
    133                         // Problem writing to the file.  Quit copying data and delete
    134                         // the file, but of course keep consuming the input stream.
    135                         Log.e(TAG, "Unable to write to file " + outFile.getPath(), e);
    136                         out.close();
    137                         out = null;
    138                         outFile.delete();
    139                     }
    140                 }
    141                 size -= got;
    142             }
    143             if (out != null) out.close();
    144         }
    145 
    146         // Now twiddle the state to match the backup, assuming all went well
    147         if (mode >= 0 && outFile != null) {
    148             try {
    149                 Libcore.os.chmod(outFile.getPath(), (int)mode);
    150             } catch (ErrnoException e) {
    151                 e.rethrowAsIOException();
    152             }
    153             outFile.setLastModified(mtime);
    154         }
    155     }
    156 }
    157