146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate/* 246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * Copyright (C) 2013 The Android Open Source Project 346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * 446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * Licensed under the Apache License, Version 2.0 (the "License"); 546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * you may not use this file except in compliance with the License. 646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * You may obtain a copy of the License at 746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * 846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * http://www.apache.org/licenses/LICENSE-2.0 946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * 1046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * Unless required by applicable law or agreed to in writing, software 1146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * distributed under the License is distributed on an "AS IS" BASIS, 1246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * See the License for the specific language governing permissions and 1446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * limitations under the License. 1546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate */ 1646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 1746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tatepackage com.android.sharedstoragebackup; 1846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 1946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.app.Service; 2046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.app.backup.BackupDataOutput; 2146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.app.backup.FullBackup; 2246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.app.backup.IBackupManager; 2346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.content.Intent; 2446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.os.Environment; 2546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.os.IBinder; 2646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.os.ParcelFileDescriptor; 2746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.os.RemoteException; 2846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport android.util.Log; 2946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 3046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport java.io.File; 3146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport java.io.FileDescriptor; 3246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport java.io.FileOutputStream; 3346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport java.io.IOException; 3446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport java.util.ArrayList; 3546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 3646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tateimport com.android.internal.backup.IObbBackupService; 3746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 3846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate/** 3946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * Service that the Backup Manager Services delegates OBB backup/restore operations to, 4046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * because those require accessing external storage. 4146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * 4246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * {@hide} 4346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate */ 4446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tatepublic class ObbBackupService extends Service { 4546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate static final String TAG = "ObbBackupService"; 4646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate static final boolean DEBUG = true; 4746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 4846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate /** 4946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * IObbBackupService interface implementation 5046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate */ 5146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate IObbBackupService mService = new IObbBackupService.Stub() { 5246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate /* 5346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * Back up a package's OBB directory tree 5446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate */ 5546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate @Override 5646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate public void backupObbs(String packageName, ParcelFileDescriptor data, 5746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate int token, IBackupManager callbackBinder) { 5846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate final FileDescriptor outFd = data.getFileDescriptor(); 5946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate try { 607f392defccfae54dc8169e5ad82c2616e0713c8eJeff Sharkey File obbDir = Environment.buildExternalStorageAppObbDirs(packageName)[0]; 6146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (obbDir != null) { 6246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (obbDir.exists()) { 6346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate ArrayList<File> obbList = allFileContents(obbDir); 6446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (obbList != null) { 6546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate // okay, there's at least something there 6646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (DEBUG) { 6746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate Log.i(TAG, obbList.size() + " files to back up"); 6846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 6946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate final String rootPath = obbDir.getCanonicalPath(); 7046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate final BackupDataOutput out = new BackupDataOutput(outFd); 7146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate for (File f : obbList) { 7246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate final String filePath = f.getCanonicalPath(); 7346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (DEBUG) { 7446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate Log.i(TAG, "storing: " + filePath); 7546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 7646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate FullBackup.backupToTar(packageName, FullBackup.OBB_TREE_TOKEN, null, 7746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate rootPath, filePath, out); 7846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 7946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 8046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 8146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 8246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } catch (IOException e) { 8346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate Log.w(TAG, "Exception backing up OBBs for " + packageName, e); 8446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } finally { 8546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate // Send the EOD marker indicating that there is no more data 8646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate try { 8746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate FileOutputStream out = new FileOutputStream(outFd); 8846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate byte[] buf = new byte[4]; 8946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate out.write(buf); 9046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } catch (IOException e) { 9146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate Log.e(TAG, "Unable to finalize obb backup stream!"); 9246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 9346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 9446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate try { 9546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate callbackBinder.opComplete(token); 9646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } catch (RemoteException e) { 9746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 9846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 9946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 10046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 10146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate /* 10246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * Restore an OBB file for the given package from the incoming stream 10346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate */ 10446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate @Override 10546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate public void restoreObbFile(String packageName, ParcelFileDescriptor data, 10646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate long fileSize, int type, String path, long mode, long mtime, 10746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate int token, IBackupManager callbackBinder) { 10846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate try { 1097f392defccfae54dc8169e5ad82c2616e0713c8eJeff Sharkey File outFile = Environment.buildExternalStorageAppObbDirs(packageName)[0]; 11046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (outFile != null) { 11146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate outFile = new File(outFile, path); 11246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 11346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate // outFile is null here if we couldn't get access to external storage, 11446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate // in which case restoreFile() will discard the data cleanly and let 11546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate // us proceed with the next file segment in the stream. We pass -1 11646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate // for the file mode to suppress attempts to chmod() on shared storage. 11746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate FullBackup.restoreFile(data, fileSize, type, -1, mtime, outFile); 11846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } catch (IOException e) { 11946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate Log.i(TAG, "Exception restoring OBB " + path, e); 12046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } finally { 12146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate try { 12246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate callbackBinder.opComplete(token); 12346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } catch (RemoteException e) { 12446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 12546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 12646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 12746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 12846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate ArrayList<File> allFileContents(File rootDir) { 12946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate final ArrayList<File> files = new ArrayList<File>(); 13046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate final ArrayList<File> dirs = new ArrayList<File>(); 13146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 13246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate dirs.add(rootDir); 13346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate while (!dirs.isEmpty()) { 13446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate File dir = dirs.remove(0); 13546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate File[] contents = dir.listFiles(); 13646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (contents != null) { 13746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate for (File f : contents) { 13846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate if (f.isDirectory()) dirs.add(f); 13946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate else if (f.isFile()) files.add(f); 14046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 14146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 14246cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 14346cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate return files; 14446cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 14546cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate }; 14646cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate 14746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate @Override 14846cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate public IBinder onBind(Intent intent) { 14946cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate return mService.asBinder(); 15046cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate } 15146cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate} 152