diff options
Diffstat (limited to 'mobile/android/services/src/main/java/org/mozilla/gecko/background')
35 files changed, 0 insertions, 3938 deletions
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/ReadingListConstants.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/ReadingListConstants.java deleted file mode 100644 index df603a58e..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/ReadingListConstants.java +++ /dev/null @@ -1,23 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background; - -import org.mozilla.gecko.AppConstants; - -/** - * This is in 'background' not 'reading' so that it's still usable even when the - * Reading List feature is build-time disabled. - */ -public class ReadingListConstants { - public static final String GLOBAL_LOG_TAG = "FxReadingList"; - public static final String USER_AGENT = "Firefox-Android-FxReader/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_UA_NAME + ")"; - public static final String DEFAULT_DEV_ENDPOINT = "https://readinglist.dev.mozaws.net/v1/"; - public static final String DEFAULT_PROD_ENDPOINT = "https://readinglist.services.mozilla.com/v1/"; - - public static final String OAUTH_SCOPE_READINGLIST = "readinglist"; - public static final String AUTH_TOKEN_TYPE = "oauth::" + OAUTH_SCOPE_READINGLIST; - - public static boolean DEBUG = false; -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/EditorBranch.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/EditorBranch.java deleted file mode 100644 index 1ead09afa..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/EditorBranch.java +++ /dev/null @@ -1,82 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common; - -import java.util.Set; - -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; - -public class EditorBranch implements Editor { - - private final String prefix; - private Editor editor; - - public EditorBranch(final SharedPreferences prefs, final String prefix) { - if (!prefix.endsWith(".")) { - throw new IllegalArgumentException("No trailing period in prefix."); - } - this.prefix = prefix; - this.editor = prefs.edit(); - } - - @Override - public void apply() { - this.editor.apply(); - } - - @Override - public Editor clear() { - this.editor = this.editor.clear(); - return this; - } - - @Override - public boolean commit() { - return this.editor.commit(); - } - - @Override - public Editor putBoolean(String key, boolean value) { - this.editor = this.editor.putBoolean(prefix + key, value); - return this; - } - - @Override - public Editor putFloat(String key, float value) { - this.editor = this.editor.putFloat(prefix + key, value); - return this; - } - - @Override - public Editor putInt(String key, int value) { - this.editor = this.editor.putInt(prefix + key, value); - return this; - } - - @Override - public Editor putLong(String key, long value) { - this.editor = this.editor.putLong(prefix + key, value); - return this; - } - - @Override - public Editor putString(String key, String value) { - this.editor = this.editor.putString(prefix + key, value); - return this; - } - - // Not marking as Override, because Android <= 10 doesn't have - // putStringSet. Neither can we implement it. - public Editor putStringSet(String key, Set<String> value) { - throw new RuntimeException("putStringSet not available."); - } - - @Override - public Editor remove(String key) { - this.editor = this.editor.remove(prefix + key); - return this; - } -}
\ No newline at end of file diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/GlobalConstants.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/GlobalConstants.java deleted file mode 100644 index d661e62dc..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/GlobalConstants.java +++ /dev/null @@ -1,90 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common; - -import org.mozilla.gecko.AppConstants; -import org.mozilla.gecko.AppConstants.Versions; - -/** - * Constant values common to all Android services. - */ -public class GlobalConstants { - public static final String BROWSER_INTENT_PACKAGE = AppConstants.ANDROID_PACKAGE_NAME; - public static final String BROWSER_INTENT_CLASS = AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS; - - public static final int SHARED_PREFERENCES_MODE = 0; - - // Common time values. - public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - public static final long MILLISECONDS_PER_SIX_MONTHS = 180 * MILLISECONDS_PER_DAY; - - // Acceptable cipher suites. - /** - * We support only a very limited range of strong cipher suites and protocols: - * no SSLv3 or TLSv1.0 (if we can), no DHE ciphers that might be vulnerable to Logjam - * (https://weakdh.org/), no RC4. - * - * Backstory: Bug 717691 (we no longer support Android 2.2, so the name - * workaround is unnecessary), Bug 1081953, Bug 1061273, Bug 1166839. - * - * See <http://developer.android.com/reference/javax/net/ssl/SSLSocket.html> for - * supported Android versions for each set of protocols and cipher suites. - * - * Note that currently we need to support connections to Sync 1.1 on Mozilla-hosted infra, - * as well as connections to FxA and Sync 1.5 on AWS. - * - * ELB cipher suites: - * <http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-security-policy-table.html> - */ - public static final String[] DEFAULT_CIPHER_SUITES; - public static final String[] DEFAULT_PROTOCOLS; - - static { - // Prioritize 128 over 256 as a tradeoff between device CPU/battery and the minor - // increase in strength. - if (Versions.feature20Plus) { - DEFAULT_CIPHER_SUITES = new String[] - { - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", // 20+ - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // 20+ - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", // 20+ - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 11+ - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // 20+ - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", // 20+ - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 11+ - - // For Sync 1.1. - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", // 9+ - "TLS_RSA_WITH_AES_128_CBC_SHA", // 9+ - }; - } else { - DEFAULT_CIPHER_SUITES = new String[] - { - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 11+ - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", // 11+ - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 11+ - - // For Sync 1.1. - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", // 9+ - "TLS_RSA_WITH_AES_128_CBC_SHA", // 9+ - }; - } - - if (Versions.feature16Plus) { - DEFAULT_PROTOCOLS = new String[] - { - "TLSv1.2", - "TLSv1.1", - "TLSv1", // We would like to remove this, and will do so when we can. - }; - } else { - // Fall back to TLSv1 if there's nothing better. - DEFAULT_PROTOCOLS = new String[] - { - "TLSv1", - }; - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/PrefsBranch.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/PrefsBranch.java deleted file mode 100644 index 78d5f61a1..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/PrefsBranch.java +++ /dev/null @@ -1,83 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common; - -import java.util.Map; -import java.util.Set; - -import android.content.SharedPreferences; - -/** - * A wrapper around a portion of the SharedPreferences space. - */ -public class PrefsBranch implements SharedPreferences { - private final SharedPreferences prefs; - private final String prefix; // Including trailing period. - - public PrefsBranch(SharedPreferences prefs, String prefix) { - if (!prefix.endsWith(".")) { - throw new IllegalArgumentException("No trailing period in prefix."); - } - this.prefs = prefs; - this.prefix = prefix; - } - - @Override - public boolean contains(String key) { - return prefs.contains(prefix + key); - } - - @Override - public Editor edit() { - return new EditorBranch(prefs, prefix); - } - - @Override - public Map<String, ?> getAll() { - // Not implemented. TODO - return null; - } - - @Override - public boolean getBoolean(String key, boolean defValue) { - return prefs.getBoolean(prefix + key, defValue); - } - - @Override - public float getFloat(String key, float defValue) { - return prefs.getFloat(prefix + key, defValue); - } - - @Override - public int getInt(String key, int defValue) { - return prefs.getInt(prefix + key, defValue); - } - - @Override - public long getLong(String key, long defValue) { - return prefs.getLong(prefix + key, defValue); - } - - @Override - public String getString(String key, String defValue) { - return prefs.getString(prefix + key, defValue); - } - - // Not marking as Override, because Android <= 10 doesn't have - // getStringSet. Neither can we implement it. - public Set<String> getStringSet(String key, Set<String> defValue) { - throw new RuntimeException("getStringSet not available."); - } - - @Override - public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { - prefs.registerOnSharedPreferenceChangeListener(listener); - } - - @Override - public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { - prefs.unregisterOnSharedPreferenceChangeListener(listener); - } -}
\ No newline at end of file diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/Logger.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/Logger.java deleted file mode 100644 index 2575717eb..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/Logger.java +++ /dev/null @@ -1,232 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log; - -import java.io.PrintWriter; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.mozilla.gecko.background.common.GlobalConstants; -import org.mozilla.gecko.background.common.log.writers.AndroidLevelCachingLogWriter; -import org.mozilla.gecko.background.common.log.writers.AndroidLogWriter; -import org.mozilla.gecko.background.common.log.writers.LogWriter; -import org.mozilla.gecko.background.common.log.writers.PrintLogWriter; -import org.mozilla.gecko.background.common.log.writers.SimpleTagLogWriter; -import org.mozilla.gecko.background.common.log.writers.ThreadLocalTagLogWriter; - -import android.util.Log; - -/** - * Logging helper class. Serializes all log operations (by synchronizing). - */ -public class Logger { - public static final String LOGGER_TAG = "Logger"; - public static final String DEFAULT_LOG_TAG = "GeckoLogger"; - - // For extra debugging. - public static boolean LOG_PERSONAL_INFORMATION = false; - - /** - * Allow each thread to use its own global log tag. This allows - * independent services to log as different sources. - * - * When your thread sets up logging, it should do something like the following: - * - * Logger.setThreadLogTag("MyTag"); - * - * The value is inheritable, so worker threads and such do not need to - * set the same log tag as their parent. - */ - private static final InheritableThreadLocal<String> logTag = new InheritableThreadLocal<String>() { - @Override - protected String initialValue() { - return DEFAULT_LOG_TAG; - } - }; - - public static void setThreadLogTag(final String logTag) { - Logger.logTag.set(logTag); - } - public static String getThreadLogTag() { - return Logger.logTag.get(); - } - - /** - * Current set of writers to which we will log. - * <p> - * We want logging to be available while running tests, so we initialize - * this set statically. - */ - protected final static Set<LogWriter> logWriters; - static { - final Set<LogWriter> defaultWriters = Logger.defaultLogWriters(); - logWriters = new LinkedHashSet<LogWriter>(defaultWriters); - } - - /** - * Default set of log writers to log to. - */ - public final static Set<LogWriter> defaultLogWriters() { - final String processedPackage = GlobalConstants.BROWSER_INTENT_PACKAGE.replace("org.mozilla.", ""); - - final Set<LogWriter> defaultLogWriters = new LinkedHashSet<LogWriter>(); - - final LogWriter log = new AndroidLogWriter(); - final LogWriter cache = new AndroidLevelCachingLogWriter(log); - - final LogWriter single = new SimpleTagLogWriter(processedPackage, new ThreadLocalTagLogWriter(Logger.logTag, cache)); - - defaultLogWriters.add(single); - return defaultLogWriters; - } - - public static synchronized void startLoggingTo(LogWriter logWriter) { - logWriters.add(logWriter); - } - - public static synchronized void startLoggingToWriters(Set<LogWriter> writers) { - logWriters.addAll(writers); - } - - public static synchronized void stopLoggingTo(LogWriter logWriter) { - try { - logWriter.close(); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception closing and removing LogWriter " + logWriter + ".", e); - } - logWriters.remove(logWriter); - } - - public static synchronized void stopLoggingToAll() { - for (LogWriter logWriter : logWriters) { - try { - logWriter.close(); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception closing and removing LogWriter " + logWriter + ".", e); - } - } - logWriters.clear(); - } - - /** - * Write to only the default log writers. - */ - public static synchronized void resetLogging() { - stopLoggingToAll(); - logWriters.addAll(Logger.defaultLogWriters()); - } - - /** - * Start writing log output to stdout. - * <p> - * Use <code>resetLogging</code> to stop logging to stdout. - */ - public static synchronized void startLoggingToConsole() { - setThreadLogTag("Test"); - startLoggingTo(new PrintLogWriter(new PrintWriter(System.out, true))); - } - - // Synchronized version for other classes to use. - public static synchronized boolean shouldLogVerbose(String logTag) { - for (LogWriter logWriter : logWriters) { - if (logWriter.shouldLogVerbose(logTag)) { - return true; - } - } - return false; - } - - public static void error(String tag, String message) { - Logger.error(tag, message, null); - } - - public static void warn(String tag, String message) { - Logger.warn(tag, message, null); - } - - public static void info(String tag, String message) { - Logger.info(tag, message, null); - } - - public static void debug(String tag, String message) { - Logger.debug(tag, message, null); - } - - public static void trace(String tag, String message) { - Logger.trace(tag, message, null); - } - - public static void pii(String tag, String message) { - if (LOG_PERSONAL_INFORMATION) { - Logger.debug(tag, "$$PII$$: " + message); - } - } - - public static synchronized void error(String tag, String message, Throwable error) { - Iterator<LogWriter> it = logWriters.iterator(); - while (it.hasNext()) { - LogWriter writer = it.next(); - try { - writer.error(tag, message, error); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception logging; removing LogWriter " + writer + ".", e); - it.remove(); - } - } - } - - public static synchronized void warn(String tag, String message, Throwable error) { - Iterator<LogWriter> it = logWriters.iterator(); - while (it.hasNext()) { - LogWriter writer = it.next(); - try { - writer.warn(tag, message, error); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception logging; removing LogWriter " + writer + ".", e); - it.remove(); - } - } - } - - public static synchronized void info(String tag, String message, Throwable error) { - Iterator<LogWriter> it = logWriters.iterator(); - while (it.hasNext()) { - LogWriter writer = it.next(); - try { - writer.info(tag, message, error); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception logging; removing LogWriter " + writer + ".", e); - it.remove(); - } - } - } - - public static synchronized void debug(String tag, String message, Throwable error) { - Iterator<LogWriter> it = logWriters.iterator(); - while (it.hasNext()) { - LogWriter writer = it.next(); - try { - writer.debug(tag, message, error); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception logging; removing LogWriter " + writer + ".", e); - it.remove(); - } - } - } - - public static synchronized void trace(String tag, String message, Throwable error) { - Iterator<LogWriter> it = logWriters.iterator(); - while (it.hasNext()) { - LogWriter writer = it.next(); - try { - writer.trace(tag, message, error); - } catch (Exception e) { - Log.e(LOGGER_TAG, "Got exception logging; removing LogWriter " + writer + ".", e); - it.remove(); - } - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/AndroidLevelCachingLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/AndroidLevelCachingLogWriter.java deleted file mode 100644 index ac4250a03..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/AndroidLevelCachingLogWriter.java +++ /dev/null @@ -1,132 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -import java.util.IdentityHashMap; -import java.util.Map; - -import android.util.Log; - -/** - * Make a <code>LogWriter</code> only log when the Android log system says to. - */ -public class AndroidLevelCachingLogWriter extends LogWriter { - protected final LogWriter inner; - - public AndroidLevelCachingLogWriter(LogWriter inner) { - this.inner = inner; - } - - // I can't believe we have to implement this ourselves. - // These aren't synchronized (and neither are the setters) because - // the logging calls themselves are synchronized. - private Map<String, Boolean> isErrorLoggable = new IdentityHashMap<String, Boolean>(); - private Map<String, Boolean> isWarnLoggable = new IdentityHashMap<String, Boolean>(); - private Map<String, Boolean> isInfoLoggable = new IdentityHashMap<String, Boolean>(); - private Map<String, Boolean> isDebugLoggable = new IdentityHashMap<String, Boolean>(); - private Map<String, Boolean> isVerboseLoggable = new IdentityHashMap<String, Boolean>(); - - /** - * Empty the caches of log levels. - */ - public void refreshLogLevels() { - isErrorLoggable = new IdentityHashMap<String, Boolean>(); - isWarnLoggable = new IdentityHashMap<String, Boolean>(); - isInfoLoggable = new IdentityHashMap<String, Boolean>(); - isDebugLoggable = new IdentityHashMap<String, Boolean>(); - isVerboseLoggable = new IdentityHashMap<String, Boolean>(); - } - - private boolean shouldLogError(String logTag) { - Boolean out = isErrorLoggable.get(logTag); - if (out != null) { - return out; - } - out = Log.isLoggable(logTag, Log.ERROR); - isErrorLoggable.put(logTag, out); - return out; - } - - private boolean shouldLogWarn(String logTag) { - Boolean out = isWarnLoggable.get(logTag); - if (out != null) { - return out; - } - out = Log.isLoggable(logTag, Log.WARN); - isWarnLoggable.put(logTag, out); - return out; - } - - private boolean shouldLogInfo(String logTag) { - Boolean out = isInfoLoggable.get(logTag); - if (out != null) { - return out; - } - out = Log.isLoggable(logTag, Log.INFO); - isInfoLoggable.put(logTag, out); - return out; - } - - private boolean shouldLogDebug(String logTag) { - Boolean out = isDebugLoggable.get(logTag); - if (out != null) { - return out; - } - out = Log.isLoggable(logTag, Log.DEBUG); - isDebugLoggable.put(logTag, out); - return out; - } - - @Override - public boolean shouldLogVerbose(String logTag) { - Boolean out = isVerboseLoggable.get(logTag); - if (out != null) { - return out; - } - out = Log.isLoggable(logTag, Log.VERBOSE); - isVerboseLoggable.put(logTag, out); - return out; - } - - @Override - public void error(String tag, String message, Throwable error) { - if (shouldLogError(tag)) { - inner.error(tag, message, error); - } - } - - @Override - public void warn(String tag, String message, Throwable error) { - if (shouldLogWarn(tag)) { - inner.warn(tag, message, error); - } - } - - @Override - public void info(String tag, String message, Throwable error) { - if (shouldLogInfo(tag)) { - inner.info(tag, message, error); - } - } - - @Override - public void debug(String tag, String message, Throwable error) { - if (shouldLogDebug(tag)) { - inner.debug(tag, message, error); - } - } - - @Override - public void trace(String tag, String message, Throwable error) { - if (shouldLogVerbose(tag)) { - inner.trace(tag, message, error); - } - } - - @Override - public void close() { - inner.close(); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/AndroidLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/AndroidLogWriter.java deleted file mode 100644 index 9d309844d..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/AndroidLogWriter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -import android.util.Log; - -/** - * Log to the Android log. - */ -public class AndroidLogWriter extends LogWriter { - @Override - public boolean shouldLogVerbose(String logTag) { - return true; - } - - @Override - public void error(String tag, String message, Throwable error) { - Log.e(tag, message, error); - } - - @Override - public void warn(String tag, String message, Throwable error) { - Log.w(tag, message, error); - } - - @Override - public void info(String tag, String message, Throwable error) { - Log.i(tag, message, error); - } - - @Override - public void debug(String tag, String message, Throwable error) { - Log.d(tag, message, error); - } - - @Override - public void trace(String tag, String message, Throwable error) { - Log.v(tag, message, error); - } - - @Override - public void close() { - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/LevelFilteringLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/LevelFilteringLogWriter.java deleted file mode 100644 index 74c3608c4..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/LevelFilteringLogWriter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -import android.util.Log; - -/** - * A LogWriter that logs only if the message is as important as the specified - * level. For example, if the specified level is <code>Log.WARN</code>, only - * <code>warn</code> and <code>error</code> will log. - */ -public class LevelFilteringLogWriter extends LogWriter { - protected final LogWriter inner; - protected final int logLevel; - - public LevelFilteringLogWriter(int logLevel, LogWriter inner) { - this.inner = inner; - this.logLevel = logLevel; - } - - @Override - public void close() { - inner.close(); - } - - @Override - public void error(String tag, String message, Throwable error) { - if (logLevel <= Log.ERROR) { - inner.error(tag, message, error); - } - } - - @Override - public void warn(String tag, String message, Throwable error) { - if (logLevel <= Log.WARN) { - inner.warn(tag, message, error); - } - } - - @Override - public void info(String tag, String message, Throwable error) { - if (logLevel <= Log.INFO) { - inner.info(tag, message, error); - } - } - - @Override - public void debug(String tag, String message, Throwable error) { - if (logLevel <= Log.DEBUG) { - inner.debug(tag, message, error); - } - } - - @Override - public void trace(String tag, String message, Throwable error) { - if (logLevel <= Log.VERBOSE) { - inner.trace(tag, message, error); - } - } - - @Override - public boolean shouldLogVerbose(String tag) { - return logLevel <= Log.VERBOSE; - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/LogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/LogWriter.java deleted file mode 100644 index acfb09969..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/LogWriter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -/** - * An abstract object that logs information in some way. - * <p> - * Intended to be composed with other log writers, for example a log - * writer could make all log entries have the same single log tag, or - * could ignore certain log levels, before delegating to an inner log - * writer. - */ -public abstract class LogWriter { - public abstract void error(String tag, String message, Throwable error); - public abstract void warn(String tag, String message, Throwable error); - public abstract void info(String tag, String message, Throwable error); - public abstract void debug(String tag, String message, Throwable error); - public abstract void trace(String tag, String message, Throwable error); - - /** - * We expect <code>close</code> to be called only by static - * synchronized methods in class <code>Logger</code>. - */ - public abstract void close(); - - public abstract boolean shouldLogVerbose(String tag); -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/PrintLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/PrintLogWriter.java deleted file mode 100644 index 6e1f63de3..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/PrintLogWriter.java +++ /dev/null @@ -1,77 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -import java.io.PrintWriter; - -/** - * Log to a <code>PrintWriter</code>. - */ -public class PrintLogWriter extends LogWriter { - protected final PrintWriter pw; - protected boolean closed = false; - - public static final String ERROR = " :: E :: "; - public static final String WARN = " :: W :: "; - public static final String INFO = " :: I :: "; - public static final String DEBUG = " :: D :: "; - public static final String VERBOSE = " :: V :: "; - - public PrintLogWriter(PrintWriter pw) { - this.pw = pw; - } - - protected void log(String tag, String message, Throwable error) { - if (closed) { - return; - } - - pw.println(tag + message); - if (error != null) { - error.printStackTrace(pw); - } - } - - @Override - public void error(String tag, String message, Throwable error) { - log(tag, ERROR + message, error); - } - - @Override - public void warn(String tag, String message, Throwable error) { - log(tag, WARN + message, error); - } - - @Override - public void info(String tag, String message, Throwable error) { - log(tag, INFO + message, error); - } - - @Override - public void debug(String tag, String message, Throwable error) { - log(tag, DEBUG + message, error); - } - - @Override - public void trace(String tag, String message, Throwable error) { - log(tag, VERBOSE + message, error); - } - - @Override - public boolean shouldLogVerbose(String tag) { - return true; - } - - @Override - public void close() { - if (closed) { - return; - } - if (pw != null) { - pw.close(); - } - closed = true; - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/SimpleTagLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/SimpleTagLogWriter.java deleted file mode 100644 index a17654371..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/SimpleTagLogWriter.java +++ /dev/null @@ -1,21 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -/** - * Make a <code>LogWriter</code> only log with a single string tag. - */ -public class SimpleTagLogWriter extends TagLogWriter { - final String tag; - public SimpleTagLogWriter(String tag, LogWriter inner) { - super(inner); - this.tag = tag; - } - - @Override - protected String getMainTag() { - return tag; - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/StringLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/StringLogWriter.java deleted file mode 100644 index d6a9f5eb8..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/StringLogWriter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -import java.io.PrintWriter; -import java.io.StringWriter; - -public class StringLogWriter extends LogWriter { - protected final StringWriter sw; - protected final PrintLogWriter inner; - - public StringLogWriter() { - sw = new StringWriter(); - inner = new PrintLogWriter(new PrintWriter(sw)); - } - - public String toString() { - return sw.toString(); - } - - @Override - public boolean shouldLogVerbose(String tag) { - return true; - } - - @Override - public void error(String tag, String message, Throwable error) { - inner.error(tag, message, error); - } - - @Override - public void warn(String tag, String message, Throwable error) { - inner.warn(tag, message, error); - } - - @Override - public void info(String tag, String message, Throwable error) { - inner.info(tag, message, error); - } - - @Override - public void debug(String tag, String message, Throwable error) { - inner.debug(tag, message, error); - } - - @Override - public void trace(String tag, String message, Throwable error) { - inner.trace(tag, message, error); - } - - @Override - public void close() { - inner.close(); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/TagLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/TagLogWriter.java deleted file mode 100644 index fbcd94a91..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/TagLogWriter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -/** - * A @link{LogWriter} that logs each message under a parent tag. - */ -public abstract class TagLogWriter extends LogWriter { - - protected final LogWriter inner; - - public TagLogWriter(final LogWriter inner) { - super(); - this.inner = inner; - } - - protected abstract String getMainTag(); - - @Override - public void error(String tag, String message, Throwable error) { - inner.error(this.getMainTag(), tag + " :: " + message, error); - } - - @Override - public void warn(String tag, String message, Throwable error) { - inner.warn(this.getMainTag(), tag + " :: " + message, error); - } - - @Override - public void info(String tag, String message, Throwable error) { - inner.info(this.getMainTag(), tag + " :: " + message, error); - } - - @Override - public void debug(String tag, String message, Throwable error) { - inner.debug(this.getMainTag(), tag + " :: " + message, error); - } - - @Override - public void trace(String tag, String message, Throwable error) { - inner.trace(this.getMainTag(), tag + " :: " + message, error); - } - - @Override - public boolean shouldLogVerbose(String tag) { - return inner.shouldLogVerbose(this.getMainTag()); - } - - @Override - public void close() { - inner.close(); - } -}
\ No newline at end of file diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/ThreadLocalTagLogWriter.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/ThreadLocalTagLogWriter.java deleted file mode 100644 index 0c83504a0..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/log/writers/ThreadLocalTagLogWriter.java +++ /dev/null @@ -1,25 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.log.writers; - -/** - * Log with a single global tag… but that tag can be different for each thread. - * - * Takes a @link{ThreadLocal} as a constructor parameter. - */ -public class ThreadLocalTagLogWriter extends TagLogWriter { - - private final ThreadLocal<String> tag; - - public ThreadLocalTagLogWriter(ThreadLocal<String> tag, LogWriter inner) { - super(inner); - this.tag = tag; - } - - @Override - protected String getMainTag() { - return this.tag.get(); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/telemetry/TelemetryWrapper.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/telemetry/TelemetryWrapper.java deleted file mode 100644 index 6639b817d..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/common/telemetry/TelemetryWrapper.java +++ /dev/null @@ -1,56 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.common.telemetry; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import org.mozilla.gecko.background.common.log.Logger; - -/** - * Android Background Services are normally built into Fennec, but can also be - * built as a stand-alone APK for rapid local development. The current Telemetry - * implementation is coupled to Gecko, and Background Services should not - * interact with Gecko directly. To maintain this independence, Background - * Services lazily introspects the relevant Telemetry class from the enclosing - * package, warning but otherwise ignoring failures during introspection or - * invocation. - * <p> - * It is possible that Background Services will introspect and invoke the - * Telemetry implementation while Gecko is not running. In this case, the Fennec - * process itself buffers Telemetry events until such time as they can be - * flushed to disk and uploaded. <b>There is no guarantee that all Telemetry - * events will be uploaded!</b> Depending on the volume of data and the - * application lifecycle, Telemetry events may be dropped. - */ -public class TelemetryWrapper { - private static final String LOG_TAG = TelemetryWrapper.class.getSimpleName(); - - // Marking this volatile maintains thread safety cheaply. - private static volatile Method mAddToHistogram; - - public static void addToHistogram(String key, int value) { - if (mAddToHistogram == null) { - try { - final Class<?> telemetry = Class.forName("org.mozilla.gecko.Telemetry"); - mAddToHistogram = telemetry.getMethod("addToHistogram", String.class, int.class); - } catch (ClassNotFoundException e) { - Logger.warn(LOG_TAG, "org.mozilla.gecko.Telemetry class found!"); - return; - } catch (NoSuchMethodException e) { - Logger.warn(LOG_TAG, "org.mozilla.gecko.Telemetry.addToHistogram(String, int) method not found!"); - return; - } - } - - if (mAddToHistogram != null) { - try { - mAddToHistogram.invoke(null, key, value); - } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { - Logger.warn(LOG_TAG, "Got exception invoking telemetry!"); - } - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/CursorDumper.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/CursorDumper.java deleted file mode 100644 index bce968b00..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/CursorDumper.java +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.db; - -import android.database.Cursor; - -/** - * A utility for dumping a cursor the debug log. - * <p> - * <b>For debugging only!</p> - */ -public class CursorDumper { - protected static String fixedWidth(int width, String s) { - if (s == null) { - return spaces(width); - } - int length = s.length(); - if (width == length) { - return s; - } - if (width > length) { - return s + spaces(width - length); - } - return s.substring(0, width); - } - - protected static String spaces(int i) { - return " ".substring(0, i); - } - - protected static String dashes(int i) { - return "-------------------------------------".substring(0, i); - } - - /** - * Dump a cursor to the debug log, ignoring any log level settings. - * <p> - * The position in the cursor is maintained. Caller is responsible for opening - * and closing cursor. - * - * @param cursor - * to dump. - */ - public static void dumpCursor(Cursor cursor) { - dumpCursor(cursor, 18, "records"); - } - - /** - * Dump a cursor to the debug log, ignoring any log level settings. - * <p> - * The position in the cursor is maintained. Caller is responsible for opening - * and closing cursor. - * - * @param cursor - * to dump. - * @param columnWidth - * how many characters per cursor column. - * @param tags - * a descriptor, printed like "(10 tags)", in the header row. - */ - protected static void dumpCursor(Cursor cursor, int columnWidth, String tags) { - int originalPosition = cursor.getPosition(); - try { - String[] columnNames = cursor.getColumnNames(); - int columnCount = cursor.getColumnCount(); - - for (int i = 0; i < columnCount; ++i) { - System.out.print(fixedWidth(columnWidth, columnNames[i]) + " | "); - } - System.out.println("(" + cursor.getCount() + " " + tags + ")"); - for (int i = 0; i < columnCount; ++i) { - System.out.print(dashes(columnWidth) + " | "); - } - System.out.println(""); - if (!cursor.moveToFirst()) { - System.out.println("EMPTY"); - return; - } - - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - for (int i = 0; i < columnCount; ++i) { - System.out.print(fixedWidth(columnWidth, cursor.getString(i)) + " | "); - } - System.out.println(""); - cursor.moveToNext(); - } - for (int i = 0; i < columnCount-1; ++i) { - System.out.print(dashes(columnWidth + 3)); - } - System.out.print(dashes(columnWidth + 3 - 1)); - System.out.println(""); - } finally { - cursor.moveToPosition(originalPosition); - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/Tab.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/Tab.java deleted file mode 100644 index f38cfdf0e..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/db/Tab.java +++ /dev/null @@ -1,86 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.db; - -import org.json.simple.JSONArray; -import org.mozilla.gecko.db.BrowserContract; -import org.mozilla.gecko.db.BrowserContract.Tabs; -import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.repositories.android.RepoUtils; - -import android.content.ContentValues; -import android.database.Cursor; - -// Immutable. -public class Tab { - public final String title; - public final String icon; - public final JSONArray history; - public final long lastUsed; - - public Tab(String title, String icon, JSONArray history, long lastUsed) { - this.title = title; - this.icon = icon; - this.history = history; - this.lastUsed = lastUsed; - } - - public ContentValues toContentValues(String clientGUID, int position) { - ContentValues out = new ContentValues(); - out.put(BrowserContract.Tabs.POSITION, position); - out.put(BrowserContract.Tabs.CLIENT_GUID, clientGUID); - - out.put(BrowserContract.Tabs.FAVICON, this.icon); - out.put(BrowserContract.Tabs.LAST_USED, this.lastUsed); - out.put(BrowserContract.Tabs.TITLE, this.title); - out.put(BrowserContract.Tabs.URL, (String) this.history.get(0)); - out.put(BrowserContract.Tabs.HISTORY, this.history.toJSONString()); - return out; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Tab)) { - return false; - } - final Tab other = (Tab) o; - - if (!RepoUtils.stringsEqual(this.title, other.title)) { - return false; - } - if (!RepoUtils.stringsEqual(this.icon, other.icon)) { - return false; - } - - if (!(this.lastUsed == other.lastUsed)) { - return false; - } - - return Utils.sameArrays(this.history, other.history); - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - /** - * Extract a <code>Tab</code> from a cursor row. - * <p> - * Caller is responsible for creating, positioning, and closing the cursor. - * - * @param cursor - * to inspect. - * @return <code>Tab</code> instance. - */ - public static Tab fromCursor(final Cursor cursor) { - final String title = RepoUtils.getStringFromCursor(cursor, Tabs.TITLE); - final String icon = RepoUtils.getStringFromCursor(cursor, Tabs.FAVICON); - final JSONArray history = RepoUtils.getJSONArrayFromCursor(cursor, Tabs.HISTORY); - final long lastUsed = RepoUtils.getLongFromCursor(cursor, Tabs.LAST_USED); - - return new Tab(title, icon, history, lastUsed); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20CreateDelegate.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20CreateDelegate.java deleted file mode 100644 index 98809137f..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20CreateDelegate.java +++ /dev/null @@ -1,52 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.Utils; - -import java.io.UnsupportedEncodingException; -import java.security.GeneralSecurityException; - -public class FxAccount20CreateDelegate { - protected final byte[] emailUTF8; - protected final byte[] authPW; - protected final boolean preVerified; - - /** - * Make a new "create account" delegate. - * - * @param emailUTF8 - * email as UTF-8 bytes. - * @param quickStretchedPW - * quick stretched password as bytes. - * @param preVerified - * true if account should be marked already verified; only effective - * for non-production auth servers. - * @throws UnsupportedEncodingException - * @throws GeneralSecurityException - */ - public FxAccount20CreateDelegate(byte[] emailUTF8, byte[] quickStretchedPW, boolean preVerified) throws UnsupportedEncodingException, GeneralSecurityException { - this.emailUTF8 = emailUTF8; - this.authPW = FxAccountUtils.generateAuthPW(quickStretchedPW); - this.preVerified = preVerified; - } - - public ExtendedJSONObject getCreateBody() throws FxAccountClientException { - final ExtendedJSONObject body = new ExtendedJSONObject(); - try { - body.put("email", new String(emailUTF8, "UTF-8")); - body.put("authPW", Utils.byte2Hex(authPW)); - if (preVerified) { - // Production endpoints do not allow preVerified; this assumes we only - // set it when it's okay to send it. - body.put("preVerified", preVerified); - } - return body; - } catch (UnsupportedEncodingException e) { - throw new FxAccountClientException(e); - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20LoginDelegate.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20LoginDelegate.java deleted file mode 100644 index 0266a6eab..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccount20LoginDelegate.java +++ /dev/null @@ -1,36 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.Utils; - -import java.io.UnsupportedEncodingException; -import java.security.GeneralSecurityException; - -/** - * An abstraction around providing an email and authorization token to the auth - * server. - */ -public class FxAccount20LoginDelegate { - protected final byte[] emailUTF8; - protected final byte[] authPW; - - public FxAccount20LoginDelegate(byte[] emailUTF8, byte[] quickStretchedPW) throws UnsupportedEncodingException, GeneralSecurityException { - this.emailUTF8 = emailUTF8; - this.authPW = FxAccountUtils.generateAuthPW(quickStretchedPW); - } - - public ExtendedJSONObject getCreateBody() throws FxAccountClientException { - final ExtendedJSONObject body = new ExtendedJSONObject(); - try { - body.put("email", new String(emailUTF8, "UTF-8")); - body.put("authPW", Utils.byte2Hex(authPW)); - return body; - } catch (UnsupportedEncodingException e) { - throw new FxAccountClientException(e); - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java deleted file mode 100644 index ed959ff0e..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java +++ /dev/null @@ -1,24 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse; -import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; -import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; -import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys; -import org.mozilla.gecko.fxa.FxAccountDevice; -import org.mozilla.gecko.sync.ExtendedJSONObject; - -import java.util.List; - -public interface FxAccountClient { - public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate); - public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate); - public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate); - public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate<String> requestDelegate); - public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> requestDelegate); - public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate); - public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate); -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java deleted file mode 100644 index 596f4525e..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java +++ /dev/null @@ -1,914 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import android.support.annotation.NonNull; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.mozilla.gecko.background.common.log.Logger; -import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientMalformedResponseException; -import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; -import org.mozilla.gecko.fxa.FxAccountConstants; -import org.mozilla.gecko.Locales; -import org.mozilla.gecko.fxa.FxAccountDevice; -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.crypto.HKDF; -import org.mozilla.gecko.sync.net.AuthHeaderProvider; -import org.mozilla.gecko.sync.net.BaseResource; -import org.mozilla.gecko.sync.net.BaseResourceDelegate; -import org.mozilla.gecko.sync.net.HawkAuthHeaderProvider; -import org.mozilla.gecko.sync.net.Resource; -import org.mozilla.gecko.sync.net.SyncResponse; -import org.mozilla.gecko.sync.net.SyncStorageResponse; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLEncoder; -import java.security.GeneralSecurityException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.Executor; - -import javax.crypto.Mac; - -import ch.boye.httpclientandroidlib.HttpEntity; -import ch.boye.httpclientandroidlib.HttpHeaders; -import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.client.ClientProtocolException; -import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase; -import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient; - -/** - * An HTTP client for talking to an FxAccount server. - * <p> - * <p> - * The delegate structure used is a little different from the rest of the code - * base. We add a <code>RequestDelegate</code> layer that processes a typed - * value extracted from the body of a successful response. - */ -public class FxAccountClient20 implements FxAccountClient { - protected static final String LOG_TAG = FxAccountClient20.class.getSimpleName(); - - protected static final String ACCEPT_HEADER = "application/json;charset=utf-8"; - - public static final String JSON_KEY_EMAIL = "email"; - public static final String JSON_KEY_KEYFETCHTOKEN = "keyFetchToken"; - public static final String JSON_KEY_SESSIONTOKEN = "sessionToken"; - public static final String JSON_KEY_UID = "uid"; - public static final String JSON_KEY_VERIFIED = "verified"; - public static final String JSON_KEY_ERROR = "error"; - public static final String JSON_KEY_MESSAGE = "message"; - public static final String JSON_KEY_INFO = "info"; - public static final String JSON_KEY_CODE = "code"; - public static final String JSON_KEY_ERRNO = "errno"; - public static final String JSON_KEY_EXISTS = "exists"; - - protected static final String[] requiredErrorStringFields = { JSON_KEY_ERROR, JSON_KEY_MESSAGE, JSON_KEY_INFO }; - protected static final String[] requiredErrorLongFields = { JSON_KEY_CODE, JSON_KEY_ERRNO }; - - /** - * The server's URI. - * <p> - * We assume throughout that this ends with a trailing slash (and guarantee as - * much in the constructor). - */ - protected final String serverURI; - - protected final Executor executor; - - public FxAccountClient20(String serverURI, Executor executor) { - if (serverURI == null) { - throw new IllegalArgumentException("Must provide a server URI."); - } - if (executor == null) { - throw new IllegalArgumentException("Must provide a non-null executor."); - } - this.serverURI = serverURI.endsWith("/") ? serverURI : serverURI + "/"; - if (!this.serverURI.endsWith("/")) { - throw new IllegalArgumentException("Constructed serverURI must end with a trailing slash: " + this.serverURI); - } - this.executor = executor; - } - - protected BaseResource getBaseResource(String path, Map<String, String> queryParameters) throws UnsupportedEncodingException, URISyntaxException { - if (queryParameters == null || queryParameters.isEmpty()) { - return getBaseResource(path); - } - final String[] array = new String[2 * queryParameters.size()]; - int i = 0; - for (Entry<String, String> entry : queryParameters.entrySet()) { - array[i++] = entry.getKey(); - array[i++] = entry.getValue(); - } - return getBaseResource(path, array); - } - - /** - * Create <code>BaseResource</code>, encoding query parameters carefully. - * <p> - * This is equivalent to <code>android.net.Uri.Builder</code>, which is not - * present in our JUnit 4 tests. - * - * @param path fragment. - * @param queryParameters list of key/value query parameter pairs. Must be even length! - * @return <code>BaseResource<instance> - * @throws URISyntaxException - * @throws UnsupportedEncodingException - */ - protected BaseResource getBaseResource(String path, String... queryParameters) throws URISyntaxException, UnsupportedEncodingException { - final StringBuilder sb = new StringBuilder(serverURI); - sb.append(path); - if (queryParameters != null) { - int i = 0; - while (i < queryParameters.length) { - sb.append(i > 0 ? "&" : "?"); - final String key = queryParameters[i++]; - final String val = queryParameters[i++]; - sb.append(URLEncoder.encode(key, "UTF-8")); - sb.append("="); - sb.append(URLEncoder.encode(val, "UTF-8")); - } - } - return new BaseResource(new URI(sb.toString())); - } - - /** - * Process a typed value extracted from a successful response (in an - * endpoint-dependent way). - */ - public interface RequestDelegate<T> { - public void handleError(Exception e); - public void handleFailure(FxAccountClientRemoteException e); - public void handleSuccess(T result); - } - - /** - * Thin container for two cryptographic keys. - */ - public static class TwoKeys { - public final byte[] kA; - public final byte[] wrapkB; - public TwoKeys(byte[] kA, byte[] wrapkB) { - this.kA = kA; - this.wrapkB = wrapkB; - } - } - - protected <T> void invokeHandleError(final RequestDelegate<T> delegate, final Exception e) { - executor.execute(new Runnable() { - @Override - public void run() { - delegate.handleError(e); - } - }); - } - - enum ResponseType { - JSON_ARRAY, - JSON_OBJECT - } - - /** - * Translate resource callbacks into request callbacks invoked on the provided - * executor. - * <p> - * Override <code>handleSuccess</code> to parse the body of the resource - * request and call the request callback. <code>handleSuccess</code> is - * invoked via the executor, so you don't need to delegate further. - */ - protected abstract class ResourceDelegate<T> extends BaseResourceDelegate { - - protected void handleSuccess(final int status, HttpResponse response, final ExtendedJSONObject body) throws Exception { - throw new UnsupportedOperationException(); - } - - protected void handleSuccess(final int status, HttpResponse response, final JSONArray body) throws Exception { - throw new UnsupportedOperationException(); - } - - protected final RequestDelegate<T> delegate; - - protected final byte[] tokenId; - protected final byte[] reqHMACKey; - protected final SkewHandler skewHandler; - protected final ResponseType responseType; - - /** - * Create a delegate for an un-authenticated resource. - */ - public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, ResponseType responseType) { - this(resource, delegate, responseType, null, null); - } - - /** - * Create a delegate for a Hawk-authenticated resource. - * <p> - * Every Hawk request that encloses an entity (PATCH, POST, and PUT) will - * include the payload verification hash. - */ - public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, ResponseType responseType, final byte[] tokenId, final byte[] reqHMACKey) { - super(resource); - this.delegate = delegate; - this.reqHMACKey = reqHMACKey; - this.tokenId = tokenId; - this.skewHandler = SkewHandler.getSkewHandlerForResource(resource); - this.responseType = responseType; - } - - @Override - public AuthHeaderProvider getAuthHeaderProvider() { - if (tokenId != null && reqHMACKey != null) { - // We always include the payload verification hash for FxA Hawk-authenticated requests. - final boolean includePayloadVerificationHash = true; - return new HawkAuthHeaderProvider(Utils.byte2Hex(tokenId), reqHMACKey, includePayloadVerificationHash, skewHandler.getSkewInSeconds()); - } - return super.getAuthHeaderProvider(); - } - - @Override - public String getUserAgent() { - return FxAccountConstants.USER_AGENT; - } - - @Override - public void handleHttpResponse(HttpResponse response) { - try { - final int status = validateResponse(response); - skewHandler.updateSkew(response, now()); - invokeHandleSuccess(status, response); - } catch (FxAccountClientRemoteException e) { - if (!skewHandler.updateSkew(response, now())) { - // If we couldn't update skew, but we got a failure, let's try clearing the skew. - skewHandler.resetSkew(); - } - invokeHandleFailure(e); - } - } - - protected void invokeHandleFailure(final FxAccountClientRemoteException e) { - executor.execute(new Runnable() { - @Override - public void run() { - delegate.handleFailure(e); - } - }); - } - - protected void invokeHandleSuccess(final int status, final HttpResponse response) { - executor.execute(new Runnable() { - @Override - public void run() { - try { - SyncResponse syncResponse = new SyncResponse(response); - if (responseType == ResponseType.JSON_ARRAY) { - JSONArray body = syncResponse.jsonArrayBody(); - ResourceDelegate.this.handleSuccess(status, response, body); - } else { - ExtendedJSONObject body = syncResponse.jsonObjectBody(); - ResourceDelegate.this.handleSuccess(status, response, body); - } - } catch (Exception e) { - delegate.handleError(e); - } - } - }); - } - - @Override - public void handleHttpProtocolException(final ClientProtocolException e) { - invokeHandleError(delegate, e); - } - - @Override - public void handleHttpIOException(IOException e) { - invokeHandleError(delegate, e); - } - - @Override - public void handleTransportException(GeneralSecurityException e) { - invokeHandleError(delegate, e); - } - - @Override - public void addHeaders(HttpRequestBase request, DefaultHttpClient client) { - super.addHeaders(request, client); - - // The basics. - final Locale locale = Locale.getDefault(); - request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, Locales.getLanguageTag(locale)); - request.addHeader(HttpHeaders.ACCEPT, ACCEPT_HEADER); - } - } - - protected <T> void post(BaseResource resource, final ExtendedJSONObject requestBody) { - if (requestBody == null) { - resource.post((HttpEntity) null); - } else { - resource.post(requestBody); - } - } - - @SuppressWarnings("static-method") - public long now() { - return System.currentTimeMillis(); - } - - /** - * Intepret a response from the auth server. - * <p> - * Throw an appropriate exception on errors; otherwise, return the response's - * status code. - * - * @return response's HTTP status code. - * @throws FxAccountClientException - */ - public static int validateResponse(HttpResponse response) throws FxAccountClientRemoteException { - final int status = response.getStatusLine().getStatusCode(); - if (status == 200) { - return status; - } - int code; - int errno; - String error; - String message; - String info; - ExtendedJSONObject body; - try { - body = new SyncStorageResponse(response).jsonObjectBody(); - body.throwIfFieldsMissingOrMisTyped(requiredErrorStringFields, String.class); - body.throwIfFieldsMissingOrMisTyped(requiredErrorLongFields, Long.class); - code = body.getLong(JSON_KEY_CODE).intValue(); - errno = body.getLong(JSON_KEY_ERRNO).intValue(); - error = body.getString(JSON_KEY_ERROR); - message = body.getString(JSON_KEY_MESSAGE); - info = body.getString(JSON_KEY_INFO); - } catch (Exception e) { - throw new FxAccountClientMalformedResponseException(response); - } - throw new FxAccountClientRemoteException(response, code, errno, error, message, info, body); - } - - /** - * Don't call this directly. Use <code>unbundleBody</code> instead. - */ - protected void unbundleBytes(byte[] bundleBytes, byte[] respHMACKey, byte[] respXORKey, byte[]... rest) - throws InvalidKeyException, NoSuchAlgorithmException, FxAccountClientException { - if (bundleBytes.length < 32) { - throw new IllegalArgumentException("input bundle must include HMAC"); - } - int len = respXORKey.length; - if (bundleBytes.length != len + 32) { - throw new IllegalArgumentException("input bundle and XOR key with HMAC have different lengths"); - } - int left = len; - for (byte[] array : rest) { - left -= array.length; - } - if (left != 0) { - throw new IllegalArgumentException("XOR key and total output arrays have different lengths"); - } - - byte[] ciphertext = new byte[len]; - byte[] HMAC = new byte[32]; - System.arraycopy(bundleBytes, 0, ciphertext, 0, len); - System.arraycopy(bundleBytes, len, HMAC, 0, 32); - - Mac hmacHasher = HKDF.makeHMACHasher(respHMACKey); - byte[] computedHMAC = hmacHasher.doFinal(ciphertext); - if (!Arrays.equals(computedHMAC, HMAC)) { - throw new FxAccountClientException("Bad message HMAC"); - } - - int offset = 0; - for (byte[] array : rest) { - for (int i = 0; i < array.length; i++) { - array[i] = (byte) (respXORKey[offset + i] ^ ciphertext[offset + i]); - } - offset += array.length; - } - } - - protected void unbundleBody(ExtendedJSONObject body, byte[] requestKey, byte[] ctxInfo, byte[]... rest) throws Exception { - int length = 0; - for (byte[] array : rest) { - length += array.length; - } - - if (body == null) { - throw new FxAccountClientException("body must be non-null"); - } - String bundle = body.getString("bundle"); - if (bundle == null) { - throw new FxAccountClientException("bundle must be a non-null string"); - } - byte[] bundleBytes = Utils.hex2Byte(bundle); - - final byte[] respHMACKey = new byte[32]; - final byte[] respXORKey = new byte[length]; - HKDF.deriveMany(requestKey, new byte[0], ctxInfo, respHMACKey, respXORKey); - unbundleBytes(bundleBytes, respHMACKey, respXORKey, rest); - } - - public void keys(byte[] keyFetchToken, final RequestDelegate<TwoKeys> delegate) { - final byte[] tokenId = new byte[32]; - final byte[] reqHMACKey = new byte[32]; - final byte[] requestKey = new byte[32]; - try { - HKDF.deriveMany(keyFetchToken, new byte[0], FxAccountUtils.KW("keyFetchToken"), tokenId, reqHMACKey, requestKey); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - BaseResource resource; - try { - resource = getBaseResource("account/keys"); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<TwoKeys>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { - byte[] kA = new byte[FxAccountUtils.CRYPTO_KEY_LENGTH_BYTES]; - byte[] wrapkB = new byte[FxAccountUtils.CRYPTO_KEY_LENGTH_BYTES]; - unbundleBody(body, requestKey, FxAccountUtils.KW("account/keys"), kA, wrapkB); - delegate.handleSuccess(new TwoKeys(kA, wrapkB)); - } - }; - resource.get(); - } - - /** - * Thin container for account status response. - */ - public static class AccountStatusResponse { - public final boolean exists; - public AccountStatusResponse(boolean exists) { - this.exists = exists; - } - } - - /** - * Query the account status of an account given a uid. - * - * @param uid to query. - * @param delegate to invoke callbacks. - */ - public void accountStatus(String uid, final RequestDelegate<AccountStatusResponse> delegate) { - final BaseResource resource; - try { - final Map<String, String> params = new HashMap<>(1); - params.put("uid", uid); - resource = getBaseResource("account/status", params); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<AccountStatusResponse>(resource, delegate, ResponseType.JSON_OBJECT) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { - boolean exists = body.getBoolean(JSON_KEY_EXISTS); - delegate.handleSuccess(new AccountStatusResponse(exists)); - } - }; - resource.get(); - } - - /** - * Thin container for recovery email status response. - */ - public static class RecoveryEmailStatusResponse { - public final String email; - public final boolean verified; - public RecoveryEmailStatusResponse(String email, boolean verified) { - this.email = email; - this.verified = verified; - } - } - - /** - * Query the recovery email status of an account given a valid session token. - * <p> - * This API is a little odd: the auth server returns the email and - * verification state of the account that corresponds to the (opaque) session - * token. It might fail if the session token is unknown (or invalid, or - * revoked). - * - * @param sessionToken - * to query. - * @param delegate - * to invoke callbacks. - */ - public void recoveryEmailStatus(byte[] sessionToken, final RequestDelegate<RecoveryEmailStatusResponse> delegate) { - final byte[] tokenId = new byte[32]; - final byte[] reqHMACKey = new byte[32]; - final byte[] requestKey = new byte[32]; - try { - HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - BaseResource resource; - try { - resource = getBaseResource("recovery_email/status"); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<RecoveryEmailStatusResponse>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { - String[] requiredStringFields = new String[] { JSON_KEY_EMAIL }; - body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); - String email = body.getString(JSON_KEY_EMAIL); - Boolean verified = body.getBoolean(JSON_KEY_VERIFIED); - delegate.handleSuccess(new RecoveryEmailStatusResponse(email, verified)); - } - }; - resource.get(); - } - - @SuppressWarnings("unchecked") - public void sign(final byte[] sessionToken, final ExtendedJSONObject publicKey, long durationInMilliseconds, final RequestDelegate<String> delegate) { - final ExtendedJSONObject body = new ExtendedJSONObject(); - body.put("publicKey", publicKey); - body.put("duration", durationInMilliseconds); - - final byte[] tokenId = new byte[32]; - final byte[] reqHMACKey = new byte[32]; - try { - HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - BaseResource resource; - try { - resource = getBaseResource("certificate/sign"); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<String>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { - String cert = body.getString("cert"); - if (cert == null) { - delegate.handleError(new FxAccountClientException("cert must be a non-null string")); - return; - } - delegate.handleSuccess(cert); - } - }; - post(resource, body); - } - - protected static final String[] LOGIN_RESPONSE_REQUIRED_STRING_FIELDS = new String[] { JSON_KEY_UID, JSON_KEY_SESSIONTOKEN }; - protected static final String[] LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS = new String[] { JSON_KEY_UID, JSON_KEY_SESSIONTOKEN, JSON_KEY_KEYFETCHTOKEN, }; - protected static final String[] LOGIN_RESPONSE_REQUIRED_BOOLEAN_FIELDS = new String[] { JSON_KEY_VERIFIED }; - - /** - * Thin container for login response. - * <p> - * The <code>remoteEmail</code> field is the email address as normalized by the - * server, and is <b>not necessarily</b> the email address delivered to the - * <code>login</code> or <code>create</code> call. - */ - public static class LoginResponse { - public final String remoteEmail; - public final String uid; - public final byte[] sessionToken; - public final boolean verified; - public final byte[] keyFetchToken; - - public LoginResponse(String remoteEmail, String uid, boolean verified, byte[] sessionToken, byte[] keyFetchToken) { - this.remoteEmail = remoteEmail; - this.uid = uid; - this.verified = verified; - this.sessionToken = sessionToken; - this.keyFetchToken = keyFetchToken; - } - } - - // Public for testing only; prefer login and loginAndGetKeys (without boolean parameter). - public void login(final byte[] emailUTF8, final byte[] quickStretchedPW, final boolean getKeys, - final Map<String, String> queryParameters, - final RequestDelegate<LoginResponse> delegate) { - final BaseResource resource; - final ExtendedJSONObject body; - try { - final String path = "account/login"; - final Map<String, String> modifiedParameters = new HashMap<>(); - if (queryParameters != null) { - modifiedParameters.putAll(queryParameters); - } - if (getKeys) { - modifiedParameters.put("keys", "true"); - } - resource = getBaseResource(path, modifiedParameters); - body = new FxAccount20LoginDelegate(emailUTF8, quickStretchedPW).getCreateBody(); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<LoginResponse>(resource, delegate, ResponseType.JSON_OBJECT) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { - final String[] requiredStringFields = getKeys ? LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS : LOGIN_RESPONSE_REQUIRED_STRING_FIELDS; - body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); - - final String[] requiredBooleanFields = LOGIN_RESPONSE_REQUIRED_BOOLEAN_FIELDS; - body.throwIfFieldsMissingOrMisTyped(requiredBooleanFields, Boolean.class); - - String uid = body.getString(JSON_KEY_UID); - boolean verified = body.getBoolean(JSON_KEY_VERIFIED); - byte[] sessionToken = Utils.hex2Byte(body.getString(JSON_KEY_SESSIONTOKEN)); - byte[] keyFetchToken = null; - if (getKeys) { - keyFetchToken = Utils.hex2Byte(body.getString(JSON_KEY_KEYFETCHTOKEN)); - } - LoginResponse loginResponse = new LoginResponse(new String(emailUTF8, "UTF-8"), uid, verified, sessionToken, keyFetchToken); - - delegate.handleSuccess(loginResponse); - } - }; - - post(resource, body); - } - - public void createAccount(final byte[] emailUTF8, final byte[] quickStretchedPW, - final boolean getKeys, - final boolean preVerified, - final Map<String, String> queryParameters, - final RequestDelegate<LoginResponse> delegate) { - final BaseResource resource; - final ExtendedJSONObject body; - try { - final String path = "account/create"; - final Map<String, String> modifiedParameters = new HashMap<>(); - if (queryParameters != null) { - modifiedParameters.putAll(queryParameters); - } - if (getKeys) { - modifiedParameters.put("keys", "true"); - } - resource = getBaseResource(path, modifiedParameters); - body = new FxAccount20CreateDelegate(emailUTF8, quickStretchedPW, preVerified).getCreateBody(); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - // This is very similar to login, except verified is not required. - resource.delegate = new ResourceDelegate<LoginResponse>(resource, delegate, ResponseType.JSON_OBJECT) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { - final String[] requiredStringFields = getKeys ? LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS : LOGIN_RESPONSE_REQUIRED_STRING_FIELDS; - body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); - - String uid = body.getString(JSON_KEY_UID); - boolean verified = false; // In production, we're definitely not verified immediately upon creation. - Boolean tempVerified = body.getBoolean(JSON_KEY_VERIFIED); - if (tempVerified != null) { - verified = tempVerified; - } - byte[] sessionToken = Utils.hex2Byte(body.getString(JSON_KEY_SESSIONTOKEN)); - byte[] keyFetchToken = null; - if (getKeys) { - keyFetchToken = Utils.hex2Byte(body.getString(JSON_KEY_KEYFETCHTOKEN)); - } - LoginResponse loginResponse = new LoginResponse(new String(emailUTF8, "UTF-8"), uid, verified, sessionToken, keyFetchToken); - - delegate.handleSuccess(loginResponse); - } - }; - - post(resource, body); - } - - /** - * We want users to be able to enter their email address case-insensitively. - * We stretch the password locally using the email address as a salt, to make - * dictionary attacks more expensive. This means that a client with a - * case-differing email address is unable to produce the correct - * authorization, even though it knows the password. In this case, the server - * returns the email that the account was created with, so that the client can - * re-stretch the password locally with the correct email salt. This version - * of <code>login</code> retries at most one time with a server provided email - * address. - * <p> - * Be aware that consumers will not see the initial error response from the - * server providing an alternate email (if there is one). - * - * @param emailUTF8 - * user entered email address. - * @param stretcher - * delegate to stretch and re-stretch password. - * @param getKeys - * true if a <code>keyFetchToken</code> should be returned (in - * addition to the standard <code>sessionToken</code>). - * @param queryParameters - * @param delegate - * to invoke callbacks. - */ - public void login(final byte[] emailUTF8, final PasswordStretcher stretcher, final boolean getKeys, - final Map<String, String> queryParameters, - final RequestDelegate<LoginResponse> delegate) { - byte[] quickStretchedPW; - try { - FxAccountUtils.pii(LOG_TAG, "Trying user provided email: '" + new String(emailUTF8, "UTF-8") + "'" ); - quickStretchedPW = stretcher.getQuickStretchedPW(emailUTF8); - } catch (Exception e) { - delegate.handleError(e); - return; - } - - this.login(emailUTF8, quickStretchedPW, getKeys, queryParameters, new RequestDelegate<LoginResponse>() { - @Override - public void handleSuccess(LoginResponse result) { - delegate.handleSuccess(result); - } - - @Override - public void handleError(Exception e) { - delegate.handleError(e); - } - - @Override - public void handleFailure(FxAccountClientRemoteException e) { - String alternateEmail = e.body.getString(JSON_KEY_EMAIL); - if (!e.isBadEmailCase() || alternateEmail == null) { - delegate.handleFailure(e); - return; - }; - - Logger.info(LOG_TAG, "Server returned alternate email; retrying login with provided email."); - FxAccountUtils.pii(LOG_TAG, "Trying server provided email: '" + alternateEmail + "'" ); - - try { - // Nota bene: this is not recursive, since we call the fixed password - // signature here, which invokes a non-retrying version. - byte[] alternateEmailUTF8 = alternateEmail.getBytes("UTF-8"); - byte[] alternateQuickStretchedPW = stretcher.getQuickStretchedPW(alternateEmailUTF8); - login(alternateEmailUTF8, alternateQuickStretchedPW, getKeys, queryParameters, delegate); - } catch (Exception innerException) { - delegate.handleError(innerException); - return; - } - } - }); - } - - /** - * Registers a device given a valid session token. - * - * @param sessionToken to query. - * @param delegate to invoke callbacks. - */ - @Override - public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> delegate) { - final byte[] tokenId = new byte[32]; - final byte[] reqHMACKey = new byte[32]; - final byte[] requestKey = new byte[32]; - try { - HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - final BaseResource resource; - final ExtendedJSONObject body; - try { - resource = getBaseResource("account/device"); - body = device.toJson(); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<FxAccountDevice>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - delegate.handleSuccess(FxAccountDevice.fromJson(body)); - } catch (Exception e) { - delegate.handleError(e); - } - } - }; - - post(resource, body); - } - - @Override - public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> delegate) { - final byte[] tokenId = new byte[32]; - final byte[] reqHMACKey = new byte[32]; - final byte[] requestKey = new byte[32]; - try { - HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - final BaseResource resource; - try { - resource = getBaseResource("account/devices"); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<FxAccountDevice[]>(resource, delegate, ResponseType.JSON_ARRAY, tokenId, reqHMACKey) { - @Override - public void handleSuccess(int status, HttpResponse response, JSONArray devicesJson) { - try { - FxAccountDevice[] devices = new FxAccountDevice[devicesJson.size()]; - for (int i = 0; i < devices.length; i++) { - ExtendedJSONObject deviceJson = new ExtendedJSONObject((JSONObject) devicesJson.get(i)); - devices[i] = FxAccountDevice.fromJson(deviceJson); - } - delegate.handleSuccess(devices); - } catch (Exception e) { - delegate.handleError(e); - } - } - }; - - resource.get(); - } - - @Override - public void notifyDevices(@NonNull byte[] sessionToken, @NonNull List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> delegate) { - final byte[] tokenId = new byte[32]; - final byte[] reqHMACKey = new byte[32]; - final byte[] requestKey = new byte[32]; - try { - HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - - final BaseResource resource; - final ExtendedJSONObject body = createNotifyDevicesBody(deviceIds, payload, TTL); - try { - resource = getBaseResource("account/devices/notify"); - } catch (URISyntaxException | UnsupportedEncodingException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<ExtendedJSONObject>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - delegate.handleSuccess(body); - } catch (Exception e) { - delegate.handleError(e); - } - } - }; - - post(resource, body); - } - - @NonNull - @SuppressWarnings("unchecked") - private ExtendedJSONObject createNotifyDevicesBody(@NonNull List<String> deviceIds, ExtendedJSONObject payload, Long TTL) { - final ExtendedJSONObject body = new ExtendedJSONObject(); - final JSONArray to = new JSONArray(); - to.addAll(deviceIds); - body.put("to", to); - if (payload != null) { - body.put("payload", payload); - } - if (TTL != null) { - body.put("TTL", TTL); - } - return body; - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClientException.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClientException.java deleted file mode 100644 index 28ee5630e..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClientException.java +++ /dev/null @@ -1,133 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import org.mozilla.gecko.R; -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.HTTPFailureException; -import org.mozilla.gecko.sync.net.SyncStorageResponse; - -import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.HttpStatus; - -/** - * From <a href="https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md">https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md</a>. - */ -public class FxAccountClientException extends Exception { - private static final long serialVersionUID = 7953459541558266597L; - - public FxAccountClientException(String detailMessage) { - super(detailMessage); - } - - public FxAccountClientException(Exception e) { - super(e); - } - - public static class FxAccountClientRemoteException extends FxAccountClientException { - private static final long serialVersionUID = 2209313149952001097L; - - public final HttpResponse response; - public final long httpStatusCode; - public final long apiErrorNumber; - public final String error; - public final String message; - public final String info; - public final ExtendedJSONObject body; - - public FxAccountClientRemoteException(HttpResponse response, long httpStatusCode, long apiErrorNumber, String error, String message, String info, ExtendedJSONObject body) { - super(new HTTPFailureException(new SyncStorageResponse(response))); - if (body == null) { - throw new IllegalArgumentException("body must not be null"); - } - this.response = response; - this.httpStatusCode = httpStatusCode; - this.apiErrorNumber = apiErrorNumber; - this.error = error; - this.message = message; - this.info = info; - this.body = body; - } - - @Override - public String toString() { - return "<FxAccountClientRemoteException " + this.httpStatusCode + " [" + this.apiErrorNumber + "]: " + this.message + ">"; - } - - public boolean isInvalidAuthentication() { - return httpStatusCode == HttpStatus.SC_UNAUTHORIZED; - } - - public boolean isAccountAlreadyExists() { - return apiErrorNumber == FxAccountRemoteError.ATTEMPT_TO_CREATE_AN_ACCOUNT_THAT_ALREADY_EXISTS; - } - - public boolean isAccountDoesNotExist() { - return apiErrorNumber == FxAccountRemoteError.ATTEMPT_TO_ACCESS_AN_ACCOUNT_THAT_DOES_NOT_EXIST; - } - - public boolean isBadPassword() { - return apiErrorNumber == FxAccountRemoteError.INCORRECT_PASSWORD; - } - - public boolean isUnverified() { - return apiErrorNumber == FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT; - } - - public boolean isUpgradeRequired() { - return - apiErrorNumber == FxAccountRemoteError.ENDPOINT_IS_NO_LONGER_SUPPORTED || - apiErrorNumber == FxAccountRemoteError.INCORRECT_LOGIN_METHOD_FOR_THIS_ACCOUNT || - apiErrorNumber == FxAccountRemoteError.INCORRECT_KEY_RETRIEVAL_METHOD_FOR_THIS_ACCOUNT || - apiErrorNumber == FxAccountRemoteError.INCORRECT_API_VERSION_FOR_THIS_ACCOUNT; - } - - public boolean isTooManyRequests() { - return apiErrorNumber == FxAccountRemoteError.CLIENT_HAS_SENT_TOO_MANY_REQUESTS; - } - - public boolean isServerUnavailable() { - return apiErrorNumber == FxAccountRemoteError.SERVICE_TEMPORARILY_UNAVAILABLE_DUE_TO_HIGH_LOAD; - } - - public boolean isBadEmailCase() { - return apiErrorNumber == FxAccountRemoteError.INCORRECT_EMAIL_CASE; - } - - public boolean isAccountLocked() { - return apiErrorNumber == FxAccountRemoteError.ACCOUNT_LOCKED; - } - - public int getErrorMessageStringResource() { - if (isUpgradeRequired()) { - return R.string.fxaccount_remote_error_UPGRADE_REQUIRED; - } else if (isAccountAlreadyExists()) { - return R.string.fxaccount_remote_error_ATTEMPT_TO_CREATE_AN_ACCOUNT_THAT_ALREADY_EXISTS; - } else if (isAccountDoesNotExist()) { - return R.string.fxaccount_remote_error_ATTEMPT_TO_ACCESS_AN_ACCOUNT_THAT_DOES_NOT_EXIST; - } else if (isBadPassword()) { - return R.string.fxaccount_remote_error_INCORRECT_PASSWORD; - } else if (isUnverified()) { - return R.string.fxaccount_remote_error_ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT; - } else if (isTooManyRequests()) { - return R.string.fxaccount_remote_error_CLIENT_HAS_SENT_TOO_MANY_REQUESTS; - } else if (isServerUnavailable()) { - return R.string.fxaccount_remote_error_SERVICE_TEMPORARILY_UNAVAILABLE_TO_DUE_HIGH_LOAD; - } else if (isAccountLocked()) { - return R.string.fxaccount_remote_error_ACCOUNT_LOCKED; - } else { - return R.string.fxaccount_remote_error_UNKNOWN_ERROR; - } - } - } - - public static class FxAccountClientMalformedResponseException extends FxAccountClientRemoteException { - private static final long serialVersionUID = 2209313149952001098L; - - public FxAccountClientMalformedResponseException(HttpResponse response) { - super(response, 0, FxAccountRemoteError.UNKNOWN_ERROR, "Response malformed", "Response malformed", "Response malformed", new ExtendedJSONObject()); - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountRemoteError.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountRemoteError.java deleted file mode 100644 index 5a89561cb..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountRemoteError.java +++ /dev/null @@ -1,33 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -public interface FxAccountRemoteError { - public static final int ATTEMPT_TO_CREATE_AN_ACCOUNT_THAT_ALREADY_EXISTS = 101; - public static final int ATTEMPT_TO_ACCESS_AN_ACCOUNT_THAT_DOES_NOT_EXIST = 102; - public static final int INCORRECT_PASSWORD = 103; - public static final int ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT = 104; - public static final int INVALID_VERIFICATION_CODE = 105; - public static final int REQUEST_BODY_WAS_NOT_VALID_JSON = 106; - public static final int REQUEST_BODY_CONTAINS_INVALID_PARAMETERS = 107; - public static final int REQUEST_BODY_MISSING_REQUIRED_PARAMETERS = 108; - public static final int INVALID_REQUEST_SIGNATURE = 109; - public static final int INVALID_AUTHENTICATION_TOKEN = 110; - public static final int INVALID_AUTHENTICATION_TIMESTAMP = 111; - public static final int CONTENT_LENGTH_HEADER_WAS_NOT_PROVIDED = 112; - public static final int REQUEST_BODY_TOO_LARGE = 113; - public static final int CLIENT_HAS_SENT_TOO_MANY_REQUESTS = 114; - public static final int INVALID_NONCE_IN_REQUEST_SIGNATURE = 115; - public static final int ENDPOINT_IS_NO_LONGER_SUPPORTED = 116; - public static final int INCORRECT_LOGIN_METHOD_FOR_THIS_ACCOUNT = 117; - public static final int INCORRECT_KEY_RETRIEVAL_METHOD_FOR_THIS_ACCOUNT = 118; - public static final int INCORRECT_API_VERSION_FOR_THIS_ACCOUNT = 119; - public static final int INCORRECT_EMAIL_CASE = 120; - public static final int ACCOUNT_LOCKED = 121; - public static final int UNKNOWN_DEVICE = 123; - public static final int DEVICE_SESSION_CONFLICT = 124; - public static final int SERVICE_TEMPORARILY_UNAVAILABLE_DUE_TO_HIGH_LOAD = 201; - public static final int UNKNOWN_ERROR = 999; -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountUtils.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountUtils.java deleted file mode 100644 index 2d29725a0..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountUtils.java +++ /dev/null @@ -1,217 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.GeneralSecurityException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import org.mozilla.gecko.AppConstants; -import org.mozilla.gecko.R; -import org.mozilla.gecko.background.common.log.Logger; -import org.mozilla.gecko.background.nativecode.NativeCrypto; -import org.mozilla.gecko.sync.Utils; -import org.mozilla.gecko.sync.crypto.HKDF; -import org.mozilla.gecko.sync.crypto.KeyBundle; -import org.mozilla.gecko.sync.crypto.PBKDF2; - -import android.content.Context; - -public class FxAccountUtils { - private static final String LOG_TAG = FxAccountUtils.class.getSimpleName(); - - public static final int SALT_LENGTH_BYTES = 32; - public static final int SALT_LENGTH_HEX = 2 * SALT_LENGTH_BYTES; - - public static final int HASH_LENGTH_BYTES = 16; - public static final int HASH_LENGTH_HEX = 2 * HASH_LENGTH_BYTES; - - public static final int CRYPTO_KEY_LENGTH_BYTES = 32; - public static final int CRYPTO_KEY_LENGTH_HEX = 2 * CRYPTO_KEY_LENGTH_BYTES; - - public static final String KW_VERSION_STRING = "identity.mozilla.com/picl/v1/"; - - public static final int NUMBER_OF_QUICK_STRETCH_ROUNDS = 1000; - - // For extra debugging. Not final so it can be changed from Fennec, or from - // an add-on. - public static boolean LOG_PERSONAL_INFORMATION = false; - - public static void pii(String tag, String message) { - if (FxAccountUtils.LOG_PERSONAL_INFORMATION) { - Logger.info(tag, "$$FxA PII$$: " + message); - } - } - - public static String bytes(String string) throws UnsupportedEncodingException { - return Utils.byte2Hex(string.getBytes("UTF-8")); - } - - public static byte[] KW(String name) throws UnsupportedEncodingException { - return Utils.concatAll( - KW_VERSION_STRING.getBytes("UTF-8"), - name.getBytes("UTF-8")); - } - - public static byte[] KWE(String name, byte[] emailUTF8) throws UnsupportedEncodingException { - return Utils.concatAll( - KW_VERSION_STRING.getBytes("UTF-8"), - name.getBytes("UTF-8"), - ":".getBytes("UTF-8"), - emailUTF8); - } - - /** - * Calculate the SRP verifier <tt>x</tt> value. - */ - public static BigInteger srpVerifierLowercaseX(byte[] emailUTF8, byte[] srpPWBytes, byte[] srpSaltBytes) - throws NoSuchAlgorithmException, UnsupportedEncodingException { - byte[] inner = Utils.sha256(Utils.concatAll(emailUTF8, ":".getBytes("UTF-8"), srpPWBytes)); - byte[] outer = Utils.sha256(Utils.concatAll(srpSaltBytes, inner)); - return new BigInteger(1, outer); - } - - /** - * Calculate the SRP verifier <tt>v</tt> value. - */ - public static BigInteger srpVerifierLowercaseV(byte[] emailUTF8, byte[] srpPWBytes, byte[] srpSaltBytes, BigInteger g, BigInteger N) - throws NoSuchAlgorithmException, UnsupportedEncodingException { - BigInteger x = srpVerifierLowercaseX(emailUTF8, srpPWBytes, srpSaltBytes); - BigInteger v = g.modPow(x, N); - return v; - } - - /** - * Format x modulo N in hexadecimal, using as many characters as N takes (in hexadecimal). - * @param x to format. - * @param N modulus. - * @return x modulo N in hexadecimal. - */ - public static String hexModN(BigInteger x, BigInteger N) { - int byteLength = (N.bitLength() + 7) / 8; - int hexLength = 2 * byteLength; - return Utils.byte2Hex(Utils.hex2Byte((x.mod(N)).toString(16), byteLength), hexLength); - } - - /** - * The first engineering milestone of PICL (Profile-in-the-Cloud) was - * comprised of Sync 1.1 fronted by a Firefox Account. The sync key was - * generated from the Firefox Account password-derived kB value using this - * method. - */ - public static KeyBundle generateSyncKeyBundle(final byte[] kB) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException { - byte[] encryptionKey = new byte[32]; - byte[] hmacKey = new byte[32]; - byte[] derived = HKDF.derive(kB, new byte[0], FxAccountUtils.KW("oldsync"), 2*32); - System.arraycopy(derived, 0*32, encryptionKey, 0, 1*32); - System.arraycopy(derived, 1*32, hmacKey, 0, 1*32); - return new KeyBundle(encryptionKey, hmacKey); - } - - /** - * Firefox Accounts are password authenticated, but clients should not store - * the plain-text password for any amount of time. Equivalent, but slightly - * more secure, is the quickly client-side stretched password. - * <p> - * We separate this since multiple login-time operations want it, and the - * PBKDF2 operation is computationally expensive. - */ - public static byte[] generateQuickStretchedPW(byte[] emailUTF8, byte[] passwordUTF8) throws GeneralSecurityException, UnsupportedEncodingException { - byte[] S = FxAccountUtils.KWE("quickStretch", emailUTF8); - try { - return NativeCrypto.pbkdf2SHA256(passwordUTF8, S, NUMBER_OF_QUICK_STRETCH_ROUNDS, 32); - } catch (final LinkageError e) { - // This will throw UnsatisfiedLinkError (missing mozglue) the first time it is called, and - // ClassNotDefFoundError, for the uninitialized NativeCrypto class, each subsequent time this - // is called; LinkageError is their common ancestor. - Logger.warn(LOG_TAG, "Got throwable stretching password using native pbkdf2SHA256 " + - "implementation; ignoring and using Java implementation.", e); - return PBKDF2.pbkdf2SHA256(passwordUTF8, S, NUMBER_OF_QUICK_STRETCH_ROUNDS, 32); - } - } - - /** - * The password-derived credential used to authenticate to the Firefox Account - * auth server. - */ - public static byte[] generateAuthPW(byte[] quickStretchedPW) throws GeneralSecurityException, UnsupportedEncodingException { - return HKDF.derive(quickStretchedPW, new byte[0], FxAccountUtils.KW("authPW"), 32); - } - - /** - * The password-derived credential used to unwrap keys managed by the Firefox - * Account auth server. - */ - public static byte[] generateUnwrapBKey(byte[] quickStretchedPW) throws GeneralSecurityException, UnsupportedEncodingException { - return HKDF.derive(quickStretchedPW, new byte[0], FxAccountUtils.KW("unwrapBkey"), 32); - } - - public static byte[] unwrapkB(byte[] unwrapkB, byte[] wrapkB) { - if (unwrapkB == null) { - throw new IllegalArgumentException("unwrapkB must not be null"); - } - if (wrapkB == null) { - throw new IllegalArgumentException("wrapkB must not be null"); - } - if (unwrapkB.length != CRYPTO_KEY_LENGTH_BYTES || wrapkB.length != CRYPTO_KEY_LENGTH_BYTES) { - throw new IllegalArgumentException("unwrapkB and wrapkB must be " + CRYPTO_KEY_LENGTH_BYTES + " bytes long"); - } - byte[] kB = new byte[CRYPTO_KEY_LENGTH_BYTES]; - for (int i = 0; i < wrapkB.length; i++) { - kB[i] = (byte) (wrapkB[i] ^ unwrapkB[i]); - } - return kB; - } - - /** - * The token server accepts an X-Client-State header, which is the - * lowercase-hex-encoded first 16 bytes of the SHA-256 hash of the - * bytes of kB. - * @param kB a byte array, expected to be 32 bytes long. - * @return a 32-character string. - * @throws NoSuchAlgorithmException - */ - public static String computeClientState(byte[] kB) throws NoSuchAlgorithmException { - if (kB == null || - kB.length != 32) { - throw new IllegalArgumentException("Unexpected kB."); - } - byte[] sha256 = Utils.sha256(kB); - byte[] truncated = new byte[16]; - System.arraycopy(sha256, 0, truncated, 0, 16); - return Utils.byte2Hex(truncated); // This is automatically lowercase. - } - - /** - * Given an endpoint, calculate the corresponding BrowserID audience. - * <p> - * This is the domain, in web parlance. - * - * @param serverURI endpoint. - * @return BrowserID audience. - * @throws URISyntaxException - */ - public static String getAudienceForURL(String serverURI) throws URISyntaxException { - URI uri = new URI(serverURI); - return new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), null, null, null).toString(); - } - - public static String defaultClientName(Context context) { - String name = AppConstants.MOZ_APP_DISPLAYNAME; // The display name is never translated. - // Change "Firefox Aurora" or similar into "Aurora". - if (name.contains("Aurora")) { - name = "Aurora"; - } else if (name.contains("Beta")) { - name = "Beta"; - } else if (name.contains("Nightly")) { - name = "Nightly"; - } - return context.getResources().getString(R.string.sync_default_client_name, name, android.os.Build.MODEL); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/PasswordStretcher.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/PasswordStretcher.java deleted file mode 100644 index 2debf3c77..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/PasswordStretcher.java +++ /dev/null @@ -1,12 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import java.io.UnsupportedEncodingException; -import java.security.GeneralSecurityException; - -public interface PasswordStretcher { - public byte[] getQuickStretchedPW(byte[] emailUTF8) throws UnsupportedEncodingException, GeneralSecurityException; -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/QuickPasswordStretcher.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/QuickPasswordStretcher.java deleted file mode 100644 index bf4b1bc97..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/QuickPasswordStretcher.java +++ /dev/null @@ -1,35 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import java.io.UnsupportedEncodingException; -import java.security.GeneralSecurityException; -import java.util.HashMap; -import java.util.Map; - -import org.mozilla.gecko.sync.Utils; - -public class QuickPasswordStretcher implements PasswordStretcher { - protected final String password; - protected final Map<String, String> cache = new HashMap<String, String>(); - - public QuickPasswordStretcher(String password) { - this.password = password; - } - - @Override - public synchronized byte[] getQuickStretchedPW(byte[] emailUTF8) throws UnsupportedEncodingException, GeneralSecurityException { - if (emailUTF8 == null) { - throw new IllegalArgumentException("emailUTF8 must not be null"); - } - String key = Utils.byte2Hex(emailUTF8); - if (!cache.containsKey(key)) { - byte[] value = FxAccountUtils.generateQuickStretchedPW(emailUTF8, password.getBytes("UTF-8")); - cache.put(key, Utils.byte2Hex(value)); - return value; - } - return Utils.hex2Byte(cache.get(key)); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/SkewHandler.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/SkewHandler.java deleted file mode 100644 index 9d0ad5e03..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/SkewHandler.java +++ /dev/null @@ -1,111 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; - -import org.mozilla.gecko.background.common.log.Logger; -import org.mozilla.gecko.sync.net.Resource; - -import ch.boye.httpclientandroidlib.Header; -import ch.boye.httpclientandroidlib.HttpHeaders; -import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.impl.cookie.DateParseException; -import ch.boye.httpclientandroidlib.impl.cookie.DateUtils; - -public class SkewHandler { - private static final String LOG_TAG = "SkewHandler"; - protected volatile long skewMillis = 0L; - protected final String hostname; - - private static final HashMap<String, SkewHandler> skewHandlers = new HashMap<String, SkewHandler>(); - - public static SkewHandler getSkewHandlerForResource(final Resource resource) { - return getSkewHandlerForHostname(resource.getHostname()); - } - - public static SkewHandler getSkewHandlerFromEndpointString(final String url) throws URISyntaxException { - if (url == null) { - throw new IllegalArgumentException("url must not be null."); - } - URI u = new URI(url); - return getSkewHandlerForHostname(u.getHost()); - } - - public static synchronized SkewHandler getSkewHandlerForHostname(final String hostname) { - SkewHandler handler = skewHandlers.get(hostname); - if (handler == null) { - handler = new SkewHandler(hostname); - skewHandlers.put(hostname, handler); - } - return handler; - } - - public static synchronized void clearSkewHandlers() { - skewHandlers.clear(); - } - - public SkewHandler(final String hostname) { - this.hostname = hostname; - } - - public boolean updateSkewFromServerMillis(long millis, long now) { - skewMillis = millis - now; - Logger.debug(LOG_TAG, "Updated skew: " + skewMillis + "ms for hostname " + this.hostname); - return true; - } - - public boolean updateSkewFromHTTPDateString(String date, long now) { - try { - final long millis = DateUtils.parseDate(date).getTime(); - return updateSkewFromServerMillis(millis, now); - } catch (DateParseException e) { - Logger.warn(LOG_TAG, "Unexpected: invalid Date header from " + this.hostname); - return false; - } - } - - public boolean updateSkewFromDateHeader(Header header, long now) { - String date = header.getValue(); - if (null == date) { - Logger.warn(LOG_TAG, "Unexpected: null Date header from " + this.hostname); - return false; - } - return updateSkewFromHTTPDateString(date, now); - } - - /** - * Update our tracked skew value to account for the local clock differing from - * the server's. - * - * @param response - * the received HTTP response. - * @param now - * the current time in milliseconds. - * @return true if the skew value was updated, false otherwise. - */ - public boolean updateSkew(HttpResponse response, long now) { - Header header = response.getFirstHeader(HttpHeaders.DATE); - if (null == header) { - Logger.warn(LOG_TAG, "Unexpected: missing Date header from " + this.hostname); - return false; - } - return updateSkewFromDateHeader(header, now); - } - - public long getSkewInMillis() { - return skewMillis; - } - - public long getSkewInSeconds() { - return skewMillis / 1000; - } - - public void resetSkew() { - skewMillis = 0L; - } -}
\ No newline at end of file diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountAbstractClient.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountAbstractClient.java deleted file mode 100644 index 4bdaa6690..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountAbstractClient.java +++ /dev/null @@ -1,224 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa.oauth; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.util.Locale; -import java.util.concurrent.Executor; - -import org.mozilla.gecko.background.fxa.FxAccountClientException; -import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientMalformedResponseException; -import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException; -import org.mozilla.gecko.fxa.FxAccountConstants; -import org.mozilla.gecko.Locales; -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.net.AuthHeaderProvider; -import org.mozilla.gecko.sync.net.BaseResource; -import org.mozilla.gecko.sync.net.BaseResourceDelegate; -import org.mozilla.gecko.sync.net.Resource; -import org.mozilla.gecko.sync.net.SyncResponse; -import org.mozilla.gecko.sync.net.SyncStorageResponse; - -import ch.boye.httpclientandroidlib.HttpEntity; -import ch.boye.httpclientandroidlib.HttpHeaders; -import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.client.ClientProtocolException; -import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase; -import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient; - -public abstract class FxAccountAbstractClient { - protected static final String LOG_TAG = FxAccountAbstractClient.class.getSimpleName(); - - protected static final String ACCEPT_HEADER = "application/json;charset=utf-8"; - protected static final String AUTHORIZATION_RESPONSE_TYPE = "token"; - - public static final String JSON_KEY_ERROR = "error"; - public static final String JSON_KEY_MESSAGE = "message"; - public static final String JSON_KEY_CODE = "code"; - public static final String JSON_KEY_ERRNO = "errno"; - - protected static final String[] requiredErrorStringFields = { JSON_KEY_ERROR, JSON_KEY_MESSAGE }; - protected static final String[] requiredErrorLongFields = { JSON_KEY_CODE, JSON_KEY_ERRNO }; - - /** - * The server's URI. - * <p> - * We assume throughout that this ends with a trailing slash (and guarantee as - * much in the constructor). - */ - protected final String serverURI; - - protected final Executor executor; - - public FxAccountAbstractClient(String serverURI, Executor executor) { - if (serverURI == null) { - throw new IllegalArgumentException("Must provide a server URI."); - } - if (executor == null) { - throw new IllegalArgumentException("Must provide a non-null executor."); - } - this.serverURI = serverURI.endsWith("/") ? serverURI : serverURI + "/"; - if (!this.serverURI.endsWith("/")) { - throw new IllegalArgumentException("Constructed serverURI must end with a trailing slash: " + this.serverURI); - } - this.executor = executor; - } - - /** - * Process a typed value extracted from a successful response (in an - * endpoint-dependent way). - */ - public interface RequestDelegate<T> { - public void handleError(Exception e); - public void handleFailure(FxAccountAbstractClientRemoteException e); - public void handleSuccess(T result); - } - - /** - * Intepret a response from the auth server. - * <p> - * Throw an appropriate exception on errors; otherwise, return the response's - * status code. - * - * @return response's HTTP status code. - * @throws FxAccountClientException - */ - public static int validateResponse(HttpResponse response) throws FxAccountAbstractClientRemoteException { - final int status = response.getStatusLine().getStatusCode(); - if (status == 200) { - return status; - } - int code; - int errno; - String error; - String message; - ExtendedJSONObject body; - try { - body = new SyncStorageResponse(response).jsonObjectBody(); - body.throwIfFieldsMissingOrMisTyped(requiredErrorStringFields, String.class); - body.throwIfFieldsMissingOrMisTyped(requiredErrorLongFields, Long.class); - code = body.getLong(JSON_KEY_CODE).intValue(); - errno = body.getLong(JSON_KEY_ERRNO).intValue(); - error = body.getString(JSON_KEY_ERROR); - message = body.getString(JSON_KEY_MESSAGE); - } catch (Exception e) { - throw new FxAccountAbstractClientMalformedResponseException(response); - } - throw new FxAccountAbstractClientRemoteException(response, code, errno, error, message, body); - } - - protected <T> void invokeHandleError(final RequestDelegate<T> delegate, final Exception e) { - executor.execute(new Runnable() { - @Override - public void run() { - delegate.handleError(e); - } - }); - } - - protected <T> void post(BaseResource resource, final ExtendedJSONObject requestBody, final RequestDelegate<T> delegate) { - try { - if (requestBody == null) { - resource.post((HttpEntity) null); - } else { - resource.post(requestBody); - } - } catch (Exception e) { - invokeHandleError(delegate, e); - return; - } - } - - /** - * Translate resource callbacks into request callbacks invoked on the provided - * executor. - * <p> - * Override <code>handleSuccess</code> to parse the body of the resource - * request and call the request callback. <code>handleSuccess</code> is - * invoked via the executor, so you don't need to delegate further. - */ - protected abstract class ResourceDelegate<T> extends BaseResourceDelegate { - protected abstract void handleSuccess(final int status, HttpResponse response, final ExtendedJSONObject body); - - protected final RequestDelegate<T> delegate; - - /** - * Create a delegate for an un-authenticated resource. - */ - public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate) { - super(resource); - this.delegate = delegate; - } - - @Override - public AuthHeaderProvider getAuthHeaderProvider() { - return super.getAuthHeaderProvider(); - } - - @Override - public String getUserAgent() { - return FxAccountConstants.USER_AGENT; - } - - @Override - public void handleHttpResponse(HttpResponse response) { - try { - final int status = validateResponse(response); - invokeHandleSuccess(status, response); - } catch (FxAccountAbstractClientRemoteException e) { - invokeHandleFailure(e); - } - } - - protected void invokeHandleFailure(final FxAccountAbstractClientRemoteException e) { - executor.execute(new Runnable() { - @Override - public void run() { - delegate.handleFailure(e); - } - }); - } - - protected void invokeHandleSuccess(final int status, final HttpResponse response) { - executor.execute(new Runnable() { - @Override - public void run() { - try { - ExtendedJSONObject body = new SyncResponse(response).jsonObjectBody(); - ResourceDelegate.this.handleSuccess(status, response, body); - } catch (Exception e) { - delegate.handleError(e); - } - } - }); - } - - @Override - public void handleHttpProtocolException(final ClientProtocolException e) { - invokeHandleError(delegate, e); - } - - @Override - public void handleHttpIOException(IOException e) { - invokeHandleError(delegate, e); - } - - @Override - public void handleTransportException(GeneralSecurityException e) { - invokeHandleError(delegate, e); - } - - @Override - public void addHeaders(HttpRequestBase request, DefaultHttpClient client) { - super.addHeaders(request, client); - - // The basics. - final Locale locale = Locale.getDefault(); - request.addHeader(HttpHeaders.ACCEPT_LANGUAGE, Locales.getLanguageTag(locale)); - request.addHeader(HttpHeaders.ACCEPT, ACCEPT_HEADER); - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountAbstractClientException.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountAbstractClientException.java deleted file mode 100644 index 21025af0a..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountAbstractClientException.java +++ /dev/null @@ -1,68 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa.oauth; - -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.HTTPFailureException; -import org.mozilla.gecko.sync.net.SyncStorageResponse; - -import ch.boye.httpclientandroidlib.HttpResponse; -import ch.boye.httpclientandroidlib.HttpStatus; - -/** - * From <a href="https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md">https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md</a>. - */ -public class FxAccountAbstractClientException extends Exception { - private static final long serialVersionUID = 1953459541558266597L; - - public FxAccountAbstractClientException(String detailMessage) { - super(detailMessage); - } - - public FxAccountAbstractClientException(Exception e) { - super(e); - } - - public static class FxAccountAbstractClientRemoteException extends FxAccountAbstractClientException { - private static final long serialVersionUID = 1209313149952001097L; - - public final HttpResponse response; - public final long httpStatusCode; - public final long apiErrorNumber; - public final String error; - public final String message; - public final ExtendedJSONObject body; - - public FxAccountAbstractClientRemoteException(HttpResponse response, long httpStatusCode, long apiErrorNumber, String error, String message, ExtendedJSONObject body) { - super(new HTTPFailureException(new SyncStorageResponse(response))); - if (body == null) { - throw new IllegalArgumentException("body must not be null"); - } - this.response = response; - this.httpStatusCode = httpStatusCode; - this.apiErrorNumber = apiErrorNumber; - this.error = error; - this.message = message; - this.body = body; - } - - @Override - public String toString() { - return "<FxAccountAbstractClientRemoteException " + this.httpStatusCode + " [" + this.apiErrorNumber + "]: " + this.message + ">"; - } - - public boolean isInvalidAuthentication() { - return this.httpStatusCode == HttpStatus.SC_UNAUTHORIZED; - } - } - - public static class FxAccountAbstractClientMalformedResponseException extends FxAccountAbstractClientRemoteException { - private static final long serialVersionUID = 1209313149952001098L; - - public FxAccountAbstractClientMalformedResponseException(HttpResponse response) { - super(response, 0, FxAccountOAuthRemoteError.UNKNOWN_ERROR, "Response malformed", "Response malformed", new ExtendedJSONObject()); - } - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountOAuthClient10.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountOAuthClient10.java deleted file mode 100644 index 4f233695b..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountOAuthClient10.java +++ /dev/null @@ -1,129 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa.oauth; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.Executor; - -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.net.BaseResource; - -import ch.boye.httpclientandroidlib.HttpResponse; - -/** - * Talk to an fxa-oauth-server to get "implicitly granted" OAuth tokens. - * <p> - * To use this client, you will need a pre-allocated fxa-oauth-server - * "client_id" with special "implicit grant" permissions. - * <p> - * This client was written against the API documented at <a href="https://github.com/mozilla/fxa-oauth-server/blob/41538990df9e91158558ae5a8115194383ac3b05/docs/api.md">https://github.com/mozilla/fxa-oauth-server/blob/41538990df9e91158558ae5a8115194383ac3b05/docs/api.md</a>. - */ -public class FxAccountOAuthClient10 extends FxAccountAbstractClient { - protected static final String LOG_TAG = FxAccountOAuthClient10.class.getSimpleName(); - - protected static final String AUTHORIZATION_RESPONSE_TYPE = "token"; - - protected static final String JSON_KEY_ACCESS_TOKEN = "access_token"; - protected static final String JSON_KEY_ASSERTION = "assertion"; - protected static final String JSON_KEY_CLIENT_ID = "client_id"; - protected static final String JSON_KEY_RESPONSE_TYPE = "response_type"; - protected static final String JSON_KEY_SCOPE = "scope"; - protected static final String JSON_KEY_STATE = "state"; - protected static final String JSON_KEY_TOKEN = "token"; - protected static final String JSON_KEY_TOKEN_TYPE = "token_type"; - - // access_token: A string that can be used for authorized requests to service providers. - // scope: A string of space-separated permissions that this token has. May differ from requested scopes, since user can deny permissions. - // token_type: A string representing the token type. Currently will always be "bearer". - protected static final String[] AUTHORIZATION_RESPONSE_REQUIRED_STRING_FIELDS = new String[] { JSON_KEY_ACCESS_TOKEN, JSON_KEY_SCOPE, JSON_KEY_TOKEN_TYPE }; - - public FxAccountOAuthClient10(String serverURI, Executor executor) { - super(serverURI, executor); - } - - /** - * Thin container for an authorization response. - */ - public static class AuthorizationResponse { - public final String access_token; - public final String token_type; - public final String scope; - - public AuthorizationResponse(String access_token, String token_type, String scope) { - this.access_token = access_token; - this.token_type = token_type; - this.scope = scope; - } - } - - public void authorization(String client_id, String assertion, String state, String scope, - RequestDelegate<AuthorizationResponse> delegate) { - final BaseResource resource; - try { - resource = new BaseResource(new URI(serverURI + "authorization")); - } catch (URISyntaxException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<AuthorizationResponse>(resource, delegate) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - body.throwIfFieldsMissingOrMisTyped(AUTHORIZATION_RESPONSE_REQUIRED_STRING_FIELDS, String.class); - String access_token = body.getString(JSON_KEY_ACCESS_TOKEN); - String token_type = body.getString(JSON_KEY_TOKEN_TYPE); - String scope = body.getString(JSON_KEY_SCOPE); - delegate.handleSuccess(new AuthorizationResponse(access_token, token_type, scope)); - return; - } catch (Exception e) { - delegate.handleError(e); - return; - } - } - }; - - final ExtendedJSONObject requestBody = new ExtendedJSONObject(); - requestBody.put(JSON_KEY_RESPONSE_TYPE, AUTHORIZATION_RESPONSE_TYPE); - requestBody.put(JSON_KEY_CLIENT_ID, client_id); - requestBody.put(JSON_KEY_ASSERTION, assertion); - if (scope != null) { - requestBody.put(JSON_KEY_SCOPE, scope); - } - if (state != null) { - requestBody.put(JSON_KEY_STATE, state); - } - - post(resource, requestBody, delegate); - } - - public void deleteToken(final String token, final RequestDelegate<Void> delegate) { - final BaseResource resource; - try { - resource = new BaseResource(new URI(serverURI + "destroy")); - } catch (URISyntaxException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<Void>(resource, delegate) { - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - delegate.handleSuccess(null); - return; - } catch (Exception e) { - delegate.handleError(e); - return; - } - } - }; - - final ExtendedJSONObject requestBody = new ExtendedJSONObject(); - requestBody.put(JSON_KEY_TOKEN, token); - post(resource, requestBody, delegate); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountOAuthRemoteError.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountOAuthRemoteError.java deleted file mode 100644 index d949d316b..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/oauth/FxAccountOAuthRemoteError.java +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa.oauth; - -public interface FxAccountOAuthRemoteError { - public static final int ATTEMPT_TO_CREATE_AN_ACCOUNT_THAT_ALREADY_EXISTS = 101; - public static final int UNKNOWN_CLIENT_ID = 101; - public static final int INCORRECT_CLIENT_SECRET = 102; - public static final int REDIRECT_URI_DOES_NOT_MATCH_REGISTERED_VALUE = 103; - public static final int INVALID_FXA_ASSERTION = 104; - public static final int UNKNOWN_CODE = 105; - public static final int INCORRECT_CODE = 106; - public static final int EXPIRED_CODE = 107; - public static final int INVALID_TOKEN = 108; - public static final int INVALID_REQUEST_PARAMETER = 109; - public static final int UNKNOWN_ERROR = 999; -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/profile/FxAccountProfileClient10.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/profile/FxAccountProfileClient10.java deleted file mode 100644 index cb851a8db..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/profile/FxAccountProfileClient10.java +++ /dev/null @@ -1,59 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.fxa.profile; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.Executor; - -import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient; -import org.mozilla.gecko.sync.ExtendedJSONObject; -import org.mozilla.gecko.sync.net.AuthHeaderProvider; -import org.mozilla.gecko.sync.net.BaseResource; -import org.mozilla.gecko.sync.net.BearerAuthHeaderProvider; - -import ch.boye.httpclientandroidlib.HttpResponse; - - -/** - * Talk to an fxa-profile-server to get profile information like name, age, gender, and avatar image. - * <p> - * This client was written against the API documented at <a href="https://github.com/mozilla/fxa-profile-server/blob/0c065619f5a2e867f813a343b4c67da3fe2c82a4/docs/API.md">https://github.com/mozilla/fxa-profile-server/blob/0c065619f5a2e867f813a343b4c67da3fe2c82a4/docs/API.md</a>. - */ -public class FxAccountProfileClient10 extends FxAccountAbstractClient { - public FxAccountProfileClient10(String serverURI, Executor executor) { - super(serverURI, executor); - } - - public void profile(final String token, RequestDelegate<ExtendedJSONObject> delegate) { - BaseResource resource; - try { - resource = new BaseResource(new URI(serverURI + "profile")); - } catch (URISyntaxException e) { - invokeHandleError(delegate, e); - return; - } - - resource.delegate = new ResourceDelegate<ExtendedJSONObject>(resource, delegate) { - @Override - public AuthHeaderProvider getAuthHeaderProvider() { - return new BearerAuthHeaderProvider(token); - } - - @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - delegate.handleSuccess(body); - return; - } catch (Exception e) { - delegate.handleError(e); - return; - } - } - }; - - resource.get(); - } -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/nativecode/NativeCrypto.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/nativecode/NativeCrypto.java deleted file mode 100644 index 25f0f84d9..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/nativecode/NativeCrypto.java +++ /dev/null @@ -1,60 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.background.nativecode; - -import java.security.GeneralSecurityException; - -import org.mozilla.gecko.annotation.RobocopTarget; -import org.mozilla.gecko.AppConstants; - -import android.util.Log; - -@RobocopTarget -public class NativeCrypto { - static { - try { - System.loadLibrary("mozglue"); - } catch (UnsatisfiedLinkError e) { - Log.wtf("NativeCrypto", "Couldn't load mozglue. Trying /data/app-lib path."); - try { - System.load("/data/app-lib/" + AppConstants.ANDROID_PACKAGE_NAME + "/libmozglue.so"); - } catch (Throwable ee) { - try { - Log.wtf("NativeCrypto", "Couldn't load mozglue: " + ee + ". Trying /data/data path."); - System.load("/data/data/" + AppConstants.ANDROID_PACKAGE_NAME + "/lib/libmozglue.so"); - } catch (UnsatisfiedLinkError eee) { - Log.wtf("NativeCrypto", "Failed every attempt to load mozglue. Giving up."); - throw new RuntimeException("Unable to load mozglue", eee); - } - } - } - } - - /** - * Wrapper to perform PBKDF2-HMAC-SHA-256 in native code. - */ - public native static byte[] pbkdf2SHA256(byte[] password, byte[] salt, int c, int dkLen) - throws GeneralSecurityException; - - /** - * Wrapper to perform SHA-1 in native code. - */ - public native static byte[] sha1(byte[] str); - - /** - * Wrapper to perform SHA-256 init in native code. Returns a SHA-256 context. - */ - public native static byte[] sha256init(); - - /** - * Wrapper to update a SHA-256 context in native code. - */ - public native static void sha256update(byte[] ctx, byte[] str, int len); - - /** - * Wrapper to finalize a SHA-256 context in native code. Returns digest. - */ - public native static byte[] sha256finalize(byte[] ctx); -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/preferences/PreferenceFragment.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/preferences/PreferenceFragment.java deleted file mode 100644 index 5bc5422c8..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/preferences/PreferenceFragment.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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 org.mozilla.gecko.background.preferences; - -import org.mozilla.gecko.R; -import org.mozilla.gecko.util.WeakReferenceHandler; - -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.support.v4.app.Fragment; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnKeyListener; -import android.view.ViewGroup; -import android.widget.ListView; - -public abstract class PreferenceFragment extends Fragment implements PreferenceManagerCompat.OnPreferenceTreeClickListener { - private static final String PREFERENCES_TAG = "android:preferences"; - - private PreferenceManager mPreferenceManager; - private ListView mList; - private boolean mHavePrefs; - private boolean mInitDone; - - /** - * The starting request code given out to preference framework. - */ - private static final int FIRST_REQUEST_CODE = 100; - - private static final int MSG_BIND_PREFERENCES = 1; - - private static class PreferenceFragmentHandler extends WeakReferenceHandler<PreferenceFragment> { - public PreferenceFragmentHandler(final PreferenceFragment that) { - super(that); - } - - @Override - public void handleMessage(Message msg) { - final PreferenceFragment that = mTarget.get(); - if (that == null) { - return; - } - - switch (msg.what) { - - case MSG_BIND_PREFERENCES: - that.bindPreferences(); - break; - } - } - } - - private final Handler mHandler = new PreferenceFragmentHandler(this); - - final private Runnable mRequestFocus = new Runnable() { - @Override - public void run() { - mList.focusableViewAvailable(mList); - } - }; - - /** - * Interface that PreferenceFragment's containing activity should - * implement to be able to process preference items that wish to - * switch to a new fragment. - */ - public interface OnPreferenceStartFragmentCallback { - /** - * Called when the user has clicked on a Preference that has - * a fragment class name associated with it. The implementation - * to should instantiate and switch to an instance of the given - * fragment. - */ - boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref); - } - - @Override - public void onCreate(Bundle paramBundle) { - super.onCreate(paramBundle); - mPreferenceManager = PreferenceManagerCompat.newInstance(getActivity(), FIRST_REQUEST_CODE); - PreferenceManagerCompat.setFragment(mPreferenceManager, this); - } - - @Override - public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, Bundle paramBundle) { - return paramLayoutInflater.inflate(R.layout.fxaccount_preference_list_fragment, paramViewGroup, - false); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (mHavePrefs) { - bindPreferences(); - } - - mInitDone = true; - - if (savedInstanceState != null) { - Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); - if (container != null) { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - preferenceScreen.restoreHierarchyState(container); - } - } - } - } - - @Override - public void onStart() { - super.onStart(); - PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, this); - } - - @Override - public void onStop() { - super.onStop(); - PreferenceManagerCompat.dispatchActivityStop(mPreferenceManager); - PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, null); - } - - @Override - public void onDestroyView() { - mList = null; - mHandler.removeCallbacks(mRequestFocus); - mHandler.removeMessages(MSG_BIND_PREFERENCES); - super.onDestroyView(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - PreferenceManagerCompat.dispatchActivityDestroy(mPreferenceManager); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - Bundle container = new Bundle(); - preferenceScreen.saveHierarchyState(container); - outState.putBundle(PREFERENCES_TAG, container); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - PreferenceManagerCompat.dispatchActivityResult(mPreferenceManager, requestCode, resultCode, data); - } - - /** - * Returns the {@link PreferenceManager} used by this fragment. - * @return The {@link PreferenceManager}. - */ - public PreferenceManager getPreferenceManager() { - return mPreferenceManager; - } - - /** - * Sets the root of the preference hierarchy that this fragment is showing. - * - * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. - */ - public void setPreferenceScreen(PreferenceScreen preferenceScreen) { - if (PreferenceManagerCompat.setPreferences(mPreferenceManager, preferenceScreen) && preferenceScreen != null) { - mHavePrefs = true; - if (mInitDone) { - postBindPreferences(); - } - } - } - - /** - * Gets the root of the preference hierarchy that this fragment is showing. - * - * @return The {@link PreferenceScreen} that is the root of the preference - * hierarchy. - */ - public PreferenceScreen getPreferenceScreen() { - return PreferenceManagerCompat.getPreferenceScreen(mPreferenceManager); - } - - /** - * Adds preferences from activities that match the given {@link Intent}. - * - * @param intent The {@link Intent} to query activities. - */ - public void addPreferencesFromIntent(Intent intent) { - requirePreferenceManager(); - - setPreferenceScreen(PreferenceManagerCompat.inflateFromIntent(mPreferenceManager, intent, getPreferenceScreen())); - } - - /** - * Inflates the given XML resource and adds the preference hierarchy to the current - * preference hierarchy. - * - * @param preferencesResId The XML resource ID to inflate. - */ - public void addPreferencesFromResource(int preferencesResId) { - requirePreferenceManager(); - - setPreferenceScreen(PreferenceManagerCompat.inflateFromResource(mPreferenceManager, getActivity(), - preferencesResId, getPreferenceScreen())); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, - Preference preference) { - //if (preference.getFragment() != null && - if ( - getActivity() instanceof OnPreferenceStartFragmentCallback) { - return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment( - this, preference); - } - return false; - } - - /** - * Finds a {@link Preference} based on its key. - * - * @param key The key of the preference to retrieve. - * @return The {@link Preference} with the key, or null. - * @see PreferenceGroup#findPreference(CharSequence) - */ - public Preference findPreference(CharSequence key) { - if (mPreferenceManager == null) { - return null; - } - return mPreferenceManager.findPreference(key); - } - - private void requirePreferenceManager() { - if (mPreferenceManager == null) { - throw new RuntimeException("This should be called after super.onCreate."); - } - } - - private void postBindPreferences() { - if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; - mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); - } - - private void bindPreferences() { - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - if (preferenceScreen != null) { - preferenceScreen.bind(getListView()); - } - } - - public ListView getListView() { - ensureList(); - return mList; - } - - private void ensureList() { - if (mList != null) { - return; - } - View root = getView(); - if (root == null) { - throw new IllegalStateException("Content view not yet created"); - } - View rawListView = root.findViewById(android.R.id.list); - if (!(rawListView instanceof ListView)) { - throw new RuntimeException( - "Content has view with id attribute 'android.R.id.list' " - + "that is not a ListView class"); - } - mList = (ListView)rawListView; - if (mList == null) { - throw new RuntimeException( - "Your content must have a ListView whose id attribute is " + - "'android.R.id.list'"); - } - mList.setOnKeyListener(mListOnKeyListener); - mHandler.post(mRequestFocus); - } - - private final OnKeyListener mListOnKeyListener = new OnKeyListener() { - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - Object selectedItem = mList.getSelectedItem(); - if (selectedItem instanceof Preference) { - @SuppressWarnings("unused") - View selectedView = mList.getSelectedView(); - //return ((Preference)selectedItem).onKey( - // selectedView, keyCode, event); - return false; - } - return false; - } - - }; -} diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/preferences/PreferenceManagerCompat.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/preferences/PreferenceManagerCompat.java deleted file mode 100644 index 22c62e431..000000000 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/preferences/PreferenceManagerCompat.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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 org.mozilla.gecko.background.preferences; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.preference.Preference; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.util.Log; - -public class PreferenceManagerCompat { - - private static final String TAG = PreferenceManagerCompat.class.getSimpleName(); - - /** - * Interface definition for a callback to be invoked when a {@link Preference} in the hierarchy - * rooted at this {@link PreferenceScreen} is clicked. - */ - interface OnPreferenceTreeClickListener { - /** - * Called when a preference in the tree rooted at this {@link PreferenceScreen} has been - * clicked. - * - * @param preferenceScreen The {@link PreferenceScreen} that the preference is located in. - * @param preference The preference that was clicked. - * - * @return Whether the click was handled. - */ - boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference); - } - - static PreferenceManager newInstance(Activity activity, int firstRequestCode) { - try { - Constructor<PreferenceManager> c = PreferenceManager.class.getDeclaredConstructor(Activity.class, int.class); - c.setAccessible(true); - return c.newInstance(activity, firstRequestCode); - } catch (Exception e) { - Log.w(TAG, "Couldn't call constructor PreferenceManager by reflection", e); - } - return null; - } - - /** - * Sets the owning preference fragment - */ - static void setFragment(PreferenceManager manager, PreferenceFragment fragment) { - // stub - } - - /** - * Sets the callback to be invoked when a {@link Preference} in the hierarchy rooted at this - * {@link PreferenceManager} is clicked. - * - * @param listener The callback to be invoked. - */ - static void setOnPreferenceTreeClickListener(PreferenceManager manager, final OnPreferenceTreeClickListener listener) { - try { - Field onPreferenceTreeClickListener = PreferenceManager.class.getDeclaredField("mOnPreferenceTreeClickListener"); - onPreferenceTreeClickListener.setAccessible(true); - if (listener != null) { - Object proxy = Proxy.newProxyInstance( - onPreferenceTreeClickListener.getType().getClassLoader(), - new Class<?>[] { onPreferenceTreeClickListener.getType() }, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) { - if (method.getName().equals("onPreferenceTreeClick")) { - return listener.onPreferenceTreeClick((PreferenceScreen) args[0], (Preference) args[1]); - } else { - return null; - } - } - }); - onPreferenceTreeClickListener.set(manager, proxy); - } else { - onPreferenceTreeClickListener.set(manager, null); - } - } catch (Exception e) { - Log.w(TAG, "Couldn't set PreferenceManager.mOnPreferenceTreeClickListener by reflection", e); - } - } - - /** - * Inflates a preference hierarchy from the preference hierarchies of {@link Activity Activities} - * that match the given {@link Intent}. An {@link Activity} defines its preference hierarchy with - * meta-data using the {@link #METADATA_KEY_PREFERENCES} key. - * <p/> - * If a preference hierarchy is given, the new preference hierarchies will be merged in. - * - * @param queryIntent The intent to match activities. - * @param rootPreferences Optional existing hierarchy to merge the new hierarchies into. - * - * @return The root hierarchy (if one was not provided, the new hierarchy's root). - */ - static PreferenceScreen inflateFromIntent(PreferenceManager manager, Intent intent, PreferenceScreen screen) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("inflateFromIntent", Intent.class, PreferenceScreen.class); - m.setAccessible(true); - PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(manager, intent, screen); - return prefScreen; - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.inflateFromIntent by reflection", e); - } - return null; - } - - /** - * Inflates a preference hierarchy from XML. If a preference hierarchy is given, the new - * preference hierarchies will be merged in. - * - * @param context The context of the resource. - * @param resId The resource ID of the XML to inflate. - * @param rootPreferences Optional existing hierarchy to merge the new hierarchies into. - * - * @return The root hierarchy (if one was not provided, the new hierarchy's root). - * - * @hide - */ - static PreferenceScreen inflateFromResource(PreferenceManager manager, Activity activity, int resId, PreferenceScreen screen) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("inflateFromResource", Context.class, int.class, PreferenceScreen.class); - m.setAccessible(true); - PreferenceScreen prefScreen = (PreferenceScreen) m.invoke(manager, activity, resId, screen); - return prefScreen; - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.inflateFromResource by reflection", e); - } - return null; - } - - /** - * Returns the root of the preference hierarchy managed by this class. - * - * @return The {@link PreferenceScreen} object that is at the root of the hierarchy. - */ - static PreferenceScreen getPreferenceScreen(PreferenceManager manager) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("getPreferenceScreen"); - m.setAccessible(true); - return (PreferenceScreen) m.invoke(manager); - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.getPreferenceScreen by reflection", e); - } - return null; - } - - /** - * Called by the {@link PreferenceManager} to dispatch a subactivity result. - */ - static void dispatchActivityResult(PreferenceManager manager, int requestCode, int resultCode, Intent data) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityResult", int.class, int.class, Intent.class); - m.setAccessible(true); - m.invoke(manager, requestCode, resultCode, data); - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityResult by reflection", e); - } - } - - /** - * Called by the {@link PreferenceManager} to dispatch the activity stop event. - */ - static void dispatchActivityStop(PreferenceManager manager) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityStop"); - m.setAccessible(true); - m.invoke(manager); - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityStop by reflection", e); - } - } - - /** - * Called by the {@link PreferenceManager} to dispatch the activity destroy event. - */ - static void dispatchActivityDestroy(PreferenceManager manager) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("dispatchActivityDestroy"); - m.setAccessible(true); - m.invoke(manager); - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.dispatchActivityDestroy by reflection", e); - } - } - - /** - * Sets the root of the preference hierarchy. - * - * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. - * - * @return Whether the {@link PreferenceScreen} given is different than the previous. - */ - static boolean setPreferences(PreferenceManager manager, PreferenceScreen screen) { - try { - Method m = PreferenceManager.class.getDeclaredMethod("setPreferences", PreferenceScreen.class); - m.setAccessible(true); - return ((Boolean) m.invoke(manager, screen)); - } catch (Exception e) { - Log.w(TAG, "Couldn't call PreferenceManager.setPreferences by reflection", e); - } - return false; - } - -} |