Clear session cookies before attempting pre-login.

If ClientLogin issues session cookies, we do not want to clear them immediately
after getting them from login.  If we are not going to restore tabs, go ahead
and clear the cookies before attempting pre-login.  Keep track of the tab to
restore so that we don't need to figure it out again.  Requires a change in
frameworks/base that exposes the CookieManager api.

If we receive a 403 from IssueTokenAuth, inval the auth tokens and try again.

Bug: 3421214
Change-Id: I5dd4cc0eba365a20a731ac43dd2571ef6274eaa9
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index ec09673..899a7c2 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -111,12 +111,7 @@
             icicle = state;
         }
 
-        final Bundle b = icicle;
-        GoogleAccountLogin.startLoginIfNeeded(this, settings, new Runnable() {
-            @Override public void run() {
-                mController.start(b, getIntent());
-            }
-        });
+        mController.start(icicle, getIntent());
     }
 
     @VisibleForTesting
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 07e1ef9..74a66b1 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -251,7 +251,7 @@
         retainIconsOnStartup();
     }
 
-    void start(Bundle icicle, Intent intent) {
+    void start(final Bundle icicle, final Intent intent) {
         // Unless the last browser usage was within 24 hours, destroy any
         // remaining incognito tabs.
 
@@ -261,17 +261,32 @@
         Calendar yesterday = Calendar.getInstance();
         yesterday.add(Calendar.DATE, -1);
 
-        boolean restoreIncognitoTabs = !(lastActiveDate == null
+        final boolean restoreIncognitoTabs = !(lastActiveDate == null
             || lastActiveDate.before(yesterday)
             || lastActiveDate.after(today));
 
-        if (!mTabControl.restoreState(icicle, restoreIncognitoTabs,
-                mUi.needsRestoreAllTabs())) {
-            // there is no quit on Android. But if we can't restore the state,
-            // we can treat it as a new Browser, remove the old session cookies.
-            // This is done async in the CookieManager.
-            CookieManager.getInstance().removeSessionCookie();
+        // Find out if we will restore any state and remember the tab.
+        final int currentTab =
+                mTabControl.canRestoreState(icicle, restoreIncognitoTabs);
 
+        if (currentTab == -1) {
+            // Not able to restore so we go ahead and clear session cookies.  We
+            // must do this before trying to login the user as we don't want to
+            // clear any session cookies set during login.
+            CookieManager.getInstance().removeSessionCookie();
+        }
+
+        GoogleAccountLogin.startLoginIfNeeded(mActivity, mSettings,
+                new Runnable() {
+                    @Override public void run() {
+                        start(icicle, intent, currentTab, restoreIncognitoTabs);
+                    }
+                });
+    }
+
+    private void start(Bundle icicle, Intent intent, int currentTab,
+            boolean restoreIncognitoTabs) {
+        if (currentTab == -1) {
             final Bundle extra = intent.getExtras();
             // Create an initial tab.
             // If the intent is ACTION_VIEW and data is not null, the Browser is
@@ -303,6 +318,8 @@
                 loadUrlDataIn(t, urlData);
             }
         } else {
+            mTabControl.restoreState(icicle, currentTab, restoreIncognitoTabs,
+                    mUi.needsRestoreAllTabs());
             mUi.updateTabs(mTabControl.getTabs());
             // TabControl.restoreState() will create a new tab even if
             // restoring the state fails.
diff --git a/src/com/android/browser/GoogleAccountLogin.java b/src/com/android/browser/GoogleAccountLogin.java
index f019b52..eaf45ea 100644
--- a/src/com/android/browser/GoogleAccountLogin.java
+++ b/src/com/android/browser/GoogleAccountLogin.java
@@ -46,7 +46,7 @@
 
 import java.util.StringTokenizer;
 
-public class GoogleAccountLogin extends Thread implements
+public class GoogleAccountLogin implements Runnable,
         AccountManagerCallback<Bundle>, OnCancelListener {
 
     private static final String LOGTAG = "BrowserLogin";
@@ -77,6 +77,7 @@
     private String mSid;
     private String mLsid;
     private int mState;  // {NONE(0), SID(1), LSID(2)}
+    private boolean mTokensInvalidated;
 
     private GoogleAccountLogin(Activity activity, String name,
             Runnable runnable) {
@@ -105,7 +106,7 @@
         ed.apply();
     }
 
-    // Thread
+    // Runnable
     @Override
     public void run() {
         String url = ISSUE_AUTH_TOKEN_URL.buildUpon()
@@ -128,10 +129,22 @@
         String result = null;
         try {
             HttpResponse response = client.execute(request);
-            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+            int status = response.getStatusLine().getStatusCode();
+            if (status != HttpStatus.SC_OK) {
                 Log.d(LOGTAG, "LOGIN_FAIL: Bad status from auth url "
-                      + response.getStatusLine().getStatusCode() + ": "
+                      + status + ": "
                       + response.getStatusLine().getReasonPhrase());
+                // Invalidate the tokens once just in case the 403 was for other
+                // reasons.
+                if (status == HttpStatus.SC_FORBIDDEN && !mTokensInvalidated) {
+                    Log.d(LOGTAG, "LOGIN_FAIL: Invalidating tokens...");
+                    // Need to regenerate the auth tokens and try again.
+                    invalidateTokens();
+                    // XXX: Do not touch any more member variables from this
+                    // thread as a second thread will handle the next login
+                    // attempt.
+                    return;
+                }
                 done();
                 return;
             }
@@ -171,6 +184,15 @@
         });
     }
 
+    private void invalidateTokens() {
+        AccountManager am = AccountManager.get(mActivity);
+        am.invalidateAuthToken(GOOGLE, mSid);
+        am.invalidateAuthToken(GOOGLE, mLsid);
+        mTokensInvalidated = true;
+        mState = 1;  // SID
+        am.getAuthToken(mAccount, "SID", null, mActivity, this, null);
+    }
+
     // AccountManager callbacks.
     @Override
     public void run(AccountManagerFuture<Bundle> value) {
@@ -190,7 +212,7 @@
                     break;
                 case 2:
                     mLsid = id;
-                    this.start();
+                    new Thread(this).start();
                     break;
             }
         } catch (Exception e) {
@@ -280,6 +302,11 @@
             return false;
         }
 
+        // This will potentially block the UI thread but we have to have the
+        // most updated cookies.
+        // FIXME: Figure out how to avoid waiting to clear session cookies.
+        CookieManager.getInstance().waitForCookieOperationsToComplete();
+
         // Use /a/ to grab hosted cookies as well as the base set of google.com
         // cookies.
         String cookies = CookieManager.getInstance().getCookie(
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index 050ad94..af9928a 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -287,7 +287,42 @@
     }
 
     /**
+     * Check if the state can be restored.  If the state can be restored, the
+     * current tab index is returned.  This can be passed to restoreState below
+     * in order to restore the correct tab.  Otherwise, -1 is returned and the
+     * state cannot be restored.
+     */
+    int canRestoreState(Bundle inState, boolean restoreIncognitoTabs) {
+        final int numTabs = (inState == null)
+                ? - 1 : inState.getInt(Tab.NUMTABS, -1);
+        if (numTabs == -1) {
+            return -1;
+        }
+        final int oldCurrentTab = inState.getInt(Tab.CURRTAB, -1);
+
+        // Determine whether the saved current tab can be restored, and if not,
+        // which tab will take its place.
+        int currentTab = -1;
+        if (restoreIncognitoTabs ||
+                !inState.getBundle(Tab.WEBVIEW + oldCurrentTab)
+                .getBoolean(Tab.INCOGNITO)) {
+            currentTab = oldCurrentTab;
+        } else {
+            for (int i = 0; i < numTabs; i++) {
+                if (!inState.getBundle(Tab.WEBVIEW + i)
+                        .getBoolean(Tab.INCOGNITO)) {
+                    currentTab = i;
+                    break;
+                }
+            }
+        }
+
+        return currentTab;
+    }
+
+    /**
      * Restore the state of all the tabs.
+     * @param currentTab The tab index to restore.
      * @param inState The saved state of all the tabs.
      * @param restoreIncognitoTabs Restoring private browsing tabs
      * @param restoreAll All webviews get restored, not just the current tab
@@ -295,89 +330,69 @@
      * @return True if there were previous tabs that were restored. False if
      *         there was no saved state or restoring the state failed.
      */
-    boolean restoreState(Bundle inState, boolean restoreIncognitoTabs,
-            boolean restoreAll) {
-        final int numTabs = (inState == null)
-                ? -1 : inState.getInt(Tab.NUMTABS, -1);
-        if (numTabs == -1) {
-            return false;
-        } else {
-            final int oldCurrentTab = inState.getInt(Tab.CURRTAB, -1);
+    void restoreState(Bundle inState, int currentTab,
+            boolean restoreIncognitoTabs, boolean restoreAll) {
+        if (currentTab == -1) {
+            return;
+        }
 
-            // Determine whether the saved current tab can be restored, and
-            // if not, which tab will take its place.
-            int currentTab = -1;
-            if (restoreIncognitoTabs
-                    || !inState.getBundle(Tab.WEBVIEW + oldCurrentTab).getBoolean(Tab.INCOGNITO)) {
-                currentTab = oldCurrentTab;
+        // If currentTab is valid, numTabs must be present.
+        final int numTabs = inState.getInt(Tab.NUMTABS, -1);
+
+        // Map saved tab indices to new indices, in case any incognito tabs
+        // need to not be restored.
+        HashMap<Integer, Integer> originalTabIndices = new HashMap<Integer, Integer>();
+        originalTabIndices.put(-1, -1);
+        for (int i = 0; i < numTabs; i++) {
+            Bundle state = inState.getBundle(Tab.WEBVIEW + i);
+
+            if (!restoreIncognitoTabs && state != null && state.getBoolean(Tab.INCOGNITO)) {
+                originalTabIndices.put(i, -1);
+            } else if (i == currentTab || restoreAll) {
+                Tab t = createNewTab();
+                // Me must set the current tab before restoring the state
+                // so that all the client classes are set.
+                if (i == currentTab) {
+                    setCurrentTab(t);
+                }
+                if (!t.restoreState(state)) {
+                    Log.w(LOGTAG, "Fail in restoreState, load home page.");
+                    t.getWebView().loadUrl(BrowserSettings.getInstance()
+                            .getHomePage());
+                }
+                originalTabIndices.put(i, getTabCount() - 1);
             } else {
-                for (int i = 0; i < numTabs; i++) {
-                    if (!inState.getBundle(Tab.WEBVIEW + i).getBoolean(Tab.INCOGNITO)) {
-                        currentTab = i;
-                        break;
-                    }
+                // Create a new tab and don't restore the state yet, add it
+                // to the tab list
+                Tab t = new Tab(mController, null, false, null, null);
+                if (state != null) {
+                    t.setSavedState(state);
+                    // Need to maintain the app id and original url so we
+                    // can possibly reuse this tab.
+                    t.setAppId(state.getString(Tab.APPID));
                 }
+                mTabs.add(t);
+                // added the tab to the front as they are not current
+                mTabQueue.add(0, t);
+                originalTabIndices.put(i, getTabCount() - 1);
             }
-            if (currentTab < 0) {
-                return false;
-            }
+        }
 
-            // Map saved tab indices to new indices, in case any incognito tabs
-            // need to not be restored.
-            HashMap<Integer, Integer> originalTabIndices = new HashMap<Integer, Integer>();
-            originalTabIndices.put(-1, -1);
-            for (int i = 0; i < numTabs; i++) {
-                Bundle state = inState.getBundle(Tab.WEBVIEW + i);
-
-                if (!restoreIncognitoTabs && state != null && state.getBoolean(Tab.INCOGNITO)) {
-                    originalTabIndices.put(i, -1);
-                } else if (i == currentTab || restoreAll) {
-                    Tab t = createNewTab();
-                    // Me must set the current tab before restoring the state
-                    // so that all the client classes are set.
-                    if (i == currentTab) {
-                        setCurrentTab(t);
-                    }
-                    if (!t.restoreState(state)) {
-                        Log.w(LOGTAG, "Fail in restoreState, load home page.");
-                        t.getWebView().loadUrl(BrowserSettings.getInstance()
-                                .getHomePage());
-                    }
-                    originalTabIndices.put(i, getTabCount() - 1);
-                } else {
-                    // Create a new tab and don't restore the state yet, add it
-                    // to the tab list
-                    Tab t = new Tab(mController, null, false, null, null);
-                    if (state != null) {
-                        t.setSavedState(state);
-                        // Need to maintain the app id and original url so we
-                        // can possibly reuse this tab.
-                        t.setAppId(state.getString(Tab.APPID));
-                    }
-                    mTabs.add(t);
-                    // added the tab to the front as they are not current
-                    mTabQueue.add(0, t);
-                    originalTabIndices.put(i, getTabCount() - 1);
-                }
-            }
-
-            // Rebuild the tree of tabs. Do this after all tabs have been
-            // created/restored so that the parent tab exists.
-            for (int i = 0; i < numTabs; i++) {
-                final Bundle b = inState.getBundle(Tab.WEBVIEW + i);
-                final Tab t = getTab(i);
-                if (b != null && t != null) {
-                    final Integer parentIndex = originalTabIndices.get(b.getInt(Tab.PARENTTAB, -1));
-                    if (parentIndex != -1) {
-                        final Tab parent = getTab(parentIndex);
-                        if (parent != null) {
-                            parent.addChildTab(t);
-                        }
+        // Rebuild the tree of tabs. Do this after all tabs have been
+        // created/restored so that the parent tab exists.
+        for (int i = 0; i < numTabs; i++) {
+            final Bundle b = inState.getBundle(Tab.WEBVIEW + i);
+            final Tab t = getTab(i);
+            if (b != null && t != null) {
+                final Integer parentIndex = originalTabIndices.get(b.getInt(Tab.PARENTTAB, -1));
+                if (parentIndex != -1) {
+                    final Tab parent = getTab(parentIndex);
+                    if (parent != null) {
+                        parent.addChildTab(t);
                     }
                 }
             }
         }
-        return true;
     }
 
     /**