Merge "Part of fix for bug 4997380: Some error types unknown to SslError"
diff --git a/res/layout/suggestion_item.xml b/res/layout/suggestion_item.xml
index 2bd5466..2def735 100644
--- a/res/layout/suggestion_item.xml
+++ b/res/layout/suggestion_item.xml
@@ -50,7 +50,7 @@
             <TextView
                 android:id="@android:id/text1"
                 style="@style/SuggestionLineMedium"
-                android:singleLine="true"
+                android:maxLines="1"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
             <TextView
diff --git a/res/menu/snapshot_go_live.xml b/res/menu/snapshot_go_live.xml
new file mode 100644
index 0000000..aa6b38e
--- /dev/null
+++ b/res/menu/snapshot_go_live.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/snapshot_go_live"
+        android:title="@string/snapshot_go_live" />
+</menu>
+
diff --git a/res/menu/title_context.xml b/res/menu/title_context.xml
deleted file mode 100644
index a60f102..0000000
--- a/res/menu/title_context.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- These are for the context menu for the title bar.  Note that we need a
-            different item from share_page_menu_id, which looks at the top
-            window (possibly a subwindow), because this should be the main
-            window.-->
-    <item android:id="@+id/title_bar_share_page_url"
-        android:title="@string/share_page"/>
-    <item android:id="@+id/title_bar_copy_page_url"
-        android:title="@string/copy_page_url"/>
-</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f4086ea..41b9831 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -573,6 +573,8 @@
     <!-- Do not tranlsate.  Development option -->
     <string name="pref_development_hardware_accel" translatable="false">Enable OpenGL Rendering</string>
     <!-- Do not tranlsate.  Development option -->
+    <string name="pref_development_hardware_accel_skia" translatable="false">Enable HW Accelerated Skia</string>
+    <!-- Do not tranlsate.  Development option -->
     <string name="pref_development_visual_indicator" translatable="false">Enable Visual Indicator</string>
     <!-- Do not tranlsate.  Development option -->
     <string name="js_engine_flags" translatable="false">Set JS flags</string>
@@ -1016,4 +1018,6 @@
     <string name="empty_snapshots_folder">There are no saved pages.</string>
     <!-- Menu option to delete the currently selected saved page [CHAR LIMIT=50] -->
     <string name="remove_snapshot">Delete saved page</string>
+    <!-- Popup menu option to load the live version of a saved page [CHAR LIMIT=20] -->
+    <string name="snapshot_go_live">Go live</string>
 </resources>
diff --git a/res/xml/debug_preferences.xml b/res/xml/debug_preferences.xml
index 1fc4f4c..2d15ab2 100644
--- a/res/xml/debug_preferences.xml
+++ b/res/xml/debug_preferences.xml
@@ -22,6 +22,11 @@
         android:defaultValue="true"
         android:title="@string/pref_development_hardware_accel" />
 
+    <CheckBoxPreference
+        android:key="enable_hardware_accel_skia"
+        android:defaultValue="false"
+        android:title="@string/pref_development_hardware_accel_skia" />
+
     <ListPreference
         android:key="user_agent"
         android:title="@string/pref_development_uastring"
@@ -29,13 +34,13 @@
         android:entryValues="@array/pref_development_ua_values"
         android:defaultValue="0"/>
 
-    <!-- The javascript console is enabled by default when the user has
-         also enabled debug mode by navigating to about:debug. -->
     <CheckBoxPreference
         android:key="enable_visual_indicator"
         android:defaultValue="false"
         android:title="@string/pref_development_visual_indicator" />
 
+    <!-- The javascript console is enabled by default when the user has
+         also enabled debug mode by navigating to about:debug. -->
     <CheckBoxPreference
         android:key="javascript_console"
         android:defaultValue="true"
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 8461d30..cb86c42 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -224,6 +224,7 @@
         settings.setJavaScriptEnabled(enableJavascript());
         settings.setLightTouchEnabled(enableLightTouch());
         settings.setNavDump(enableNavDump());
