1 package com.android.server.backup; 2 3 import static android.os.ParcelFileDescriptor.MODE_CREATE; 4 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; 5 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; 6 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; 7 8 import android.app.IBackupAgent; 9 import android.app.backup.BackupDataInput; 10 import android.app.backup.BackupDataOutput; 11 import android.app.backup.FullBackup; 12 import android.os.ParcelFileDescriptor; 13 import android.os.RemoteException; 14 import android.util.Slog; 15 16 import com.android.server.backup.restore.PerformAdbRestoreTask; 17 18 import libcore.io.IoUtils; 19 20 import java.io.File; 21 import java.io.FileInputStream; 22 import java.io.FileOutputStream; 23 import java.io.IOException; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 30 /** 31 * Used by BackupManagerService to perform adb restore for key-value packages. At the moment this 32 * class resembles what is done in the standard key-value code paths in BackupManagerService, and 33 * should be unified later. 34 * 35 * TODO: We should create unified backup/restore engines that can be used for both transport and 36 * adb backup/restore, and for fullbackup and key-value backup. 37 */ 38 public class KeyValueAdbRestoreEngine implements Runnable { 39 private static final String TAG = "KeyValueAdbRestoreEngine"; 40 private static final boolean DEBUG = false; 41 42 private final BackupManagerServiceInterface mBackupManagerService; 43 private final File mDataDir; 44 45 FileMetadata mInfo; 46 PerformAdbRestoreTask mRestoreTask; 47 ParcelFileDescriptor mInFD; 48 IBackupAgent mAgent; 49 int mToken; 50 51 public KeyValueAdbRestoreEngine(BackupManagerServiceInterface backupManagerService, 52 File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent, 53 int token) { 54 mBackupManagerService = backupManagerService; 55 mDataDir = dataDir; 56 mInfo = info; 57 mInFD = inFD; 58 mAgent = agent; 59 mToken = token; 60 } 61 62 @Override 63 public void run() { 64 try { 65 File restoreData = prepareRestoreData(mInfo, mInFD); 66 67 invokeAgentForAdbRestore(mAgent, mInfo, restoreData); 68 } catch (IOException e) { 69 e.printStackTrace(); 70 } 71 } 72 73 private File prepareRestoreData(FileMetadata info, ParcelFileDescriptor inFD) throws IOException { 74 String pkg = info.packageName; 75 File restoreDataName = new File(mDataDir, pkg + ".restore"); 76 File sortedDataName = new File(mDataDir, pkg + ".sorted"); 77 78 FullBackup.restoreFile(inFD, info.size, info.type, info.mode, info.mtime, restoreDataName); 79 80 // Sort the keys, as the BackupAgent expect them to come in lexicographical order 81 sortKeyValueData(restoreDataName, sortedDataName); 82 return sortedDataName; 83 } 84 85 private void invokeAgentForAdbRestore(IBackupAgent agent, FileMetadata info, File restoreData) 86 throws IOException { 87 String pkg = info.packageName; 88 File newStateName = new File(mDataDir, pkg + ".new"); 89 try { 90 ParcelFileDescriptor backupData = 91 ParcelFileDescriptor.open(restoreData, MODE_READ_ONLY); 92 ParcelFileDescriptor newState = ParcelFileDescriptor.open(newStateName, 93 MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); 94 95 if (DEBUG) { 96 Slog.i(TAG, "Starting restore of package " + pkg + " for version code " 97 + info.version); 98 } 99 agent.doRestore(backupData, info.version, newState, mToken, 100 mBackupManagerService.getBackupManagerBinder()); 101 } catch (IOException e) { 102 Slog.e(TAG, "Exception opening file. " + e); 103 } catch (RemoteException e) { 104 Slog.e(TAG, "Exception calling doRestore on agent: " + e); 105 } 106 } 107 108 private void sortKeyValueData (File restoreData, File sortedData) throws IOException { 109 FileInputStream inputStream = null; 110 FileOutputStream outputStream = null; 111 try { 112 inputStream = new FileInputStream(restoreData); 113 outputStream = new FileOutputStream(sortedData); 114 BackupDataInput reader = new BackupDataInput(inputStream.getFD()); 115 BackupDataOutput writer = new BackupDataOutput(outputStream.getFD()); 116 copyKeysInLexicalOrder(reader, writer); 117 } finally { 118 if (inputStream != null) { 119 IoUtils.closeQuietly(inputStream); 120 } 121 if (outputStream != null) { 122 IoUtils.closeQuietly(outputStream); 123 } 124 } 125 } 126 127 private void copyKeysInLexicalOrder(BackupDataInput in, BackupDataOutput out) 128 throws IOException { 129 Map<String, byte[]> data = new HashMap<>(); 130 while (in.readNextHeader()) { 131 String key = in.getKey(); 132 int size = in.getDataSize(); 133 if (size < 0) { 134 in.skipEntityData(); 135 continue; 136 } 137 byte[] value = new byte[size]; 138 in.readEntityData(value, 0, size); 139 data.put(key, value); 140 } 141 List<String> keys = new ArrayList<>(data.keySet()); 142 Collections.sort(keys); 143 for (String key : keys) { 144 byte[] value = data.get(key); 145 out.writeEntityHeader(key, value.length); 146 out.writeEntityData(value, value.length); 147 } 148 } 149 } 150