15f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato/*
25f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * Copyright (C) 2009 The Android Open Source Project
35f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato *
45f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * Licensed under the Apache License, Version 2.0 (the "License");
55f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * you may not use this file except in compliance with the License.
65f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * You may obtain a copy of the License at
75f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato *
85f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato *      http://www.apache.org/licenses/LICENSE-2.0
95f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato *
105f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * Unless required by applicable law or agreed to in writing, software
115f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * distributed under the License is distributed on an "AS IS" BASIS,
125f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * See the License for the specific language governing permissions and
145f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * limitations under the License.
155f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato */
165f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato
174528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tatepackage android.app.backup;
185f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato
19d2d9ceb7305d593c1b767bbb05de0082a9af4109Joe Onoratoimport android.os.ParcelFileDescriptor;
2083248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onoratoimport android.util.Log;
2183248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato
224ababd922eac5931e0222862ff082dc29e012816Joe Onoratoimport java.io.FileDescriptor;
235a20ea16d7e5b70dc7bad8700f54170e4f220d12Kenny Rootimport java.io.IOException;
24d2d9ceb7305d593c1b767bbb05de0082a9af4109Joe Onoratoimport java.util.Map;
255a20ea16d7e5b70dc7bad8700f54170e4f220d12Kenny Rootimport java.util.TreeMap;
265f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato
275f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato/** @hide */
2806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onoratopublic class BackupHelperDispatcher {
2906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    private static final String TAG = "BackupHelperDispatcher";
3083248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato
314ababd922eac5931e0222862ff082dc29e012816Joe Onorato    private static class Header {
324ababd922eac5931e0222862ff082dc29e012816Joe Onorato        int chunkSize; // not including the header
334ababd922eac5931e0222862ff082dc29e012816Joe Onorato        String keyPrefix;
344ababd922eac5931e0222862ff082dc29e012816Joe Onorato    }
354ababd922eac5931e0222862ff082dc29e012816Joe Onorato
3606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
3706290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
3806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public BackupHelperDispatcher() {
3906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    }
405f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato
4106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public void addHelper(String keyPrefix, BackupHelper helper) {
425f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato        mHelpers.put(keyPrefix, helper);
435f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato    }
445f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato
4506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
464ababd922eac5931e0222862ff082dc29e012816Joe Onorato             ParcelFileDescriptor newState) throws IOException {
474ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // First, do the helpers that we've already done, since they're already in the state
484ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // file.
494ababd922eac5931e0222862ff082dc29e012816Joe Onorato        int err;
504ababd922eac5931e0222862ff082dc29e012816Joe Onorato        Header header = new Header();
514ababd922eac5931e0222862ff082dc29e012816Joe Onorato        TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
524ababd922eac5931e0222862ff082dc29e012816Joe Onorato        FileDescriptor oldStateFD = null;
534ababd922eac5931e0222862ff082dc29e012816Joe Onorato
544ababd922eac5931e0222862ff082dc29e012816Joe Onorato        if (oldState != null) {
554ababd922eac5931e0222862ff082dc29e012816Joe Onorato            oldStateFD = oldState.getFileDescriptor();
564ababd922eac5931e0222862ff082dc29e012816Joe Onorato            while ((err = readHeader_native(header, oldStateFD)) >= 0) {
574ababd922eac5931e0222862ff082dc29e012816Joe Onorato                if (err == 0) {
584ababd922eac5931e0222862ff082dc29e012816Joe Onorato                    BackupHelper helper = helpers.get(header.keyPrefix);
594ababd922eac5931e0222862ff082dc29e012816Joe Onorato                    Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
604ababd922eac5931e0222862ff082dc29e012816Joe Onorato                    if (helper != null) {
614ababd922eac5931e0222862ff082dc29e012816Joe Onorato                        doOneBackup(oldState, data, newState, header, helper);
624ababd922eac5931e0222862ff082dc29e012816Joe Onorato                        helpers.remove(header.keyPrefix);
634ababd922eac5931e0222862ff082dc29e012816Joe Onorato                    } else {
644ababd922eac5931e0222862ff082dc29e012816Joe Onorato                        skipChunk_native(oldStateFD, header.chunkSize);
654ababd922eac5931e0222862ff082dc29e012816Joe Onorato                    }
664ababd922eac5931e0222862ff082dc29e012816Joe Onorato                }
674ababd922eac5931e0222862ff082dc29e012816Joe Onorato            }
684ababd922eac5931e0222862ff082dc29e012816Joe Onorato        }
694ababd922eac5931e0222862ff082dc29e012816Joe Onorato
704ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // Then go through and do the rest that we haven't done.
714ababd922eac5931e0222862ff082dc29e012816Joe Onorato        for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
724ababd922eac5931e0222862ff082dc29e012816Joe Onorato            header.keyPrefix = entry.getKey();
734ababd922eac5931e0222862ff082dc29e012816Joe Onorato            Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
744ababd922eac5931e0222862ff082dc29e012816Joe Onorato            BackupHelper helper = entry.getValue();
754ababd922eac5931e0222862ff082dc29e012816Joe Onorato            doOneBackup(oldState, data, newState, header, helper);
764ababd922eac5931e0222862ff082dc29e012816Joe Onorato        }
774ababd922eac5931e0222862ff082dc29e012816Joe Onorato    }
784ababd922eac5931e0222862ff082dc29e012816Joe Onorato
794ababd922eac5931e0222862ff082dc29e012816Joe Onorato    private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
804ababd922eac5931e0222862ff082dc29e012816Joe Onorato            ParcelFileDescriptor newState, Header header, BackupHelper helper)
814ababd922eac5931e0222862ff082dc29e012816Joe Onorato            throws IOException {
824ababd922eac5931e0222862ff082dc29e012816Joe Onorato        int err;
834ababd922eac5931e0222862ff082dc29e012816Joe Onorato        FileDescriptor newStateFD = newState.getFileDescriptor();
844ababd922eac5931e0222862ff082dc29e012816Joe Onorato
854ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // allocate space for the header in the file
864ababd922eac5931e0222862ff082dc29e012816Joe Onorato        int pos = allocateHeader_native(header, newStateFD);
874ababd922eac5931e0222862ff082dc29e012816Joe Onorato        if (pos < 0) {
884ababd922eac5931e0222862ff082dc29e012816Joe Onorato            throw new IOException("allocateHeader_native failed (error " + pos + ")");
894ababd922eac5931e0222862ff082dc29e012816Joe Onorato        }
904ababd922eac5931e0222862ff082dc29e012816Joe Onorato
914ababd922eac5931e0222862ff082dc29e012816Joe Onorato        data.setKeyPrefix(header.keyPrefix);
924ababd922eac5931e0222862ff082dc29e012816Joe Onorato
934ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // do the backup
944ababd922eac5931e0222862ff082dc29e012816Joe Onorato        helper.performBackup(oldState, data, newState);
954ababd922eac5931e0222862ff082dc29e012816Joe Onorato
964ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // fill in the header (seeking back to pos).  The file pointer will be returned to
974ababd922eac5931e0222862ff082dc29e012816Joe Onorato        // where it was at the end of performBackup.  Header.chunkSize will not be filled in.
984ababd922eac5931e0222862ff082dc29e012816Joe Onorato        err = writeHeader_native(header, newStateFD, pos);
994ababd922eac5931e0222862ff082dc29e012816Joe Onorato        if (err != 0) {
1004ababd922eac5931e0222862ff082dc29e012816Joe Onorato            throw new IOException("writeHeader_native failed (error " + err + ")");
10106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        }
10206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    }
10306290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
1045cbbf5652a78902ac3382dc4a3583bc5b0351027Christopher Tate    public void performRestore(BackupDataInput input, int appVersionCode,
1055cbbf5652a78902ac3382dc4a3583bc5b0351027Christopher Tate            ParcelFileDescriptor newState)
10606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato            throws IOException {
10783248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato        boolean alreadyComplained = false;
10883248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato
1095f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato        BackupDataInputStream stream = new BackupDataInputStream(input);
1105f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato        while (input.readNextHeader()) {
11183248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato
1125f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato            String rawKey = input.getKey();
1135f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato            int pos = rawKey.indexOf(':');
1145f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato            if (pos > 0) {
1155f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato                String prefix = rawKey.substring(0, pos);
11606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato                BackupHelper helper = mHelpers.get(prefix);
1175f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato                if (helper != null) {
1185f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato                    stream.dataSize = input.getDataSize();
1195f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato                    stream.key = rawKey.substring(pos+1);
120efd0fab04b96d7ab0c1d8bf3b79397c8621e31c5Joe Onorato                    helper.restoreEntity(stream);
12183248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                } else {
12283248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                    if (!alreadyComplained) {
12383248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                        Log.w(TAG, "Couldn't find helper for: '" + rawKey + "'");
12483248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                        alreadyComplained = true;
12583248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                    }
12683248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                }
12783248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato            } else {
12883248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                if (!alreadyComplained) {
12983248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                    Log.w(TAG, "Entity with no prefix: '" + rawKey + "'");
13083248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato                    alreadyComplained = true;
1315f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato                }
1325f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato            }
1335f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato            input.skipEntityData(); // In case they didn't consume the data.
1345f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato        }
135d2d9ceb7305d593c1b767bbb05de0082a9af4109Joe Onorato
13606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        // Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
13706290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        for (BackupHelper helper: mHelpers.values()) {
138e28290e21f908b4e917099ff2aa41e3aab9310c2Christopher Tate            helper.writeNewStateDescription(newState);
139d2d9ceb7305d593c1b767bbb05de0082a9af4109Joe Onorato        }
1405f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato    }
1414ababd922eac5931e0222862ff082dc29e012816Joe Onorato
1424ababd922eac5931e0222862ff082dc29e012816Joe Onorato    private static native int readHeader_native(Header h, FileDescriptor fd);
1434ababd922eac5931e0222862ff082dc29e012816Joe Onorato    private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
1444ababd922eac5931e0222862ff082dc29e012816Joe Onorato
1454ababd922eac5931e0222862ff082dc29e012816Joe Onorato    private static native int allocateHeader_native(Header h, FileDescriptor fd);
1464ababd922eac5931e0222862ff082dc29e012816Joe Onorato    private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
1475f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato}
148d2d9ceb7305d593c1b767bbb05de0082a9af4109Joe Onorato
149