+        settings.setHardwareAccelSkiaEnabled(isSkiaHardwareAccelerated());
         settings.setShowVisualIndicator(enableVisualIndicator());
         settings.setDefaultTextEncodingName(getDefaultTextEncoding());
         settings.setDefaultZoom(getDefaultZoom());
@@ -454,7 +455,12 @@
     }
 
     public void resetDefaultPreferences() {
-        mPrefs.edit().clear().apply();
+        // Preserve autologin setting
+        long gal = mPrefs.getLong(GoogleAccountLogin.PREF_AUTOLOGIN_TIME, -1);
+        mPrefs.edit()
+                .clear()
+                .putLong(GoogleAccountLogin.PREF_AUTOLOGIN_TIME, gal)
+                .apply();
         syncManagedSettings();
     }
 
@@ -624,6 +630,13 @@
         return mPrefs.getBoolean(PREF_ENABLE_HARDWARE_ACCEL, true);
     }
 
+    public boolean isSkiaHardwareAccelerated() {
+        if (!isDebugEnabled()) {
+            return false;
+        }
+        return mPrefs.getBoolean(PREF_ENABLE_HARDWARE_ACCEL_SKIA, false);
+    }
+
     public int getUserAgent() {
         if (!isDebugEnabled()) {
             return 0;
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 92f448c..fc4c35c 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -1636,7 +1636,6 @@
                 bookmarksOrHistoryPicker(true);
                 break;
 
-            case R.id.title_bar_share_page_url:
             case R.id.share_page_menu_id:
                 Tab currentTab = mTabControl.getCurrentTab();
                 if (null == currentTab) {
@@ -1703,20 +1702,6 @@
         int id = item.getItemId();
         boolean result = true;
         switch (id) {
-            // For the context menu from the title bar
-            case R.id.title_bar_copy_page_url:
-                Tab currentTab = mTabControl.getCurrentTab();
-                if (null == currentTab) {
-                    result = false;
-                    break;
-                }
-                WebView mainView = currentTab.getWebView();
-                if (null == mainView) {
-                    result = false;
-                    break;
-                }
-                copy(mainView.getUrl());
-                break;
             // -- Browser context menu
             case R.id.open_context_menu_id:
             case R.id.save_link_context_menu_id:
diff --git a/src/com/android/browser/CrashRecoveryHandler.java b/src/com/android/browser/CrashRecoveryHandler.java
index fdcdbc6..319ff89 100644
--- a/src/com/android/browser/CrashRecoveryHandler.java
+++ b/src/com/android/browser/CrashRecoveryHandler.java
@@ -243,11 +243,13 @@
             mRecoveryState = loadCrashState();
             mShouldPrompt = shouldPrompt();
         }
-        if (mShouldPrompt) {
-            promptToRecover(mRecoveryState, intent);
-            return;
-        } else {
-            updateLastRecovered();
+        if (mRecoveryState != null && !mRecoveryState.isEmpty()) {
+            if (mShouldPrompt) {
+                promptToRecover(mRecoveryState, intent);
+                return;
+            } else {
+                updateLastRecovered();
+            }
         }
         mController.doStart(mRecoveryState, intent);
         mRecoveryState = null;
diff --git a/src/com/android/browser/GoogleAccountLogin.java b/src/com/android/browser/GoogleAccountLogin.java
index 3896560..2bd3c8c 100644
--- a/src/com/android/browser/GoogleAccountLogin.java
+++ b/src/com/android/browser/GoogleAccountLogin.java
@@ -67,6 +67,7 @@
     private String mLsid;
     private int mState;  // {NONE(0), SID(1), LSID(2)}
     private boolean mTokensInvalidated;
+    private String mUserAgent;
 
     private GoogleAccountLogin(Activity activity, Account account,
             Runnable runnable) {
@@ -74,6 +75,7 @@
         mAccount = account;
         mWebView = new WebView(mActivity);
         mRunnable = runnable;
+        mUserAgent = mWebView.getSettings().getUserAgentString();
 
         // XXX: Doing pre-login causes onResume to skip calling
         // resumeWebViewTimers. So to avoid problems with timers not running, we
@@ -106,17 +108,8 @@
                 .appendQueryParameter("SID", mSid)
                 .appendQueryParameter("LSID", mLsid)
                 .build().toString();
-        // Check mRunnable to see if the request has been canceled.  Otherwise
-        // we might access a destroyed WebView.
-        String ua = null;
-        synchronized (this) {
-            if (mRunnable == null) {
-                return;
-            }
-            ua = mWebView.getSettings().getUserAgentString();
-        }
         // Intentionally not using Proxy.
-        AndroidHttpClient client = AndroidHttpClient.newInstance(ua);
+        AndroidHttpClient client = AndroidHttpClient.newInstance(mUserAgent);
         HttpPost request = new HttpPost(url);
 
         String result = null;
diff --git a/src/com/android/browser/NavigationBarBase.java b/src/com/android/browser/NavigationBarBase.java
index a059352..724dcc8 100644
--- a/src/com/android/browser/NavigationBarBase.java
+++ b/src/com/android/browser/NavigationBarBase.java
@@ -74,7 +74,6 @@
         mFavicon = (ImageView) findViewById(R.id.favicon);
         mUrlInput = (UrlInputView) findViewById(R.id.url);
         mUrlInput.setUrlInputListener(this);
-        mUrlInput.setController(mUiController);
         mUrlInput.setOnFocusChangeListener(this);
         mUrlInput.setSelectAllOnFocus(true);
         mUrlInput.addQueryTextWatcher(this);
@@ -84,6 +83,7 @@
         mTitleBar = titleBar;
         mBaseUi = mTitleBar.getUi();
         mUiController = mTitleBar.getUiController();
+        mUrlInput.setController(mUiController);
     }
 
     public void setLock(Drawable d) {
diff --git a/src/com/android/browser/NavigationBarPhone.java b/src/com/android/browser/NavigationBarPhone.java
index 400b695..388c262 100644
--- a/src/com/android/browser/NavigationBarPhone.java
+++ b/src/com/android/browser/NavigationBarPhone.java
@@ -86,14 +86,6 @@
     }
 
     @Override
-    public void createContextMenu(ContextMenu menu) {
-        Activity activity = mBaseUi.getActivity();
-        MenuInflater inflater = activity.getMenuInflater();
-        inflater.inflate(R.menu.title_context, menu);
-        activity.onCreateContextMenu(menu, this, null);
-    }
-
-    @Override
     protected void setSearchMode(boolean voiceSearchEnabled) {
         boolean showvoicebutton = voiceSearchEnabled &&
                 mUiController.supportsVoiceSearch();
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index ce8ec9f..65218e5 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -48,6 +48,7 @@
     // Keys for debug_preferences.xml
     // ----------------------
     static final String PREF_ENABLE_HARDWARE_ACCEL = "enable_hardware_accel";
+    static final String PREF_ENABLE_HARDWARE_ACCEL_SKIA = "enable_hardware_accel_skia";
     static final String PREF_USER_AGENT = "user_agent";
 
     // ----------------------
diff --git a/src/com/android/browser/SnapshotBar.java b/src/com/android/browser/SnapshotBar.java
index 9fb68cf..ba0bf98 100644
--- a/src/com/android/browser/SnapshotBar.java
+++ b/src/com/android/browser/SnapshotBar.java
@@ -23,16 +23,22 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.ViewConfiguration;
 import android.view.ViewPropertyAnimator;
+import android.webkit.WebView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
 import android.widget.TextView;
 
 import java.text.DateFormat;
 import java.util.Date;
 
-public class SnapshotBar extends LinearLayout implements OnClickListener {
+public class SnapshotBar extends LinearLayout implements OnClickListener,
+        OnMenuItemClickListener {
 
     private static final int MSG_SHOW_TITLE = 1;
     private static final long DURATION_SHOW_DATE = BaseUi.HIDE_TITLEBAR_DELAY;
@@ -171,7 +177,11 @@
         if (mBookmarks == v) {
             mTitleBar.getUiController().bookmarksOrHistoryPicker(false);
         } else if (mGoLive == v) {
-            goLive();
+            PopupMenu popup = new PopupMenu(mContext, mGoLive);
+            Menu menu = popup.getMenu();
+            popup.getMenuInflater().inflate(R.menu.snapshot_go_live, menu);
+            popup.setOnMenuItemClickListener(this);
+            popup.show();
         } else if (mTabSwitcher == v) {
             ((PhoneUi) mTitleBar.getUi()).toggleNavScreen();
         } else if (mOverflowMenu == v) {
@@ -188,6 +198,16 @@
         }
     }
 
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        switch (item.getItemId()) {
+        case R.id.snapshot_go_live:
+            goLive();
+            return true;
+        }
+        return false;
+    }
+
     private void goLive() {
         Tab t = mTitleBar.getUi().getActiveTab();
         t.loadUrl(t.getUrl(), null);
diff --git a/src/com/android/browser/SuggestionsAdapter.java b/src/com/android/browser/SuggestionsAdapter.java
index 9a099cb..e1511b9 100644
--- a/src/com/android/browser/SuggestionsAdapter.java
+++ b/src/com/android/browser/SuggestionsAdapter.java
@@ -17,6 +17,7 @@
 package com.android.browser;
 
 import com.android.browser.provider.BrowserProvider2;
+import com.android.browser.provider.BrowserProvider2.OmniboxSuggestions;
 import com.android.browser.search.SearchEngine;
 
 import android.app.SearchManager;
@@ -53,9 +54,12 @@
     public static final int TYPE_SUGGEST = 4;
     public static final int TYPE_VOICE_SEARCH = 5;
 
-    private static final String[] COMBINED_PROJECTION =
-            {BrowserContract.Combined._ID, BrowserContract.Combined.TITLE,
-                    BrowserContract.Combined.URL, BrowserContract.Combined.IS_BOOKMARK};
+    private static final String[] COMBINED_PROJECTION = {
+            OmniboxSuggestions._ID,
+            OmniboxSuggestions.TITLE,
+            OmniboxSuggestions.URL,
+            OmniboxSuggestions.IS_BOOKMARK
+            };
 
     private static final String COMBINED_SELECTION =
             "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ? OR title LIKE ?)";
@@ -178,9 +182,11 @@
         tv1.setText(Html.fromHtml(item.title));
         if (TextUtils.isEmpty(item.url)) {
             tv2.setVisibility(View.GONE);
+            tv1.setMaxLines(2);
         } else {
             tv2.setVisibility(View.VISIBLE);
             tv2.setText(item.url);
+            tv1.setMaxLines(1);
         }
         int id = -1;
         switch (item.type) {
@@ -470,18 +476,14 @@
                 args[4] = like;
                 selection = COMBINED_SELECTION;
             }
-            Uri.Builder ub = BrowserContract.Combined.CONTENT_URI.buildUpon();
+            Uri.Builder ub = OmniboxSuggestions.CONTENT_URI.buildUpon();
             ub.appendQueryParameter(BrowserContract.PARAM_LIMIT,
                     Integer.toString(Math.max(mLinesLandscape, mLinesPortrait)));
             ub.appendQueryParameter(BrowserProvider2.PARAM_GROUP_BY,
-                    BrowserContract.Combined.URL);
+                    OmniboxSuggestions.URL);
             mCursor =
                     mContext.getContentResolver().query(ub.build(), COMBINED_PROJECTION,
-                            selection,
-                            (constraint != null) ? args : null,
-                            BrowserContract.Combined.IS_BOOKMARK + " DESC, " +
-                            BrowserContract.Combined.VISITS + " DESC, " +
-                            BrowserContract.Combined.DATE_LAST_VISITED + " DESC");
+                            selection, (constraint != null) ? args : null, null);
             if (mCursor != null) {
                 mCursor.moveToFirst();
             }
diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java
index b2c2af8..2b4dd63 100644
--- a/src/com/android/browser/TabBar.java
+++ b/src/com/android/browser/TabBar.java
@@ -231,13 +231,6 @@
         mUi.showTitleBar();
     }
 
-    @Override
-    public void createContextMenu(ContextMenu menu) {
-        MenuInflater inflater = mActivity.getMenuInflater();
-        inflater.inflate(R.menu.title_context, menu);
-        mActivity.onCreateContextMenu(menu, this, null);
-    }
-
     private TabView buildTabView(Tab tab) {
         TabView tabview = new TabView(mActivity, tab);
         mTabMap.put(tab, tabview);
diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java
index aeba7cf..301e09f 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -253,7 +253,7 @@
     @Override
     public void onActionModeStarted(ActionMode mode) {
         if (!mTitleBar.isEditingUrl()) {
-            // hide the fake title bar when CAB is shown
+            // hide the title bar when CAB is shown
             hideTitleBar();
         }
     }
diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java
index fe7a31b..1821b50 100644
--- a/src/com/android/browser/provider/BrowserProvider2.java
+++ b/src/com/android/browser/provider/BrowserProvider2.java
@@ -81,6 +81,15 @@
         public static final String THUMBNAIL = "thumbnail";
     }
 
+    public static interface OmniboxSuggestions {
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                BrowserContract.AUTHORITY_URI, "omnibox_suggestions");
+        public static final String _ID = "_id";
+        public static final String URL = "url";
+        public static final String TITLE = "title";
+        public static final String IS_BOOKMARK = "bookmark";
+    }
+
     static final String TABLE_BOOKMARKS = "bookmarks";
     static final String TABLE_HISTORY = "history";
     static final String TABLE_IMAGES = "images";
@@ -97,6 +106,7 @@
 
     static final String VIEW_ACCOUNTS = "v_accounts";
     static final String VIEW_SNAPSHOTS_COMBINED = "v_snapshots_combined";
+    static final String VIEW_OMNIBOX_SUGGESTIONS = "v_omnibox_suggestions";
 
     static final String FORMAT_COMBINED_JOIN_SUBQUERY_JOIN_IMAGES =
             "history LEFT OUTER JOIN (%s) bookmarks " +
@@ -121,6 +131,7 @@
 
     static final int THUMBNAILS = 10;
     static final int THUMBNAILS_ID = 11;
+    static final int OMNIBOX_SUGGESTIONS = 20;
 
     static final int BOOKMARKS = 1000;
     static final int BOOKMARKS_ID = 1001;
@@ -200,6 +211,7 @@
         matcher.addURI(authority, "settings", SETTINGS);
         matcher.addURI(authority, "thumbnails", THUMBNAILS);
         matcher.addURI(authority, "thumbnails/#", THUMBNAILS_ID);
+        matcher.addURI(authority, "omnibox_suggestions", OMNIBOX_SUGGESTIONS);
 
         // Legacy
         matcher.addURI(LEGACY_AUTHORITY, "searches", SEARCHES);
@@ -346,7 +358,7 @@
 
     final class DatabaseHelper extends SQLiteOpenHelper {
         static final String DATABASE_NAME = "browser2.db";
-        static final int DATABASE_VERSION = 31;
+        static final int DATABASE_VERSION = 32;
         public DatabaseHelper(Context context) {
             super(context, DATABASE_NAME, null, DATABASE_VERSION);
         }
@@ -418,6 +430,11 @@
             }
 
             enableSync(db);
+            createOmniboxSuggestions(db);
+        }
+
+        void createOmniboxSuggestions(SQLiteDatabase db) {
+            db.execSQL(SQL_CREATE_VIEW_OMNIBOX_SUGGESTIONS);
         }
 
         void createThumbnails(SQLiteDatabase db) {
@@ -471,6 +488,7 @@
                             BookmarkColumns.TITLE, // 1
                             BookmarkColumns.FAVICON, // 2
                             BookmarkColumns.TOUCH_ICON, // 3
+                            BookmarkColumns.CREATED, // 4
                             }, BookmarkColumns.BOOKMARK + "!=0", null,
                             null, null, null);
                     if (c != null) {
@@ -478,6 +496,7 @@
                             ContentValues values = new ContentValues();
                             values.put(Bookmarks.URL, c.getString(0));
                             values.put(Bookmarks.TITLE, c.getString(1));
+                            values.put(Bookmarks.DATE_CREATED, c.getInt(4));
                             values.put(Bookmarks.POSITION, 0);
                             values.put(Bookmarks.PARENT, FIXED_ID_ROOT);
                             ContentValues imageValues = new ContentValues();
@@ -497,7 +516,9 @@
                             BookmarkColumns.VISITS, // 2
                             BookmarkColumns.DATE, // 3
                             BookmarkColumns.CREATED, // 4
-                            }, null, null, null, null, null);
+                            }, BookmarkColumns.VISITS + " > 0 OR "
+                            + BookmarkColumns.BOOKMARK + " = 0",
+                            null, null, null, null);
                     if (c != null) {
                         while (c.moveToNext()) {
                             ContentValues values = new ContentValues();
@@ -541,6 +562,9 @@
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion < 32) {
+                createOmniboxSuggestions(db);
+            }
             if (oldVersion < 31) {
                 createThumbnails(db);
             }
@@ -1030,6 +1054,11 @@
                 break;
             }
 
