mobile/android/base/sync/setup/SyncAuthenticatorService.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.sync.setup;
     7 import java.io.UnsupportedEncodingException;
     8 import java.security.NoSuchAlgorithmException;
    10 import org.mozilla.gecko.background.common.log.Logger;
    11 import org.mozilla.gecko.sync.SyncConstants;
    12 import org.mozilla.gecko.sync.Utils;
    13 import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
    15 import android.accounts.AbstractAccountAuthenticator;
    16 import android.accounts.Account;
    17 import android.accounts.AccountAuthenticatorResponse;
    18 import android.accounts.AccountManager;
    19 import android.accounts.NetworkErrorException;
    20 import android.app.Service;
    21 import android.content.Context;
    22 import android.content.Intent;
    23 import android.os.Bundle;
    24 import android.os.IBinder;
    26 public class SyncAuthenticatorService extends Service {
    27   private static final String LOG_TAG = "SyncAuthService";
    29   private SyncAccountAuthenticator sAccountAuthenticator = null;
    31   @Override
    32   public void onCreate() {
    33     Logger.debug(LOG_TAG, "onCreate");
    34     sAccountAuthenticator = getAuthenticator();
    35   }
    37   @Override
    38   public IBinder onBind(Intent intent) {
    39     if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
    40       return getAuthenticator().getIBinder();
    41     }
    42     return null;
    43   }
    45   private SyncAccountAuthenticator getAuthenticator() {
    46     if (sAccountAuthenticator == null) {
    47       sAccountAuthenticator = new SyncAccountAuthenticator(this);
    48     }
    49     return sAccountAuthenticator;
    50   }
    52   /**
    53    * Generate a "plain" auth token.
    54    * <p>
    55    * Android caches only the value of the key
    56    * <code>AccountManager.KEY_AUTHTOKEN</code>, so if a caller needs the other
    57    * keys in this bundle, it needs to invalidate the token (so that the bundle
    58    * is re-generated).
    59    *
    60    * @param context
    61    *          Android context.
    62    * @param account
    63    *          Android account.
    64    * @return a <code>Bundle</code> instance containing a subset of the following
    65    *         keys: (caller's must check for missing keys)
    66    *         <ul>
    67    *         <li><code>AccountManager.KEY_ACCOUNT_TYPE</code>: the Android
    68    *         Account's type</li>
    69    *
    70    *         <li><code>AccountManager.KEY_ACCOUNT_NAME</code>: the Android
    71    *         Account's name</li>
    72    *
    73    *         <li><code>AccountManager.KEY_AUTHTOKEN</code>: the Sync account's
    74    *         password </li>
    75    *
    76    *         <li><code> Constants.OPTION_USERNAME</code>: the Sync account's
    77    *         hashed username</li>
    78    *
    79    *         <li><code>Constants.OPTION_SERVER</code>: the Sync account's
    80    *         server</li>
    81    *
    82    *         <li><code> Constants.OPTION_SYNCKEY</code>: the Sync account's
    83    *         sync key</li>
    84    *
    85    *         </ul>
    86    * @throws NetworkErrorException
    87    */
    88   public static Bundle getPlainAuthToken(final Context context, final Account account)
    89       throws NetworkErrorException {
    90     // Extract the username and password from the Account Manager, and ask
    91     // the server for an appropriate AuthToken.
    92     final AccountManager am = AccountManager.get(context);
    93     final String password = am.getPassword(account);
    94     if (password == null) {
    95       Logger.warn(LOG_TAG, "Returning null bundle for getPlainAuthToken since Account password is null.");
    96       return null;
    97     }
    99     final Bundle result = new Bundle();
   101     // This is a Sync account.
   102     result.putString(AccountManager.KEY_ACCOUNT_TYPE, SyncConstants.ACCOUNTTYPE_SYNC);
   104     // Server.
   105     String serverURL = am.getUserData(account, Constants.OPTION_SERVER);
   106     result.putString(Constants.OPTION_SERVER, serverURL);
   108     // Full username, before hashing.
   109     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
   111     // Username after hashing.
   112     try {
   113       String username = Utils.usernameFromAccount(account.name);
   114       Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username + ".");
   115       Logger.debug(LOG_TAG, "Setting username. Null? " + (username == null));
   116       result.putString(Constants.OPTION_USERNAME, username);
   117     } catch (NoSuchAlgorithmException e) {
   118       // Do nothing. Calling code must check for missing value.
   119       Logger.debug(LOG_TAG, "Exception in account lookup: " + e);
   120     } catch (UnsupportedEncodingException e) {
   121       // Do nothing. Calling code must check for missing value.
   122       Logger.debug(LOG_TAG, "Exception in account lookup: " + e);
   123     }
   125     // Sync key.
   126     final String syncKey = am.getUserData(account, Constants.OPTION_SYNCKEY);
   127     Logger.debug(LOG_TAG, "Setting sync key. Null? " + (syncKey == null));
   128     result.putString(Constants.OPTION_SYNCKEY, syncKey);
   130     // Password.
   131     result.putString(AccountManager.KEY_AUTHTOKEN, password);
   132     return result;
   133   }
   135   private static class SyncAccountAuthenticator extends AbstractAccountAuthenticator {
   136     private Context mContext;
   137     public SyncAccountAuthenticator(Context context) {
   138       super(context);
   139       mContext = context;
   140     }
   142     @Override
   143     public Bundle addAccount(AccountAuthenticatorResponse response,
   144         String accountType, String authTokenType, String[] requiredFeatures,
   145         Bundle options) throws NetworkErrorException {
   146       Logger.debug(LOG_TAG, "addAccount()");
   147       final Intent intent = new Intent(mContext, SetupSyncActivity.class);
   148       intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
   149                       response);
   150       intent.putExtra("accountType", SyncConstants.ACCOUNTTYPE_SYNC);
   151       intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, true);
   153       final Bundle result = new Bundle();
   154       result.putParcelable(AccountManager.KEY_INTENT, intent);
   156       return result;
   157     }
   159     @Override
   160     public Bundle confirmCredentials(AccountAuthenticatorResponse response,
   161                                      Account account,
   162                                      Bundle options) throws NetworkErrorException {
   163       Logger.debug(LOG_TAG, "confirmCredentials()");
   164       return null;
   165     }
   167     @Override
   168     public Bundle editProperties(AccountAuthenticatorResponse response,
   169                                  String accountType) {
   170       Logger.debug(LOG_TAG, "editProperties");
   171       return null;
   172     }
   174     @Override
   175     public Bundle getAuthToken(AccountAuthenticatorResponse response,
   176         Account account, String authTokenType, Bundle options)
   177         throws NetworkErrorException {
   178       Logger.debug(LOG_TAG, "getAuthToken()");
   180       if (Constants.AUTHTOKEN_TYPE_PLAIN.equals(authTokenType)) {
   181         return getPlainAuthToken(mContext, account);
   182       }
   184       final Bundle result = new Bundle();
   185       result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
   186       return result;
   187     }
   189     @Override
   190     public String getAuthTokenLabel(String authTokenType) {
   191       Logger.debug(LOG_TAG, "getAuthTokenLabel()");
   192       return null;
   193     }
   195     @Override
   196     public Bundle hasFeatures(AccountAuthenticatorResponse response,
   197         Account account, String[] features) throws NetworkErrorException {
   198       Logger.debug(LOG_TAG, "hasFeatures()");
   199       return null;
   200     }
   202     @Override
   203     public Bundle updateCredentials(AccountAuthenticatorResponse response,
   204         Account account, String authTokenType, Bundle options)
   205         throws NetworkErrorException {
   206       Logger.debug(LOG_TAG, "updateCredentials()");
   207       return null;
   208     }
   210     /**
   211      * Bug 769745: persist pickled Sync account settings so that we can unpickle
   212      * after Fennec is moved to the SD card.
   213      * <p>
   214      * This is <b>not</b> called when an Android Account is blown away due to
   215      * the SD card being unmounted.
   216      * <p>
   217      * Broadcasting a Firefox intent to version sharing this Android Account is
   218      * a terrible hack, but it's better than the catching the generic
   219      * "accounts changed" broadcast intent and trying to figure out whether our
   220      * Account disappeared.
   221      */
   222     @Override
   223     public Bundle getAccountRemovalAllowed(final AccountAuthenticatorResponse response, Account account)
   224         throws NetworkErrorException {
   225       Bundle result = super.getAccountRemovalAllowed(response, account);
   227       if (result == null ||
   228           !result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) ||
   229           result.containsKey(AccountManager.KEY_INTENT)) {
   230         return result;
   231       }
   233       final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
   234       if (!removalAllowed) {
   235         return result;
   236       }
   238       // Bug 790931: Broadcast a message to all Firefox versions sharing this
   239       // Android Account type telling that this Sync Account has been deleted.
   240       //
   241       // We would really prefer to receive Android's
   242       // LOGIN_ACCOUNTS_CHANGED_ACTION broadcast, but that
   243       // doesn't include enough information about which Accounts changed to
   244       // correctly identify whether a Sync account has been removed (when some
   245       // Firefox versions are installed on the SD card).
   246       //
   247       // Broadcast intents protected with permissions are secure, so it's okay
   248       // to include password and sync key, etc.
   249       final Intent intent = SyncAccounts.makeSyncAccountDeletedIntent(mContext, AccountManager.get(mContext), account);
   250       Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
   251           "broadcasting secure intent " + intent.getAction() + ".");
   252       mContext.sendBroadcast(intent, SyncConstants.PER_ACCOUNT_TYPE_PERMISSION);
   254       return result;
   255     }
   256   }
   257 }

mercurial