CrashRecoveryHandler.java revision cadae72b6309303bc7b22e85181222b73e176c32
1847b532045e3cb117a847ebb956c9919401f332dJohn Reck/*
2847b532045e3cb117a847ebb956c9919401f332dJohn Reck * Copyright (C) 2011 The Android Open Source Project
3847b532045e3cb117a847ebb956c9919401f332dJohn Reck *
4847b532045e3cb117a847ebb956c9919401f332dJohn Reck * Licensed under the Apache License, Version 2.0 (the "License");
5847b532045e3cb117a847ebb956c9919401f332dJohn Reck * you may not use this file except in compliance with the License.
6847b532045e3cb117a847ebb956c9919401f332dJohn Reck * You may obtain a copy of the License at
7847b532045e3cb117a847ebb956c9919401f332dJohn Reck *
8847b532045e3cb117a847ebb956c9919401f332dJohn Reck *      http://www.apache.org/licenses/LICENSE-2.0
9847b532045e3cb117a847ebb956c9919401f332dJohn Reck *
10847b532045e3cb117a847ebb956c9919401f332dJohn Reck * Unless required by applicable law or agreed to in writing, software
11847b532045e3cb117a847ebb956c9919401f332dJohn Reck * distributed under the License is distributed on an "AS IS" BASIS,
12847b532045e3cb117a847ebb956c9919401f332dJohn Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13847b532045e3cb117a847ebb956c9919401f332dJohn Reck * See the License for the specific language governing permissions and
14847b532045e3cb117a847ebb956c9919401f332dJohn Reck * limitations under the License.
15847b532045e3cb117a847ebb956c9919401f332dJohn Reck */
16847b532045e3cb117a847ebb956c9919401f332dJohn Reck
17847b532045e3cb117a847ebb956c9919401f332dJohn Reckpackage com.android.browser;
18847b532045e3cb117a847ebb956c9919401f332dJohn Reck
19847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.app.AlertDialog;
20847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.content.Context;
21847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.content.DialogInterface;
220b3165d365baa066465f5ae95d93feeec75a0ef3John Reckimport android.content.DialogInterface.OnCancelListener;
23847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.content.DialogInterface.OnClickListener;
24847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.content.Intent;
25cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reckimport android.content.SharedPreferences;
26847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.os.Bundle;
27378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reckimport android.os.Handler;
28bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reckimport android.os.Message;
29847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.os.Parcel;
30847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.util.Log;
31847b532045e3cb117a847ebb956c9919401f332dJohn Reck
32847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.ByteArrayOutputStream;
33a4816530199095b8e7ad0ebd6ac9f22dff32ea2aJohn Reckimport java.io.File;
34847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.FileInputStream;
35847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.FileNotFoundException;
36847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.FileOutputStream;
37847b532045e3cb117a847ebb956c9919401f332dJohn Reck
38847b532045e3cb117a847ebb956c9919401f332dJohn Reckpublic class CrashRecoveryHandler {
39847b532045e3cb117a847ebb956c9919401f332dJohn Reck
40847b532045e3cb117a847ebb956c9919401f332dJohn Reck    private static final String LOGTAG = "BrowserCrashRecovery";
41847b532045e3cb117a847ebb956c9919401f332dJohn Reck    private static final String STATE_FILE = "browser_state.parcel";
42cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    private static final String RECOVERY_PREFERENCES = "browser_recovery_prefs";
43cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    private static final String KEY_LAST_RECOVERED = "last_recovered";
44847b532045e3cb117a847ebb956c9919401f332dJohn Reck    private static final int BUFFER_SIZE = 4096;
45378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    private static final long BACKUP_DELAY = 500; // 500ms between writes
46cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    /* This is the duration for which we will prompt to restore
47cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck     * instead of automatically restoring. The first time the browser crashes,
48cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck     * we will automatically restore. If we then crash again within XX minutes,
49cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck     * we will prompt instead of automatically restoring.
50cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck     */
51cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    private static final long PROMPT_INTERVAL = 30 * 60 * 1000; // 30 minutes
52378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
53bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private static final int MSG_WRITE_STATE = 1;
54bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private static final int MSG_CLEAR_STATE = 2;
55bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private static final int MSG_PRELOAD_STATE = 3;
56bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck
57378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    private static CrashRecoveryHandler sInstance;
58847b532045e3cb117a847ebb956c9919401f332dJohn Reck
59847b532045e3cb117a847ebb956c9919401f332dJohn Reck    private Controller mController;
60bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private Context mContext;
61378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    private Handler mForegroundHandler;
62378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    private Handler mBackgroundHandler;
63bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private boolean mIsPreloading = false;
64bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private boolean mDidPreload = false;
65bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private boolean mShouldPrompt = false;
66bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private Bundle mRecoveryState = null;
67378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
68378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    public static CrashRecoveryHandler initialize(Controller controller) {
69378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        if (sInstance == null) {
70378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck            sInstance = new CrashRecoveryHandler(controller);
71378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        } else {
72378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck            sInstance.mController = controller;
73378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        }
74378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        return sInstance;
75378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    }
76847b532045e3cb117a847ebb956c9919401f332dJohn Reck
77378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    public static CrashRecoveryHandler getInstance() {
78378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        return sInstance;
79378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    }
80378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
81378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    private CrashRecoveryHandler(Controller controller) {
82847b532045e3cb117a847ebb956c9919401f332dJohn Reck        mController = controller;
83bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        mContext = mController.getActivity().getApplicationContext();
84378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        mForegroundHandler = new Handler();
85cadae72b6309303bc7b22e85181222b73e176c32John Reck        mBackgroundHandler = new Handler(BackgroundHandler.getLooper()) {
86bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck
87bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            @Override
88bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            public void handleMessage(Message msg) {
89bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                switch (msg.what) {
90bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                case MSG_WRITE_STATE:
91bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    Parcel p = Parcel.obtain();
92bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    try {
93bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        Bundle state = (Bundle) msg.obj;
94bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        state.writeToParcel(p, 0);
95bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        File stateFile = new File(mContext.getCacheDir(), STATE_FILE);
96bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        FileOutputStream fout = new FileOutputStream(stateFile);
97bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        fout.write(p.marshall());
98bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        fout.close();
99bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    } catch (Throwable e) {
100bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        Log.i(LOGTAG, "Failed to save persistent state", e);
101bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    } finally {
102bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        p.recycle();
103bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    }
104bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    break;
105bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                case MSG_CLEAR_STATE:
106bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    File state = new File(mContext.getCacheDir(), STATE_FILE);
107bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    if (state.exists()) {
108bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        state.delete();
109bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    }
110bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    break;
111bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                case MSG_PRELOAD_STATE:
112bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    mRecoveryState = loadCrashState();
113bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    mShouldPrompt = shouldPrompt();
114bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    synchronized (CrashRecoveryHandler.this) {
115bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        mIsPreloading = false;
116bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        mDidPreload = true;
117bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        CrashRecoveryHandler.this.notifyAll();
118bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    }
119bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    break;
120bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                }
121bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            }
122bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        };
123847b532045e3cb117a847ebb956c9919401f332dJohn Reck    }
124847b532045e3cb117a847ebb956c9919401f332dJohn Reck
125847b532045e3cb117a847ebb956c9919401f332dJohn Reck    public void backupState() {
126378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        mForegroundHandler.postDelayed(mCreateState, BACKUP_DELAY);
127378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    }
128378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
129378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    private Runnable mCreateState = new Runnable() {
130378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
131378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        @Override
132378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        public void run() {
133378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck            try {
134378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck                final Bundle state = new Bundle();
135378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck                mController.onSaveInstanceState(state, false);
136bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                Message.obtain(mBackgroundHandler, MSG_WRITE_STATE, state)
137bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        .sendToTarget();
138378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck                // Remove any queued up saves
139378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck                mForegroundHandler.removeCallbacks(mCreateState);
140378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck            } catch (Throwable t) {
141378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck                Log.w(LOGTAG, "Failed to save state", t);
142378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck                return;
143847b532045e3cb117a847ebb956c9919401f332dJohn Reck            }
144378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck        }
145378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
146378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck    };
147378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck
148bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    public void clearState() {
149bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        mBackgroundHandler.sendEmptyMessage(MSG_CLEAR_STATE);
150847b532045e3cb117a847ebb956c9919401f332dJohn Reck    }
151847b532045e3cb117a847ebb956c9919401f332dJohn Reck
152847b532045e3cb117a847ebb956c9919401f332dJohn Reck    public void promptToRecover(final Bundle state, final Intent intent) {
153847b532045e3cb117a847ebb956c9919401f332dJohn Reck        new AlertDialog.Builder(mController.getActivity())
154847b532045e3cb117a847ebb956c9919401f332dJohn Reck                .setTitle(R.string.recover_title)
155847b532045e3cb117a847ebb956c9919401f332dJohn Reck                .setMessage(R.string.recover_prompt)
15624f1826440334ba8a3d2453699c51c1a4b117c7bJohn Reck                .setIcon(R.mipmap.ic_launcher_browser)
157847b532045e3cb117a847ebb956c9919401f332dJohn Reck                .setPositiveButton(R.string.recover_yes, new OnClickListener() {
158847b532045e3cb117a847ebb956c9919401f332dJohn Reck                    @Override
159847b532045e3cb117a847ebb956c9919401f332dJohn Reck                    public void onClick(DialogInterface dialog, int which) {
160cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck                        updateLastRecovered();
161847b532045e3cb117a847ebb956c9919401f332dJohn Reck                        mController.doStart(state, intent);
162847b532045e3cb117a847ebb956c9919401f332dJohn Reck                    }
163847b532045e3cb117a847ebb956c9919401f332dJohn Reck                })
164847b532045e3cb117a847ebb956c9919401f332dJohn Reck                .setNegativeButton(R.string.recover_no, new OnClickListener() {
165847b532045e3cb117a847ebb956c9919401f332dJohn Reck                    @Override
166847b532045e3cb117a847ebb956c9919401f332dJohn Reck                    public void onClick(DialogInterface dialog, int which) {
1670b3165d365baa066465f5ae95d93feeec75a0ef3John Reck                        dialog.cancel();
1680b3165d365baa066465f5ae95d93feeec75a0ef3John Reck                    }
1690b3165d365baa066465f5ae95d93feeec75a0ef3John Reck                })
1700b3165d365baa066465f5ae95d93feeec75a0ef3John Reck                .setOnCancelListener(new OnCancelListener() {
1710b3165d365baa066465f5ae95d93feeec75a0ef3John Reck                    @Override
1720b3165d365baa066465f5ae95d93feeec75a0ef3John Reck                    public void onCancel(DialogInterface dialog) {
173bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                        clearState();
174847b532045e3cb117a847ebb956c9919401f332dJohn Reck                        mController.doStart(null, intent);
175847b532045e3cb117a847ebb956c9919401f332dJohn Reck                    }
176847b532045e3cb117a847ebb956c9919401f332dJohn Reck                })
177847b532045e3cb117a847ebb956c9919401f332dJohn Reck                .show();
178847b532045e3cb117a847ebb956c9919401f332dJohn Reck    }
179847b532045e3cb117a847ebb956c9919401f332dJohn Reck
180cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    private boolean shouldPrompt() {
181bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        SharedPreferences prefs = mContext.getSharedPreferences(
182cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck                RECOVERY_PREFERENCES, Context.MODE_PRIVATE);
183bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        long lastRecovered = prefs.getLong(KEY_LAST_RECOVERED, 0);
184cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck        long timeSinceLastRecover = System.currentTimeMillis() - lastRecovered;
185cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck        if (timeSinceLastRecover > PROMPT_INTERVAL) {
186cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck            return false;
187cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck        }
188cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck        return true;
189cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    }
190cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck
191cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    private void updateLastRecovered() {
192bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        SharedPreferences prefs = mContext.getSharedPreferences(
193cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck                RECOVERY_PREFERENCES, Context.MODE_PRIVATE);
194cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck        prefs.edit()
195cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck            .putLong(KEY_LAST_RECOVERED, System.currentTimeMillis())
196cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck            .commit();
197cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck    }
198cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck
199bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    private Bundle loadCrashState() {
200cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck        Bundle state = null;
201847b532045e3cb117a847ebb956c9919401f332dJohn Reck        Parcel parcel = Parcel.obtain();
202847b532045e3cb117a847ebb956c9919401f332dJohn Reck        try {
203bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            File stateFile = new File(mContext.getCacheDir(), STATE_FILE);
204a4816530199095b8e7ad0ebd6ac9f22dff32ea2aJohn Reck            FileInputStream fin = new FileInputStream(stateFile);
205847b532045e3cb117a847ebb956c9919401f332dJohn Reck            ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
206847b532045e3cb117a847ebb956c9919401f332dJohn Reck            byte[] buffer = new byte[BUFFER_SIZE];
207847b532045e3cb117a847ebb956c9919401f332dJohn Reck            int read;
208847b532045e3cb117a847ebb956c9919401f332dJohn Reck            while ((read = fin.read(buffer)) > 0) {
209847b532045e3cb117a847ebb956c9919401f332dJohn Reck                dataStream.write(buffer, 0, read);
210847b532045e3cb117a847ebb956c9919401f332dJohn Reck            }
211847b532045e3cb117a847ebb956c9919401f332dJohn Reck            byte[] data = dataStream.toByteArray();
212847b532045e3cb117a847ebb956c9919401f332dJohn Reck            parcel.unmarshall(data, 0, data.length);
213847b532045e3cb117a847ebb956c9919401f332dJohn Reck            parcel.setDataPosition(0);
214cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck            state = parcel.readBundle();
215847b532045e3cb117a847ebb956c9919401f332dJohn Reck        } catch (FileNotFoundException e) {
216847b532045e3cb117a847ebb956c9919401f332dJohn Reck            // No state to recover
217cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck            state = null;
218bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        } catch (Throwable e) {
219847b532045e3cb117a847ebb956c9919401f332dJohn Reck            Log.w(LOGTAG, "Failed to recover state!", e);
220cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck            state = null;
221847b532045e3cb117a847ebb956c9919401f332dJohn Reck        } finally {
222847b532045e3cb117a847ebb956c9919401f332dJohn Reck            parcel.recycle();
223847b532045e3cb117a847ebb956c9919401f332dJohn Reck        }
224bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        return state;
225bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    }
226bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck
227bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    public void startRecovery(Intent intent) {
228bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        synchronized (CrashRecoveryHandler.this) {
229bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            while (mIsPreloading) {
230bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                try {
231bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                    CrashRecoveryHandler.this.wait();
232bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                } catch (InterruptedException e) {}
233bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            }
234bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        }
235bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        if (!mDidPreload) {
236bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            mRecoveryState = loadCrashState();
237bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            mShouldPrompt = shouldPrompt();
238bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        }
239bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        if (mShouldPrompt) {
240bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            promptToRecover(mRecoveryState, intent);
241bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            return;
242bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        } else {
243bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            updateLastRecovered();
244bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        }
245bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        mController.doStart(mRecoveryState, intent);
246bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        mRecoveryState = null;
247847b532045e3cb117a847ebb956c9919401f332dJohn Reck    }
248bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck
249bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    public void preloadCrashState() {
250bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        synchronized (CrashRecoveryHandler.this) {
251bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            if (mIsPreloading) {
252bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck                return;
253bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            }
254bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck            mIsPreloading = true;
255bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        }
256bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck        mBackgroundHandler.sendEmptyMessage(MSG_PRELOAD_STATE);
257bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck    }
258bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck
259847b532045e3cb117a847ebb956c9919401f332dJohn Reck}
260