+            case OMNIBOX_SUGGESTIONS: {
+                qb.setTables(VIEW_OMNIBOX_SUGGESTIONS);
+                break;
+            }
+
             default: {
                 throw new UnsupportedOperationException("Unknown URL " + uri.toString());
             }
@@ -1227,6 +1256,7 @@
                                 new String[] { Long.toString(id) });
                     }
                 }
+                c.close();
                 break;
             }
             case THUMBNAILS_ID: {
@@ -1259,8 +1289,12 @@
                     " AND account_type = ? AND account_name = ?",
                     new String[] { ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR,
                     accountType, accountName }, null, null, null);
-            if (c.moveToFirst()) {
-                return c.getLong(0);
+            try {
+                if (c.moveToFirst()) {
+                    return c.getLong(0);
+                }
+            } finally {
+                c.close();
             }
         }
         return FIXED_ID_ROOT;
@@ -1737,8 +1771,8 @@
             if (c.moveToFirst()) {
                 parentAccountName = c.getString(0);
                 parentAccountType = c.getString(1);
-                c.close();
             }
+            c.close();
         } else if (values.containsKey(Bookmarks.ACCOUNT_NAME)
                 || values.containsKey(Bookmarks.ACCOUNT_TYPE)) {
             // TODO: Implement if needed (no one needs this yet)
@@ -2053,4 +2087,21 @@
             return mSource.moveToPosition(newPosition);
         }
     }
