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.content.Context; 20847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.content.Intent; 21cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reckimport android.content.SharedPreferences; 22847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.os.Bundle; 23378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reckimport android.os.Handler; 24bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reckimport android.os.Message; 25847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.os.Parcel; 26847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport android.util.Log; 27847b532045e3cb117a847ebb956c9919401f332dJohn Reck 28847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.ByteArrayOutputStream; 29a4816530199095b8e7ad0ebd6ac9f22dff32ea2aJohn Reckimport java.io.File; 30847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.FileInputStream; 31847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.FileNotFoundException; 32847b532045e3cb117a847ebb956c9919401f332dJohn Reckimport java.io.FileOutputStream; 33679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdochimport java.io.IOException; 34847b532045e3cb117a847ebb956c9919401f332dJohn Reck 35847b532045e3cb117a847ebb956c9919401f332dJohn Reckpublic class CrashRecoveryHandler { 36847b532045e3cb117a847ebb956c9919401f332dJohn Reck 376c2e2f34718043f36488b4a94879dc2605aaac49John Reck private static final boolean LOGV_ENABLED = Browser.LOGV_ENABLED; 38847b532045e3cb117a847ebb956c9919401f332dJohn Reck private static final String LOGTAG = "BrowserCrashRecovery"; 39847b532045e3cb117a847ebb956c9919401f332dJohn Reck private static final String STATE_FILE = "browser_state.parcel"; 40847b532045e3cb117a847ebb956c9919401f332dJohn Reck private static final int BUFFER_SIZE = 4096; 41378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck private static final long BACKUP_DELAY = 500; // 500ms between writes 42cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck /* This is the duration for which we will prompt to restore 43cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck * instead of automatically restoring. The first time the browser crashes, 44cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck * we will automatically restore. If we then crash again within XX minutes, 45cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck * we will prompt instead of automatically restoring. 46cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck */ 47023124e33a36c31cee61b08020189c16ffaf8624John Reck private static final long PROMPT_INTERVAL = 5 * 60 * 1000; // 5 minutes 48378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 49bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private static final int MSG_WRITE_STATE = 1; 50bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private static final int MSG_CLEAR_STATE = 2; 51bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private static final int MSG_PRELOAD_STATE = 3; 52bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck 53378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck private static CrashRecoveryHandler sInstance; 54847b532045e3cb117a847ebb956c9919401f332dJohn Reck 55847b532045e3cb117a847ebb956c9919401f332dJohn Reck private Controller mController; 56bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private Context mContext; 57378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck private Handler mForegroundHandler; 58378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck private Handler mBackgroundHandler; 59bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private boolean mIsPreloading = false; 60bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private boolean mDidPreload = false; 61bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck private Bundle mRecoveryState = null; 62378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 63378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck public static CrashRecoveryHandler initialize(Controller controller) { 64378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck if (sInstance == null) { 65378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck sInstance = new CrashRecoveryHandler(controller); 66378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } else { 67378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck sInstance.mController = controller; 68378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } 69378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck return sInstance; 70378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } 71847b532045e3cb117a847ebb956c9919401f332dJohn Reck 72378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck public static CrashRecoveryHandler getInstance() { 73378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck return sInstance; 74378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } 75378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 76378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck private CrashRecoveryHandler(Controller controller) { 77847b532045e3cb117a847ebb956c9919401f332dJohn Reck mController = controller; 78bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mContext = mController.getActivity().getApplicationContext(); 79378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck mForegroundHandler = new Handler(); 80cadae72b6309303bc7b22e85181222b73e176c32John Reck mBackgroundHandler = new Handler(BackgroundHandler.getLooper()) { 81bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck 82bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck @Override 83bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck public void handleMessage(Message msg) { 84bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck switch (msg.what) { 85bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck case MSG_WRITE_STATE: 863636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount Bundle saveState = (Bundle) msg.obj; 873636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount writeState(saveState); 88bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck break; 89bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck case MSG_CLEAR_STATE: 906c2e2f34718043f36488b4a94879dc2605aaac49John Reck if (LOGV_ENABLED) { 916c2e2f34718043f36488b4a94879dc2605aaac49John Reck Log.v(LOGTAG, "Clearing crash recovery state"); 926c2e2f34718043f36488b4a94879dc2605aaac49John Reck } 93bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck File state = new File(mContext.getCacheDir(), STATE_FILE); 94bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck if (state.exists()) { 95bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck state.delete(); 96bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 97bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck break; 98bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck case MSG_PRELOAD_STATE: 99bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mRecoveryState = loadCrashState(); 100bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck synchronized (CrashRecoveryHandler.this) { 101bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mIsPreloading = false; 102bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mDidPreload = true; 103bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck CrashRecoveryHandler.this.notifyAll(); 104bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 105bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck break; 106bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 107bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 108bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck }; 109847b532045e3cb117a847ebb956c9919401f332dJohn Reck } 110847b532045e3cb117a847ebb956c9919401f332dJohn Reck 111847b532045e3cb117a847ebb956c9919401f332dJohn Reck public void backupState() { 112378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck mForegroundHandler.postDelayed(mCreateState, BACKUP_DELAY); 113378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } 114378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 115378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck private Runnable mCreateState = new Runnable() { 116378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 117378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck @Override 118378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck public void run() { 119378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck try { 1203636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount final Bundle state = mController.createSaveState(); 121bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck Message.obtain(mBackgroundHandler, MSG_WRITE_STATE, state) 122bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck .sendToTarget(); 123378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck // Remove any queued up saves 124378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck mForegroundHandler.removeCallbacks(mCreateState); 125378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } catch (Throwable t) { 126378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck Log.w(LOGTAG, "Failed to save state", t); 127378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck return; 128847b532045e3cb117a847ebb956c9919401f332dJohn Reck } 129378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck } 130378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 131378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck }; 132378a41055fab6c521cdc1d9b2cfeefff2af19e7cJohn Reck 133bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck public void clearState() { 134bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mBackgroundHandler.sendEmptyMessage(MSG_CLEAR_STATE); 135023124e33a36c31cee61b08020189c16ffaf8624John Reck updateLastRecovered(0); 136847b532045e3cb117a847ebb956c9919401f332dJohn Reck } 137847b532045e3cb117a847ebb956c9919401f332dJohn Reck 138023124e33a36c31cee61b08020189c16ffaf8624John Reck private boolean shouldRestore() { 1393636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount BrowserSettings browserSettings = BrowserSettings.getInstance(); 1403636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount long lastRecovered = browserSettings.getLastRecovered(); 141cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck long timeSinceLastRecover = System.currentTimeMillis() - lastRecovered; 1423636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount return (timeSinceLastRecover > PROMPT_INTERVAL) 1433636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount || browserSettings.wasLastRunPaused(); 144cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck } 145cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck 146023124e33a36c31cee61b08020189c16ffaf8624John Reck private void updateLastRecovered(long time) { 1473636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount BrowserSettings browserSettings = BrowserSettings.getInstance(); 1483636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount browserSettings.setLastRecovered(time); 149cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck } 150cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck 1513636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount synchronized private Bundle loadCrashState() { 152023124e33a36c31cee61b08020189c16ffaf8624John Reck if (!shouldRestore()) { 153023124e33a36c31cee61b08020189c16ffaf8624John Reck return null; 154023124e33a36c31cee61b08020189c16ffaf8624John Reck } 1553636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount BrowserSettings browserSettings = BrowserSettings.getInstance(); 1563636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount browserSettings.setLastRunPaused(false); 157cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck Bundle state = null; 158847b532045e3cb117a847ebb956c9919401f332dJohn Reck Parcel parcel = Parcel.obtain(); 159679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch FileInputStream fin = null; 160847b532045e3cb117a847ebb956c9919401f332dJohn Reck try { 161bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck File stateFile = new File(mContext.getCacheDir(), STATE_FILE); 162679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch fin = new FileInputStream(stateFile); 163847b532045e3cb117a847ebb956c9919401f332dJohn Reck ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); 164847b532045e3cb117a847ebb956c9919401f332dJohn Reck byte[] buffer = new byte[BUFFER_SIZE]; 165847b532045e3cb117a847ebb956c9919401f332dJohn Reck int read; 166847b532045e3cb117a847ebb956c9919401f332dJohn Reck while ((read = fin.read(buffer)) > 0) { 167847b532045e3cb117a847ebb956c9919401f332dJohn Reck dataStream.write(buffer, 0, read); 168847b532045e3cb117a847ebb956c9919401f332dJohn Reck } 169847b532045e3cb117a847ebb956c9919401f332dJohn Reck byte[] data = dataStream.toByteArray(); 170847b532045e3cb117a847ebb956c9919401f332dJohn Reck parcel.unmarshall(data, 0, data.length); 171847b532045e3cb117a847ebb956c9919401f332dJohn Reck parcel.setDataPosition(0); 172cfeae6d3dc6f62f2d475bf50c0fc26a0e9c730deJohn Reck state = parcel.readBundle(); 173e659d7b3363a01d238197c65ab032b73c2a0b9d4John Reck if (state != null && !state.isEmpty()) { 174e659d7b3363a01d238197c65ab032b73c2a0b9d4John Reck return state; 175e659d7b3363a01d238197c65ab032b73c2a0b9d4John Reck } 176847b532045e3cb117a847ebb956c9919401f332dJohn Reck } catch (FileNotFoundException e) { 177847b532045e3cb117a847ebb956c9919401f332dJohn Reck // No state to recover 178bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } catch (Throwable e) { 179847b532045e3cb117a847ebb956c9919401f332dJohn Reck Log.w(LOGTAG, "Failed to recover state!", e); 180847b532045e3cb117a847ebb956c9919401f332dJohn Reck } finally { 181847b532045e3cb117a847ebb956c9919401f332dJohn Reck parcel.recycle(); 182679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch if (fin != null) { 183679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch try { 184679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch fin.close(); 185679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch } catch (IOException e) { } 186679da25ab920d163b3f79a9f5410e78c98d557bcBen Murdoch } 187847b532045e3cb117a847ebb956c9919401f332dJohn Reck } 188023124e33a36c31cee61b08020189c16ffaf8624John Reck return null; 189bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 190bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck 191bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck public void startRecovery(Intent intent) { 192bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck synchronized (CrashRecoveryHandler.this) { 193bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck while (mIsPreloading) { 194bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck try { 195bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck CrashRecoveryHandler.this.wait(); 196bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } catch (InterruptedException e) {} 197bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 198bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 199bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck if (!mDidPreload) { 200bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mRecoveryState = loadCrashState(); 201bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 202023124e33a36c31cee61b08020189c16ffaf8624John Reck updateLastRecovered(mRecoveryState != null 203023124e33a36c31cee61b08020189c16ffaf8624John Reck ? System.currentTimeMillis() : 0); 2043636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount mController.doStart(mRecoveryState, intent); 205bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mRecoveryState = null; 206847b532045e3cb117a847ebb956c9919401f332dJohn Reck } 207bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck 208bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck public void preloadCrashState() { 209bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck synchronized (CrashRecoveryHandler.this) { 210bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck if (mIsPreloading) { 211bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck return; 212bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 213bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mIsPreloading = true; 214bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 215bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck mBackgroundHandler.sendEmptyMessage(MSG_PRELOAD_STATE); 216bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck } 217bc490d2c32aac4e12069be1b92bfaf185ff5dbb5John Reck 2183636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount /** 2193636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount * Writes the crash recovery state to a file synchronously. 2203636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount * Errors are swallowed, but logged. 2213636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount * @param state The state to write out 2223636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount */ 2233636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount synchronized void writeState(Bundle state) { 2243636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount if (LOGV_ENABLED) { 2253636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount Log.v(LOGTAG, "Saving crash recovery state"); 2263636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount } 2273636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount Parcel p = Parcel.obtain(); 2283636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount try { 2293636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount state.writeToParcel(p, 0); 2303636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount File stateJournal = new File(mContext.getCacheDir(), 2313636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount STATE_FILE + ".journal"); 2323636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount FileOutputStream fout = new FileOutputStream(stateJournal); 2333636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount fout.write(p.marshall()); 2343636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount fout.close(); 2353636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount File stateFile = new File(mContext.getCacheDir(), 2363636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount STATE_FILE); 2373636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount if (!stateJournal.renameTo(stateFile)) { 2383636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount // Failed to rename, try deleting the existing 2393636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount // file and try again 2403636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount stateFile.delete(); 2413636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount stateJournal.renameTo(stateFile); 2423636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount } 2433636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount } catch (Throwable e) { 2443636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount Log.i(LOGTAG, "Failed to save persistent state", e); 2453636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount } finally { 2463636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount p.recycle(); 2473636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount } 2483636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount } 2493636d0a6d90fd8de55a4894210b2dbe23f32aac9George Mount}