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