+
+    // ---------------------------------------------------
+    //  SQL below, be warned
+    // ---------------------------------------------------
+
+    private static final String SQL_CREATE_VIEW_OMNIBOX_SUGGESTIONS =
+            "CREATE VIEW IF NOT EXISTS v_omnibox_suggestions "
+            + " AS "
+            + "  SELECT _id, url, title, 1 AS bookmark, 0 AS visits, 0 AS date"
+            + "  FROM bookmarks "
+            + "  WHERE deleted = 0 AND folder = 0 "
+            + "  UNION ALL "
+            + "  SELECT _id, url, title, 0 AS bookmark, visits, date "
+            + "  FROM history "
+            + "  WHERE url NOT IN (SELECT url FROM bookmarks"
+            + "    WHERE deleted = 0 AND folder = 0) "
+            + "  ORDER BY bookmark DESC, visits DESC, date DESC ";
 }
diff --git a/src/com/android/browser/provider/SnapshotProvider.java b/src/com/android/browser/provider/SnapshotProvider.java
index 49557f7..c0aad23 100644
--- a/src/com/android/browser/provider/SnapshotProvider.java
+++ b/src/com/android/browser/provider/SnapshotProvider.java
@@ -106,7 +106,6 @@
 
     @Override
     public boolean onCreate() {
-        mOpenHelper = new SnapshotDatabaseHelper(getContext());
         IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
         filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
         getContext().registerReceiver(mExternalStorageReceiver, filter);
@@ -117,13 +116,15 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            try {
-                mOpenHelper.close();
-            } catch (Throwable t) {
-                // We failed to close the open helper, which most likely means
-                // another thread is busy attempting to open the database
-                // or use the database. Let that thread try to gracefully
-                // deal with the error
+            if (mOpenHelper != null) {
+                try {
+                    mOpenHelper.close();
+                } catch (Throwable t) {
+                    // We failed to close the open helper, which most likely means
+                    // another thread is busy attempting to open the database
+                    // or use the database. Let that thread try to gracefully
+                    // deal with the error
+                }
             }
         }
     };
