Wed, 31 Dec 2014 06:09:35 +0100
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 }