Change SWE app properties back to stock Android
- Changed project package name from com.android.swe.browser
back to com.android.browser along with code references to
old package name.
- Changes to AndroidManifest making it conform closer to stock
browser manifest.
- Changed app and apk name back to Browser.
Change-Id: I778ee1d1197bd50bd4a4850eef6d1d7f4ef0ad0b
diff --git a/src/com/android/browser/CrashRecoveryHandler.java b/src/com/android/browser/CrashRecoveryHandler.java
new file mode 100644
index 0000000..bcdf8b0
--- /dev/null
+++ b/src/com/android/browser/CrashRecoveryHandler.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class CrashRecoveryHandler {
+
+ private static final boolean LOGV_ENABLED = Browser.LOGV_ENABLED;
+ private static final String LOGTAG = "BrowserCrashRecovery";
+ private static final String STATE_FILE = "browser_state.parcel";
+ private static final int BUFFER_SIZE = 4096;
+ private static final long BACKUP_DELAY = 500; // 500ms between writes
+ /* This is the duration for which we will prompt to restore
+ * instead of automatically restoring. The first time the browser crashes,
+ * we will automatically restore. If we then crash again within XX minutes,
+ * we will prompt instead of automatically restoring.
+ */
+ private static final long PROMPT_INTERVAL = 5 * 60 * 1000; // 5 minutes
+
+ private static final int MSG_WRITE_STATE = 1;
+ private static final int MSG_CLEAR_STATE = 2;
+ private static final int MSG_PRELOAD_STATE = 3;
+
+ private static CrashRecoveryHandler sInstance;
+
+ private Controller mController;
+ private Context mContext;
+ private Handler mForegroundHandler;
+ private Handler mBackgroundHandler;
+ private boolean mIsPreloading = false;
+ private boolean mDidPreload = false;
+ private Bundle mRecoveryState = null;
+
+ public static CrashRecoveryHandler initialize(Controller controller) {
+ if (sInstance == null) {
+ sInstance = new CrashRecoveryHandler(controller);
+ } else {
+ sInstance.mController = controller;
+ }
+ return sInstance;
+ }
+
+ public static CrashRecoveryHandler getInstance() {
+ return sInstance;
+ }
+
+ private CrashRecoveryHandler(Controller controller) {
+ mController = controller;
+ mContext = mController.getActivity().getApplicationContext();
+ mForegroundHandler = new Handler();
+ mBackgroundHandler = new Handler(BackgroundHandler.getLooper()) {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_WRITE_STATE:
+ Bundle saveState = (Bundle) msg.obj;
+ writeState(saveState);
+ break;
+ case MSG_CLEAR_STATE:
+ if (LOGV_ENABLED) {
+ Log.v(LOGTAG, "Clearing crash recovery state");
+ }
+ File state = new File(mContext.getCacheDir(), STATE_FILE);
+ if (state.exists()) {
+ state.delete();
+ }
+ break;
+ case MSG_PRELOAD_STATE:
+ mRecoveryState = loadCrashState();
+ synchronized (CrashRecoveryHandler.this) {
+ mIsPreloading = false;
+ mDidPreload = true;
+ CrashRecoveryHandler.this.notifyAll();
+ }
+ break;
+ }
+ }
+ };
+ }
+
+ public void backupState() {
+ mForegroundHandler.postDelayed(mCreateState, BACKUP_DELAY);
+ }
+
+ private Runnable mCreateState = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ final Bundle state = mController.createSaveState();
+ Message.obtain(mBackgroundHandler, MSG_WRITE_STATE, state)
+ .sendToTarget();
+ // Remove any queued up saves
+ mForegroundHandler.removeCallbacks(mCreateState);
+ } catch (Throwable t) {
+ Log.w(LOGTAG, "Failed to save state", t);
+ return;
+ }
+ }
+
+ };
+
+ public void clearState() {
+ clearState(false);
+ }
+
+ /**
+ * Clear cached state files.
+ *
+ * @param block If block, clear state files in the caller thread, otherwise
+ * do it in a worker thread.
+ */
+ void clearState(boolean block) {
+ if (block) {
+ if (mContext != null) {
+ File state = new File(mContext.getCacheDir(), STATE_FILE);
+ if (state.exists()) {
+ state.delete();
+ }
+ }
+ } else {
+ mBackgroundHandler.sendEmptyMessage(MSG_CLEAR_STATE);
+ }
+ updateLastRecovered(0);
+ }
+
+ private boolean shouldRestore() {
+ BrowserSettings browserSettings = BrowserSettings.getInstance();
+ long lastRecovered = browserSettings.getLastRecovered();
+ long timeSinceLastRecover = System.currentTimeMillis() - lastRecovered;
+ return (timeSinceLastRecover > PROMPT_INTERVAL)
+ || browserSettings.wasLastRunPaused();
+ }
+
+ private void updateLastRecovered(long time) {
+ BrowserSettings browserSettings = BrowserSettings.getInstance();
+ browserSettings.setLastRecovered(time);
+ }
+
+ synchronized private Bundle loadCrashState() {
+ if (!shouldRestore()) {
+ return null;
+ }
+ BrowserSettings browserSettings = BrowserSettings.getInstance();
+ browserSettings.setLastRunPaused(false);
+ Bundle state = null;
+ Parcel parcel = Parcel.obtain();
+ FileInputStream fin = null;
+ try {
+ File stateFile = new File(mContext.getCacheDir(), STATE_FILE);
+ fin = new FileInputStream(stateFile);
+ ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int read;
+ while ((read = fin.read(buffer)) > 0) {
+ dataStream.write(buffer, 0, read);
+ }
+ byte[] data = dataStream.toByteArray();
+ parcel.unmarshall(data, 0, data.length);
+ parcel.setDataPosition(0);
+ state = parcel.readBundle();
+ if (state != null && !state.isEmpty()) {
+ return state;
+ }
+ } catch (FileNotFoundException e) {
+ // No state to recover
+ } catch (Throwable e) {
+ Log.w(LOGTAG, "Failed to recover state!", e);
+ } finally {
+ parcel.recycle();
+ if (fin != null) {
+ try {
+ fin.close();
+ } catch (IOException e) { }
+ }
+ }
+ return null;
+ }
+
+ public void startRecovery(Intent intent) {
+ synchronized (CrashRecoveryHandler.this) {
+ while (mIsPreloading) {
+ try {
+ CrashRecoveryHandler.this.wait();
+ } catch (InterruptedException e) {}
+ }
+ }
+ if (!mDidPreload) {
+ mRecoveryState = loadCrashState();
+ }
+ updateLastRecovered(mRecoveryState != null
+ ? System.currentTimeMillis() : 0);
+ mController.doStart(mRecoveryState, intent);
+ mRecoveryState = null;
+ }
+
+ public void preloadCrashState() {
+ synchronized (CrashRecoveryHandler.this) {
+ if (mIsPreloading) {
+ return;
+ }
+ mIsPreloading = true;
+ }
+ mBackgroundHandler.sendEmptyMessage(MSG_PRELOAD_STATE);
+ }
+
+ /**
+ * Writes the crash recovery state to a file synchronously.
+ * Errors are swallowed, but logged.
+ * @param state The state to write out
+ */
+ synchronized void writeState(Bundle state) {
+ if (LOGV_ENABLED) {
+ Log.v(LOGTAG, "Saving crash recovery state");
+ }
+ Parcel p = Parcel.obtain();
+ try {
+ state.writeToParcel(p, 0);
+ File stateJournal = new File(mContext.getCacheDir(),
+ STATE_FILE + ".journal");
+ FileOutputStream fout = new FileOutputStream(stateJournal);
+ fout.write(p.marshall());
+ fout.close();
+ File stateFile = new File(mContext.getCacheDir(),
+ STATE_FILE);
+ if (!stateJournal.renameTo(stateFile)) {
+ // Failed to rename, try deleting the existing
+ // file and try again
+ stateFile.delete();
+ stateJournal.renameTo(stateFile);
+ }
+ } catch (Throwable e) {
+ Log.i(LOGTAG, "Failed to save persistent state", e);
+ } finally {
+ p.recycle();
+ }
+ }
+}