|
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/. */ |
|
4 |
|
5 package org.mozilla.gecko.fxa.receivers; |
|
6 |
|
7 import java.util.LinkedList; |
|
8 import java.util.List; |
|
9 import java.util.concurrent.Executor; |
|
10 import java.util.concurrent.Executors; |
|
11 |
|
12 import org.mozilla.gecko.background.common.log.Logger; |
|
13 import org.mozilla.gecko.fxa.FirefoxAccounts; |
|
14 import org.mozilla.gecko.fxa.FxAccountConstants; |
|
15 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; |
|
16 import org.mozilla.gecko.fxa.login.State; |
|
17 import org.mozilla.gecko.fxa.login.State.StateLabel; |
|
18 import org.mozilla.gecko.sync.Utils; |
|
19 |
|
20 import android.accounts.Account; |
|
21 import android.content.BroadcastReceiver; |
|
22 import android.content.Context; |
|
23 import android.content.Intent; |
|
24 |
|
25 /** |
|
26 * A receiver that takes action when our Android package is upgraded (replaced). |
|
27 */ |
|
28 public class FxAccountUpgradeReceiver extends BroadcastReceiver { |
|
29 private static final String LOG_TAG = FxAccountUpgradeReceiver.class.getSimpleName(); |
|
30 |
|
31 /** |
|
32 * Produce a list of Runnable instances to be executed sequentially on |
|
33 * upgrade. |
|
34 * <p> |
|
35 * Each Runnable will be executed sequentially on a background thread. Any |
|
36 * unchecked Exception thrown will be caught and ignored. |
|
37 * |
|
38 * @param context Android context. |
|
39 * @return list of Runnable instances. |
|
40 */ |
|
41 protected List<Runnable> onUpgradeRunnables(Context context) { |
|
42 List<Runnable> runnables = new LinkedList<Runnable>(); |
|
43 runnables.add(new MaybeUnpickleRunnable(context)); |
|
44 // Recovering accounts that are in the Doghouse should happen *after* we |
|
45 // unpickle any accounts saved to disk. |
|
46 runnables.add(new AdvanceFromDoghouseRunnable(context)); |
|
47 return runnables; |
|
48 } |
|
49 |
|
50 @Override |
|
51 public void onReceive(final Context context, Intent intent) { |
|
52 Logger.setThreadLogTag(FxAccountConstants.GLOBAL_LOG_TAG); |
|
53 Logger.info(LOG_TAG, "Upgrade broadcast received."); |
|
54 |
|
55 // Iterate Runnable instances one at a time. |
|
56 final Executor executor = Executors.newSingleThreadExecutor(); |
|
57 for (final Runnable runnable : onUpgradeRunnables(context)) { |
|
58 executor.execute(new Runnable() { |
|
59 @Override |
|
60 public void run() { |
|
61 try { |
|
62 runnable.run(); |
|
63 } catch (Exception e) { |
|
64 // We really don't want to throw on a background thread, so we |
|
65 // catch, log, and move on. |
|
66 Logger.error(LOG_TAG, "Got exception executing background upgrade Runnable; ignoring.", e); |
|
67 } |
|
68 } |
|
69 }); |
|
70 } |
|
71 } |
|
72 |
|
73 /** |
|
74 * A Runnable that tries to unpickle any pickled Firefox Accounts. |
|
75 */ |
|
76 protected static class MaybeUnpickleRunnable implements Runnable { |
|
77 protected final Context context; |
|
78 |
|
79 public MaybeUnpickleRunnable(Context context) { |
|
80 this.context = context; |
|
81 } |
|
82 |
|
83 @Override |
|
84 public void run() { |
|
85 // Querying the accounts will unpickle any pickled Firefox Account. |
|
86 Logger.info(LOG_TAG, "Trying to unpickle any pickled Firefox Account."); |
|
87 FirefoxAccounts.getFirefoxAccounts(context); |
|
88 } |
|
89 } |
|
90 |
|
91 /** |
|
92 * A Runnable that tries to advance existing Firefox Accounts that are in the |
|
93 * Doghouse state to the Separated state. |
|
94 * <p> |
|
95 * This is our main deprecation-and-upgrade mechanism: in some way, the |
|
96 * Account gets moved to the Doghouse state. If possible, an upgraded version |
|
97 * of the package advances to Separated, prompting the user to re-connect the |
|
98 * Account. |
|
99 */ |
|
100 protected static class AdvanceFromDoghouseRunnable implements Runnable { |
|
101 protected final Context context; |
|
102 |
|
103 public AdvanceFromDoghouseRunnable(Context context) { |
|
104 this.context = context; |
|
105 } |
|
106 |
|
107 @Override |
|
108 public void run() { |
|
109 final Account[] accounts = FirefoxAccounts.getFirefoxAccounts(context); |
|
110 Logger.info(LOG_TAG, "Trying to advance " + accounts.length + " existing Firefox Accounts from the Doghouse to Separated (if necessary)."); |
|
111 for (Account account : accounts) { |
|
112 try { |
|
113 final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); |
|
114 // For great debugging. |
|
115 if (FxAccountConstants.LOG_PERSONAL_INFORMATION) { |
|
116 fxAccount.dump(); |
|
117 } |
|
118 State state = fxAccount.getState(); |
|
119 if (state == null || state.getStateLabel() != StateLabel.Doghouse) { |
|
120 Logger.debug(LOG_TAG, "Account named like " + Utils.obfuscateEmail(account.name) + " is not in the Doghouse; skipping."); |
|
121 continue; |
|
122 } |
|
123 Logger.debug(LOG_TAG, "Account named like " + Utils.obfuscateEmail(account.name) + " is in the Doghouse; advancing to Separated."); |
|
124 fxAccount.setState(state.makeSeparatedState()); |
|
125 } catch (Exception e) { |
|
126 Logger.warn(LOG_TAG, "Got exception trying to advance account named like " + Utils.obfuscateEmail(account.name) + |
|
127 " from Doghouse to Separated state; ignoring.", e); |
|
128 } |
|
129 } |
|
130 } |
|
131 } |
|
132 } |