@@ -132,6 +133,9 @@
         String state = Environment.getExternalStorageState();
         if (Environment.MEDIA_MOUNTED.equals(state)) {
             try {
+                if (mOpenHelper == null) {
+                    mOpenHelper = new SnapshotDatabaseHelper(getContext());
+                }
                 return mOpenHelper.getWritableDatabase();
             } catch (Throwable t) {
                 return null;
@@ -145,6 +149,9 @@
         if (Environment.MEDIA_MOUNTED.equals(state)
                 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
             try {
+                if (mOpenHelper == null) {
+                    mOpenHelper = new SnapshotDatabaseHelper(getContext());
+                }
                 return mOpenHelper.getReadableDatabase();
             } catch (Throwable t) {
                 return null;
diff --git a/src/com/android/browser/widget/BookmarkThumbnailWidgetService.java b/src/com/android/browser/widget/BookmarkThumbnailWidgetService.java
index 7d174ee..bd3e289 100644
--- a/src/com/android/browser/widget/BookmarkThumbnailWidgetService.java
+++ b/src/com/android/browser/widget/BookmarkThumbnailWidgetService.java
@@ -114,7 +114,7 @@
         pref.edit()
             .putLong(STATE_CURRENT_FOLDER, rootFolder)
             .putLong(STATE_ROOT_FOLDER, rootFolder)
-            .commit();
+            .apply();
     }
 
     /**
diff --git a/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java b/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java
new file mode 100644
index 0000000..91f7238
--- /dev/null
+++ b/tests/src/com/android/browser/tests/BP1to2UpgradeTests.java
@@ -0,0 +1,148 @@
+/*
+ * 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.tests;
+
+import com.android.browser.provider.BrowserProvider;
+import com.android.browser.tests.utils.BP2TestCaseHelper;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.net.Uri;
+import android.provider.Browser;
+import android.provider.Browser.BookmarkColumns;
+import android.provider.BrowserContract;
+import android.provider.BrowserContract.Bookmarks;
+import android.provider.BrowserContract.History;
+import android.provider.BrowserContract.Images;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+
+@SmallTest
+public class BP1to2UpgradeTests extends BP2TestCaseHelper {
+
+    BrowserProvider mBp1;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mBp1 = new BrowserProvider();
+        mBp1.attachInfo(getMockContext(), null);
+    }
+
+    /**
+     * Test that simply makes sure BP1->BP2 with no changes works as intended
+     */
+    public void testStockUpgrade() {
+        Cursor c = mBp1.query(Browser.BOOKMARKS_URI,
+                new String[] { BookmarkColumns.URL }, null, null,
+                BookmarkColumns.URL + " DESC");
+        ArrayList<String> urls = new ArrayList<String>(c.getCount());
+        while (c.moveToNext()) {
+            urls.add(c.getString(0));
+        }
+        c.close();
+        // First, test the public API (which will hit BP2)
+        c = getMockContentResolver().query(Browser.BOOKMARKS_URI,
+                new String[] { BookmarkColumns.URL }, null, null,
+                BookmarkColumns.URL + " DESC");
+        assertEquals(urls.size(), c.getCount());
+        int i = 0;
+        while (c.moveToNext()) {
+            assertEquals(urls.get(i++), c.getString(0));
+        }
+        c.close();
+        // Next, test BP2's new API (not a public API)
+        c = getMockContentResolver().query(Bookmarks.CONTENT_URI,
+                new String[] { Bookmarks.URL }, null, null,
+                Bookmarks.URL + " DESC");
+        assertEquals(urls.size(), c.getCount());
+        i = 0;
+        while (c.moveToNext()) {
+            assertEquals(urls.get(i++), c.getString(0));
+        }
+        c.close();
+    }
+
+    public void testPreserveHistory() {
+        ContentValues values = new ContentValues();
+        values.put(BookmarkColumns.URL, "http://slashdot.org/");
+        values.put(BookmarkColumns.BOOKMARK, 0);
+        values.put(BookmarkColumns.DATE, 123456);
+        mBp1.insert(Browser.BOOKMARKS_URI, values);
+        // First, test internal API
+        Cursor c = getMockContentResolver().query(History.CONTENT_URI,
+                new String[] { History.URL, History.DATE_LAST_VISITED },
+                null, null, null);
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("http://slashdot.org/", c.getString(0));
+        assertEquals(123456, c.getInt(1));
+        c.close();
+        // Next, test public API
+        c = getMockContentResolver().query(Browser.BOOKMARKS_URI,
+                Browser.HISTORY_PROJECTION, BookmarkColumns.BOOKMARK + " = 0",
+                null, null);
+        assertEquals("public API", 1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("http://slashdot.org/",
+                c.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
+        assertEquals(123456, c.getInt(Browser.HISTORY_PROJECTION_DATE_INDEX));
+        c.close();
+    }
+
+    public void testPreserveBookmarks() {
+        // First, nuke 'er (deletes stock bookmarks)
+        mBp1.delete(Browser.BOOKMARKS_URI, null, null);
+        ContentValues values = new ContentValues();
+        values.put(BookmarkColumns.URL, "http://slashdot.org/");
+        values.put(BookmarkColumns.BOOKMARK, 1);
+        values.put(BookmarkColumns.CREATED, 123456);
+        mBp1.insert(Browser.BOOKMARKS_URI, values);
+        // First, test internal API
+        Cursor c = getMockContentResolver().query(Bookmarks.CONTENT_URI,
+                new String[] { Bookmarks.URL, Bookmarks.DATE_CREATED },
+                null, null, null);
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("http://slashdot.org/", c.getString(0));
+        assertEquals(123456, c.getInt(1));
+        c.close();
+        // Next, test public API
+        c = getMockContentResolver().query(Browser.BOOKMARKS_URI,
+                new String[] { BookmarkColumns.URL, BookmarkColumns.CREATED },
+                BookmarkColumns.BOOKMARK + " = 1", null, null);
+        assertEquals("public API", 1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("http://slashdot.org/", c.getString(0));
+        assertEquals(123456, c.getInt(1));
+        c.close();
+    }
+
+    public void testEmptyUpgrade() {
+        mBp1.delete(Browser.BOOKMARKS_URI, null, null);
+        Cursor c = getMockContentResolver().query(Bookmarks.CONTENT_URI,
+                null, null, null, null);
+        assertEquals(0, c.getCount());
+        c.close();
+    }
+
+}
diff --git a/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java b/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java
index c374292..536163a 100644
--- a/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java
+++ b/tests/src/com/android/browser/tests/utils/ProviderTestCase3.java
@@ -69,6 +69,11 @@
         public Context getApplicationContext() {
             return this;
         }
+
+        @Override
+        public Object getSystemService(String name) {
+            return null;
+        }
     }
     /**
      * Constructor.
@@ -114,7 +119,15 @@
                                     //delegated to
                 getContext(), // The context that file methods are delegated to
                 filenamePrefix);
-        mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
+        // The default IsolatedContext has a mock AccountManager that doesn't
+        // work for us, so override getSystemService to always return null
+        mProviderContext = new IsolatedContext(mResolver, targetContextWrapper) {
+
+            @Override
+            public Object getSystemService(String name) {
+                return null;
+            }
+        };
 
         mProvider = mProviderClass.newInstance();
         mProvider.attachInfo(mProviderContext, null);