summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/browser/AutoFillSettingsFragment.java81
-rw-r--r--src/com/android/browser/AutofillHandler.java52
-rw-r--r--src/com/android/browser/BrowserActivity.java10
-rw-r--r--src/com/android/browser/BrowserSettings.java45
-rw-r--r--src/com/android/browser/Controller.java114
-rw-r--r--src/com/android/browser/CrashRecoveryHandler.java89
-rw-r--r--src/com/android/browser/PreferenceKeys.java13
7 files changed, 263 insertions, 141 deletions
diff --git a/src/com/android/browser/AutoFillSettingsFragment.java b/src/com/android/browser/AutoFillSettingsFragment.java
index 04f45b5..7be657d 100644
--- a/src/com/android/browser/AutoFillSettingsFragment.java
+++ b/src/com/android/browser/AutoFillSettingsFragment.java
@@ -52,7 +52,9 @@ public class AutoFillSettingsFragment extends Fragment {
private EditText mCountryEdit;
private EditText mPhoneEdit;
- private Button mSaveButton;
+ private MenuItem mSaveMenuItem;
+
+ private boolean mInitialised;
// Used to display toast after DB interactions complete.
private Handler mHandler;
@@ -87,7 +89,7 @@ public class AutoFillSettingsFragment extends Fragment {
mPhoneEdit.setError(null);
}
- updateButtonState();
+ updateSaveMenuItemState();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -99,7 +101,7 @@ public class AutoFillSettingsFragment extends Fragment {
private class FieldChangedListener implements TextWatcher {
public void afterTextChanged(Editable s) {
- updateButtonState();
+ updateSaveMenuItemState();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -147,11 +149,14 @@ public class AutoFillSettingsFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.autofill_profile_editor, menu);
+ mSaveMenuItem = menu.findItem(R.id.autofill_profile_editor_save_profile_menu_id);
+ updateSaveMenuItemState();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.autofill_profile_editor_delete_profile_menu_id) {
+ switch (item.getItemId()) {
+ case R.id.autofill_profile_editor_delete_profile_menu_id:
// Clear the UI.
mFullNameEdit.setText("");
mEmailEdit.setText("");
@@ -168,10 +173,30 @@ public class AutoFillSettingsFragment extends Fragment {
// trigger the current profile to get deleted from the DB.
mSettings.setAutoFillProfile(null,
mHandler.obtainMessage(PROFILE_DELETED_MSG));
- updateButtonState();
+ updateSaveMenuItemState();
+ return true;
+
+ case R.id.autofill_profile_editor_save_profile_menu_id:
+ AutoFillProfile newProfile = new AutoFillProfile(
+ mUniqueId,
+ mFullNameEdit.getText().toString(),
+ mEmailEdit.getText().toString(),
+ mCompanyEdit.getText().toString(),
+ mAddressLine1Edit.getText().toString(),
+ mAddressLine2Edit.getText().toString(),
+ mCityEdit.getText().toString(),
+ mStateEdit.getText().toString(),
+ mZipEdit.getText().toString(),
+ mCountryEdit.getText().toString(),
+ mPhoneEdit.getText().toString());
+
+ mSettings.setAutoFillProfile(newProfile,
+ mHandler.obtainMessage(PROFILE_SAVED_MSG));
return true;
+
+ default:
+ return false;
}
- return false;
}
@Override
@@ -203,27 +228,6 @@ public class AutoFillSettingsFragment extends Fragment {
mCountryEdit.addTextChangedListener(mFieldChangedListener);
mPhoneEdit.addTextChangedListener(new PhoneNumberValidator());
- mSaveButton = (Button)v.findViewById(R.id.autofill_profile_editor_save_button);
- mSaveButton.setOnClickListener(new OnClickListener() {
- public void onClick(View button) {
- AutoFillProfile newProfile = new AutoFillProfile(
- mUniqueId,
- mFullNameEdit.getText().toString(),
- mEmailEdit.getText().toString(),
- mCompanyEdit.getText().toString(),
- mAddressLine1Edit.getText().toString(),
- mAddressLine2Edit.getText().toString(),
- mCityEdit.getText().toString(),
- mStateEdit.getText().toString(),
- mZipEdit.getText().toString(),
- mCountryEdit.getText().toString(),
- mPhoneEdit.getText().toString());
-
- mSettings.setAutoFillProfile(newProfile,
- mHandler.obtainMessage(PROFILE_SAVED_MSG));
- }
- });
-
// Populate the text boxes with any pre existing AutoFill data.
AutoFillProfile activeProfile = mSettings.getAutoFillProfile();
if (activeProfile != null) {
@@ -239,14 +243,25 @@ public class AutoFillSettingsFragment extends Fragment {
mPhoneEdit.setText(activeProfile.getPhoneNumber());
}
- updateButtonState();
+ mInitialised = true;
+
+ updateSaveMenuItemState();
return v;
}
- public void updateButtonState() {
+ private void updateSaveMenuItemState() {
+ if (mSaveMenuItem == null) {
+ return;
+ }
- boolean valid = (mFullNameEdit.getText().toString().length() > 0 ||
+ if (!mInitialised) {
+ mSaveMenuItem.setEnabled(false);
+ return;
+ }
+
+ boolean currentState = mSaveMenuItem.isEnabled();
+ boolean newState = (mFullNameEdit.getText().toString().length() > 0 ||
mEmailEdit.getText().toString().length() > 0 ||
mCompanyEdit.getText().toString().length() > 0 ||
mAddressLine1Edit.getText().toString().length() > 0 ||
@@ -257,9 +272,9 @@ public class AutoFillSettingsFragment extends Fragment {
mCountryEdit.getText().toString().length() > 0) &&
mPhoneEdit.getError() == null;
- // Only enable the save buttons if we have at least one field completed
- // and the phone number (if present is valid).
- mSaveButton.setEnabled(valid);
+ if (currentState != newState) {
+ mSaveMenuItem.setEnabled(newState);
+ }
}
private void closeEditor() {
diff --git a/src/com/android/browser/AutofillHandler.java b/src/com/android/browser/AutofillHandler.java
index c4b14d7..dc23d00 100644
--- a/src/com/android/browser/AutofillHandler.java
+++ b/src/com/android/browser/AutofillHandler.java
@@ -21,9 +21,11 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Message;
import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
import android.webkit.WebSettings.AutoFillProfile;
import java.util.concurrent.CountDownLatch;
@@ -114,8 +116,58 @@ public class AutofillHandler {
c.close();
autoFillDb.close();
+ if (mAutoFillProfile == null) {
+ // We did not load a profile from disk. Try to populate one with the user's
+ // "me" contact.
+ final Uri profileUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
+ ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
+
+ String name = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+ // Only attempt to read other data and set a profile if we could successfully
+ // get a name.
+ if (name != null) {
+ String email = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.Email.ADDRESS,
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
+ String phone = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+ String company = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.Organization.COMPANY,
+ ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
+
+ // Can't easily get structured postal address information (even using
+ // CommonDataKinds.StructuredPostal) so omit prepopulating that for now.
+ // When querying structured postal data, it often all comes back as a string
+ // inside the "street" field.
+
+ mAutoFillProfile = new AutoFillProfile(
+ 1, name, email, company, null, null, null, null,
+ null, null, phone);
+ }
+ }
+
mLoaded.countDown();
}
+
+ private String getContactField(Uri uri, String field, String itemType) {
+ String result = null;
+
+ Cursor c = mContext.getContentResolver().query(uri, new String[] { field },
+ ContactsContract.Data.MIMETYPE + "=?", new String[] { itemType }, null);
+
+ try {
+ // Just use the first returned value if we get more than one.
+ if (c.moveToFirst()) {
+ result = c.getString(0);
+ }
+ } finally {
+ c.close();
+ }
+ return result;
+ }
}
public void setAutoFillProfile(AutoFillProfile profile, Message msg) {
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 23aeed5..77fac4f 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -62,7 +62,7 @@ public class BrowserActivity extends Activity {
finish();
return;
}
- mController = new Controller(this, icicle == null);
+ mController = new Controller(this);
boolean xlarge = isTablet(this);
if (xlarge) {
mUi = new XLargeUi(this, mController);
@@ -71,12 +71,8 @@ public class BrowserActivity extends Activity {
}
mController.setUi(mUi);
- Bundle state = getIntent().getBundleExtra(EXTRA_STATE);
- if (state != null && icicle == null) {
- icicle = state;
- }
-
- mController.start(icicle, getIntent());
+ Intent intent = (icicle == null) ? getIntent() : null;
+ mController.start(intent);
}
public static boolean isTablet(Context context) {
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 2369554..dd7bb56 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -174,8 +174,6 @@ public class BrowserSettings implements OnSharedPreferenceChangeListener,
mWebStorageSizeManager = new WebStorageSizeManager(mContext,
new WebStorageSizeManager.StatFsDiskInfo(getAppCachePath()),
new WebStorageSizeManager.WebKitAppCacheInfo(getAppCachePath()));
- // Workaround b/5253777
- CookieManager.getInstance().acceptCookie();
// Workaround b/5254577
mPrefs.registerOnSharedPreferenceChangeListener(BrowserSettings.this);
if (Build.VERSION.CODENAME.equals("REL")) {
@@ -847,4 +845,47 @@ public class BrowserSettings implements OnSharedPreferenceChangeListener,
return mPrefs.getString(PREF_DATA_PRELOAD, getDefaultPreloadSetting());
}
+ // -----------------------------
+ // getter/setters for browser recovery
+ // -----------------------------
+ /**
+ * The last time browser was started.
+ * @return The last browser start time as System.currentTimeMillis. This
+ * can be 0 if this is the first time or the last tab was closed.
+ */
+ public long getLastRecovered() {
+ return mPrefs.getLong(KEY_LAST_RECOVERED, 0);
+ }
+
+ /**
+ * Sets the last browser start time.
+ * @param time The last time as System.currentTimeMillis that the browser
+ * was started. This should be set to 0 if the last tab is closed.
+ */
+ public void setLastRecovered(long time) {
+ mPrefs.edit()
+ .putLong(KEY_LAST_RECOVERED, time)
+ .apply();
+ }
+
+ /**
+ * Used to determine whether or not the previous browser run crashed. Once
+ * the previous state has been determined, the value will be set to false
+ * until a pause is received.
+ * @return true if the last browser run was paused or false if it crashed.
+ */
+ public boolean wasLastRunPaused() {
+ return mPrefs.getBoolean(KEY_LAST_RUN_PAUSED, false);
+ }
+
+ /**
+ * Sets whether or not the last run was a pause or crash.
+ * @param isPaused Set to true When a pause is received or false after
+ * resuming.
+ */
+ public void setLastRunPaused(boolean isPaused) {
+ mPrefs.edit()
+ .putBoolean(KEY_LAST_RUN_PAUSED, isPaused)
+ .apply();
+ }
}
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 8238d77..9683a47 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -218,15 +218,13 @@ public class Controller
private boolean mBlockEvents;
- public Controller(Activity browser, boolean preloadCrashState) {
+ public Controller(Activity browser) {
mActivity = browser;
mSettings = BrowserSettings.getInstance();
mTabControl = new TabControl(this);
mSettings.setController(this);
mCrashRecoveryHandler = CrashRecoveryHandler.initialize(this);
- if (preloadCrashState) {
- mCrashRecoveryHandler.preloadCrashState();
- }
+ mCrashRecoveryHandler.preloadCrashState();
mFactory = new BrowserWebViewFactory(browser);
mUrlHandler = new UrlHandler(this);
@@ -256,16 +254,12 @@ public class Controller
openIconDatabase();
}
- void start(final Bundle icicle, final Intent intent) {
- boolean noCrashRecovery = intent.getBooleanExtra(NO_CRASH_RECOVERY, false);
- if (icicle != null || noCrashRecovery) {
- doStart(icicle, intent, false);
- } else {
- mCrashRecoveryHandler.startRecovery(intent);
- }
+ void start(final Intent intent) {
+ // mCrashRecoverHandler has any previously saved state.
+ mCrashRecoveryHandler.startRecovery(intent);
}
- void doStart(final Bundle icicle, final Intent intent, final boolean fromCrash) {
+ void doStart(final Bundle icicle, final Intent intent) {
// Unless the last browser usage was within 24 hours, destroy any
// remaining incognito tabs.
@@ -293,36 +287,42 @@ public class Controller
GoogleAccountLogin.startLoginIfNeeded(mActivity,
new Runnable() {
@Override public void run() {
- onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs,
- fromCrash);
+ onPreloginFinished(icicle, intent, currentTabId,
+ restoreIncognitoTabs);
}
});
}
private void onPreloginFinished(Bundle icicle, Intent intent, long currentTabId,
- boolean restoreIncognitoTabs, boolean fromCrash) {
+ boolean restoreIncognitoTabs) {
if (currentTabId == -1) {
BackgroundHandler.execute(new PruneThumbnails(mActivity, null));
- final Bundle extra = intent.getExtras();
- // Create an initial tab.
- // If the intent is ACTION_VIEW and data is not null, the Browser is
- // invoked to view the content by another application. In this case,
- // the tab will be close when exit.
- UrlData urlData = IntentHandler.getUrlDataFromIntent(intent);
- Tab t = null;
- if (urlData.isEmpty()) {
- t = openTabToHomePage();
+ if (intent == null) {
+ // This won't happen under common scenarios. The icicle is
+ // not null, but there aren't any tabs to restore.
+ openTabToHomePage();
} else {
- t = openTab(urlData);
- }
- if (t != null) {
- t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID));
- }
- WebView webView = t.getWebView();
- if (extra != null) {
- int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0);
- if (scale > 0 && scale <= 1000) {
- webView.setInitialScale(scale);
+ final Bundle extra = intent.getExtras();
+ // Create an initial tab.
+ // If the intent is ACTION_VIEW and data is not null, the Browser is
+ // invoked to view the content by another application. In this case,
+ // the tab will be close when exit.
+ UrlData urlData = IntentHandler.getUrlDataFromIntent(intent);
+ Tab t = null;
+ if (urlData.isEmpty()) {
+ t = openTabToHomePage();
+ } else {
+ t = openTab(urlData);
+ }
+ if (t != null) {
+ t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID));
+ }
+ WebView webView = t.getWebView();
+ if (extra != null) {
+ int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0);
+ if (scale > 0 && scale <= 1000) {
+ webView.setInitialScale(scale);
+ }
}
}
mUi.updateTabs(mTabControl.getTabs());
@@ -342,9 +342,9 @@ public class Controller
// TabControl.restoreState() will create a new tab even if
// restoring the state fails.
setActiveTab(mTabControl.getCurrentTab());
- // Handle the intent if needed. If icicle != null, we are restoring
- // and the intent will be stale - ignore it.
- if (icicle == null || fromCrash) {
+ // Intent is non-null when framework thinks the browser should be
+ // launching with a new intent (icicle is null).
+ if (intent != null) {
mIntentHandler.onNewIntent(intent);
}
}
@@ -353,7 +353,8 @@ public class Controller
if (jsFlags.trim().length() != 0) {
getCurrentWebView().setJsFlags(jsFlags);
}
- if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(intent.getAction())) {
+ if (intent != null
+ && BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(intent.getAction())) {
bookmarksOrHistoryPicker(ComboViews.Bookmarks);
}
}
@@ -651,18 +652,27 @@ public class Controller
}
void onSaveInstanceState(Bundle outState) {
- // the default implementation requires each view to have an id. As the
- // browser handles the state itself and it doesn't use id for the views,
- // don't call the default implementation. Otherwise it will trigger the
- // warning like this, "couldn't save which view has focus because the
- // focused view XXX has no id".
-
// Save all the tabs
- mTabControl.saveState(outState);
- if (!outState.isEmpty()) {
+ Bundle saveState = createSaveState();
+
+ // crash recovery manages all save & restore state
+ mCrashRecoveryHandler.writeState(saveState);
+ mSettings.setLastRunPaused(true);
+ }
+
+ /**
+ * Save the current state to outState. Does not write the state to
+ * disk.
+ * @return Bundle containing the current state of all tabs.
+ */
+ /* package */ Bundle createSaveState() {
+ Bundle saveState = new Bundle();
+ mTabControl.saveState(saveState);
+ if (!saveState.isEmpty()) {
// Save time so that we know how old incognito tabs (if any) are.
- outState.putSerializable("lastActiveDate", Calendar.getInstance());
+ saveState.putSerializable("lastActiveDate", Calendar.getInstance());
}
+ return saveState;
}
void onResume() {
@@ -670,6 +680,7 @@ public class Controller
Log.e(LOGTAG, "BrowserActivity is already resumed.");
return;
}
+ mSettings.setLastRunPaused(false);
mActivityPaused = false;
Tab current = mTabControl.getCurrentTab();
if (current != null) {
@@ -1509,9 +1520,6 @@ public class Controller
nav.setEnabled(isNavDump);
boolean showDebugSettings = mSettings.isDebugEnabled();
- final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id);
- counter.setVisible(showDebugSettings);
- counter.setEnabled(showDebugSettings);
final MenuItem uaSwitcher = menu.findItem(R.id.ua_desktop_menu_id);
uaSwitcher.setChecked(isDesktopUa);
menu.setGroupVisible(R.id.LIVE_MENU, isLive);
@@ -1666,10 +1674,6 @@ public class Controller
getCurrentTopWebView().debugDump();
break;
- case R.id.dump_counters_menu_id:
- getCurrentTopWebView().dumpV8Counters();
- break;
-
case R.id.zoom_in_menu_id:
getCurrentTopWebView().zoomIn();
break;
@@ -2107,7 +2111,7 @@ public class Controller
manager.addCompletedDownload(target.getName(),
mActivity.getTitle().toString(), false,
uri.getMimeType(), target.getAbsolutePath(),
- (long)uri.getData().length, false);
+ (long)uri.getData().length, true);
} catch (IOException e) {
Log.e(LOGTAG, "Could not save data URL");
} finally {
diff --git a/src/com/android/browser/CrashRecoveryHandler.java b/src/com/android/browser/CrashRecoveryHandler.java
index 3202016..822e82a 100644
--- a/src/com/android/browser/CrashRecoveryHandler.java
+++ b/src/com/android/browser/CrashRecoveryHandler.java
@@ -37,8 +37,6 @@ 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 String RECOVERY_PREFERENCES = "browser_recovery_prefs";
- private static final String KEY_LAST_RECOVERED = "last_recovered";
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
@@ -85,31 +83,8 @@ public class CrashRecoveryHandler {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WRITE_STATE:
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "Saving crash recovery state");
- }
- Parcel p = Parcel.obtain();
- try {
- Bundle state = (Bundle) msg.obj;
- 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();
- }
+ Bundle saveState = (Bundle) msg.obj;
+ writeState(saveState);
break;
case MSG_CLEAR_STATE:
if (LOGV_ENABLED) {
@@ -142,8 +117,7 @@ public class CrashRecoveryHandler {
@Override
public void run() {
try {
- final Bundle state = new Bundle();
- mController.onSaveInstanceState(state);
+ final Bundle state = mController.createSaveState();
Message.obtain(mBackgroundHandler, MSG_WRITE_STATE, state)
.sendToTarget();
// Remove any queued up saves
@@ -162,28 +136,24 @@ public class CrashRecoveryHandler {
}
private boolean shouldRestore() {
- SharedPreferences prefs = mContext.getSharedPreferences(
- RECOVERY_PREFERENCES, Context.MODE_PRIVATE);
- long lastRecovered = prefs.getLong(KEY_LAST_RECOVERED, 0);
+ BrowserSettings browserSettings = BrowserSettings.getInstance();
+ long lastRecovered = browserSettings.getLastRecovered();
long timeSinceLastRecover = System.currentTimeMillis() - lastRecovered;
- if (timeSinceLastRecover > PROMPT_INTERVAL) {
- return true;
- }
- return false;
+ return (timeSinceLastRecover > PROMPT_INTERVAL)
+ || browserSettings.wasLastRunPaused();
}
private void updateLastRecovered(long time) {
- SharedPreferences prefs = mContext.getSharedPreferences(
- RECOVERY_PREFERENCES, Context.MODE_PRIVATE);
- prefs.edit()
- .putLong(KEY_LAST_RECOVERED, time)
- .apply();
+ BrowserSettings browserSettings = BrowserSettings.getInstance();
+ browserSettings.setLastRecovered(time);
}
- private Bundle loadCrashState() {
+ 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;
@@ -231,7 +201,7 @@ public class CrashRecoveryHandler {
}
updateLastRecovered(mRecoveryState != null
? System.currentTimeMillis() : 0);
- mController.doStart(mRecoveryState, intent, true);
+ mController.doStart(mRecoveryState, intent);
mRecoveryState = null;
}
@@ -245,4 +215,35 @@ public class CrashRecoveryHandler {
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();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index ecab008..1a20495 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -104,4 +104,17 @@ public interface PreferenceKeys {
static final String PREF_DATA_PRELOAD = "preload_when";
static final String PREF_LOAD_IMAGES = "load_images";
+ // ----------------------
+ // Keys for browser recovery
+ // ----------------------
+ /**
+ * The last time recovery was started as System.currentTimeMillis.
+ * 0 if not set.
+ */
+ static final String KEY_LAST_RECOVERED = "last_recovered";
+
+ /**
+ * Key for whether or not the last run was paused.
+ */
+ static final String KEY_LAST_RUN_PAUSED = "last_paused";
}