Java tutorial
/* Copyright 2012 Google Inc. * * 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.num.mobiperf; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.params.ClientPNames; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.DefaultHttpClient; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import com.num.mobiperf.*; /** * Helper class for google account checkins */ public class AccountSelector { private static final String ACCOUNT_TYPE = "com.google"; private static final String ACCOUNT_NAME = "@google.com"; // The authentication period in milliseconds private static final long AUTHENTICATE_PERIOD_MSEC = 24 * 3600 * 1000; private Context context; private String authToken = null; private ExecutorService checkinExecutor = null; private Future<Cookie> checkinFuture = null; private long lastAuthTime = 0; private boolean authImmediately = false; //private PhoneUtils phoneUtils; private boolean isAnonymous = true; public boolean isAnonymous() { return isAnonymous; } public AccountSelector(Context context) { this.context = context; this.checkinExecutor = Executors.newFixedThreadPool(1); //this.phoneUtils = PhoneUtils.getPhoneUtils(); } /** Returns the Future to monitor the checkin progress */ public synchronized Future<Cookie> getCheckinFuture() { return this.checkinFuture; } /** After checkin finishes, the client of AccountSelector SHOULD reset checkinFuture */ public synchronized void resetCheckinFuture() { this.checkinFuture = null; } /** Shuts down the executor thread */ public void shutDown() { // shutdown() removes all previously submitted task and no new tasks are accepted this.checkinExecutor.shutdown(); // shutdownNow stops all currently executing tasks this.checkinExecutor.shutdownNow(); } /** Allows clients of AccountSelector to request an authentication upon the next call * to authenticate() */ public synchronized void setAuthImmediately(boolean val) { this.authImmediately = val; } private synchronized boolean shouldAuthImmediately() { return this.authImmediately; } private synchronized void setLastAuthTime(long lastTime) { this.lastAuthTime = lastTime; } private synchronized long getLastAuthTime() { return this.lastAuthTime; } /** * Return the list of account names for users to select */ public static String[] getAccountList(Context context) { AccountManager accountManager = AccountManager.get(context.getApplicationContext()); Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); int numAccounts = accounts == null ? 1 : accounts.length + 1; String[] accountNames = new String[numAccounts]; for (int i = 0; i < accounts.length; i++) { accountNames[i] = accounts[i].name; } accountNames[numAccounts - 1] = "Anonymous"; return accountNames; } /** Starts an authentication request */ public void authenticate() throws OperationCanceledException, AuthenticatorException, IOException { //Logger.i("AccountSelector.authenticate() running"); /* We only need to authenticate every AUTHENTICATE_PERIOD_MILLI milliseconds, during * which we can reuse the cookie. If authentication fails due to expired * authToken, the client of AccountSelector can call authImmedately() to request * authenticate() upon the next checkin */ long authTimeLast = this.getLastAuthTime(); long timeSinceLastAuth = System.currentTimeMillis() - authTimeLast; if (!this.shouldAuthImmediately() && authTimeLast != 0 && (timeSinceLastAuth < AUTHENTICATE_PERIOD_MSEC)) { return; } //Logger.i("Authenticating. Last authentication is " + // timeSinceLastAuth / 1000 / 60 + " minutes ago. "); AccountManager accountManager = AccountManager.get(context.getApplicationContext()); if (this.authToken != null) { // There will be no effect on the token if it is still valid //Logger.i("Invalidating token"); accountManager.invalidateAuthToken(ACCOUNT_TYPE, this.authToken); } Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); //Logger.i("Got " + accounts.length + " accounts"); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.context); String selectedAccount = prefs.getString("PREF_KEY_SELECTED_ACCOUNT", null); final String defaultUserName = "Anonymous"; isAnonymous = true; if (selectedAccount != null && selectedAccount.equals(defaultUserName)) { return; } if (accounts != null && accounts.length > 0) { // Default account should be the Anonymous account Account accountToUse = accounts[accounts.length - 1]; if (!accounts[accounts.length - 1].name.equals(defaultUserName)) { for (Account account : accounts) { if (account.name.equals(defaultUserName)) { accountToUse = account; break; } } } if (selectedAccount != null) { for (Account account : accounts) { if (account.name.equals(selectedAccount)) { accountToUse = account; break; } } } isAnonymous = accountToUse.name.equals(defaultUserName); if (isAnonymous) { //Logger.d("Skipping authentication as account is " + defaultUserName); return; } // WHERE YOU GET TOKEN!!!!!! //Logger.i("Trying to get auth token for " + accountToUse); AccountManagerFuture<Bundle> future = accountManager.getAuthToken(accountToUse, "ah", false, new AccountManagerCallback<Bundle>() { public void run(AccountManagerFuture<Bundle> result) { //Logger.i("AccountManagerCallback invoked"); try { getAuthToken(result); } catch (RuntimeException e) { System.out.println("Failed to get authToken" + e.getLocalizedMessage()); //Logger.e("Failed to get authToken", e); /* TODO(Wenjie): May ask the user whether to quit the app nicely here if a number * of trials have been made and failed. Since Speedometer is basically useless * without checkin */ } } }, null); //Logger.i("AccountManager.getAuthToken returned " + future); } else { throw new RuntimeException("No google account found"); } } private void getAuthToken(AccountManagerFuture<Bundle> result) { //Logger.i("getAuthToken() called, result " + result); String errMsg = "Failed to get login cookie. "; Bundle bundle; try { bundle = result.getResult(); Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT); if (intent != null) { // User input required. (A UI will pop up for user's consent to allow // this app access account information.) //Logger.i("Starting account manager activity"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } else { //Logger.i("Executing getCookie task"); synchronized (this) { this.authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); this.checkinFuture = checkinExecutor.submit(new GetCookieTask()); } } } catch (OperationCanceledException e) { //Logger.e(errMsg, e); throw new RuntimeException("Can't get login cookie", e); } catch (AuthenticatorException e) { //Logger.e(errMsg, e); throw new RuntimeException("Can't get login cookie", e); } catch (IOException e) { //Logger.e(errMsg, e); throw new RuntimeException("Can't get login cookie", e); } } private class GetCookieTask implements Callable<Cookie> { public Cookie call() { //Logger.i("GetCookieTask running: " + authToken); DefaultHttpClient httpClient = new DefaultHttpClient(); boolean success = false; try { String loginUrlPrefix = "https://openmobiledata.appspot.com" + "/_ah/login?continue=" + "https://openmobiledata.appspot.com" + "&action=Login&auth="; // Don't follow redirects httpClient.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false); HttpGet httpGet = new HttpGet(loginUrlPrefix + authToken); HttpResponse response; //Logger.i("Accessing: " + loginUrlPrefix + authToken); response = httpClient.execute(httpGet); if (response.getStatusLine().getStatusCode() != 302) { // Response should be a redirect to the "continue" URL. //Logger.e("Failed to get login cookie: " + // loginUrlPrefix + " returned unexpected error code " + // response.getStatusLine().getStatusCode()); throw new RuntimeException("Failed to get login cookie: " + loginUrlPrefix + " returned unexpected error code " + response.getStatusLine().getStatusCode()); } //Logger.i("Got " + // httpClient.getCookieStore().getCookies().size() + " cookies back"); for (Cookie cookie : httpClient.getCookieStore().getCookies()) { //Logger.i("Checking cookie " + cookie); if (cookie.getName().equals("SACSID") || cookie.getName().equals("ACSID")) { //Logger.i("Got cookie " + cookie); setLastAuthTime(System.currentTimeMillis()); success = true; return cookie; } } //Logger.e("No (S)ASCID cookies returned"); throw new RuntimeException( "Failed to get login cookie: " + loginUrlPrefix + " did not return any (S)ACSID cookie"); } catch (ClientProtocolException e) { //Logger.e("Failed to get login cookie", e); throw new RuntimeException("Failed to get login cookie", e); } catch (IOException e) { //Logger.e("Failed to get login cookie", e); throw new RuntimeException("Failed to get login cookie", e); } finally { httpClient.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true); if (!success) { resetCheckinFuture(); } } } } }