Bug 742109 - Make Android Sync write to Android log with single tag "FxSync". r=rnewman,liuche
authorNick Alexander <nalexander@mozilla.com>
Tue, 12 Jun 2012 12:12:43 -0700
changeset 96510 d514aa633475abf84ebae0be6b3ef0ecbd4a3014
parent 96509 6bbc085e39b5113e4da8a21fa3f892c57a9c1ac8
child 96511 8d857c53bc0aebb3d8320da8aa9bcfe1c5027ea7
push id22911
push useremorley@mozilla.com
push dateWed, 13 Jun 2012 12:49:30 +0000
treeherdermozilla-central@efbb6480e98e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman, liuche
bugs742109
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 742109 - Make Android Sync write to Android log with single tag "FxSync". r=rnewman,liuche
mobile/android/base/android-sync-files.mk
mobile/android/base/sync/GlobalSession.java
mobile/android/base/sync/InfoCollections.java
mobile/android/base/sync/Logger.java
mobile/android/base/sync/StubActivity.java
mobile/android/base/sync/jpake/JPakeCrypto.java
mobile/android/base/sync/jpake/JPakeRequest.java
mobile/android/base/sync/log/writers/AndroidLevelCachingLogWriter.java
mobile/android/base/sync/log/writers/AndroidLogWriter.java
mobile/android/base/sync/log/writers/LevelFilteringLogWriter.java
mobile/android/base/sync/log/writers/LogWriter.java
mobile/android/base/sync/log/writers/PrintLogWriter.java
mobile/android/base/sync/log/writers/SingleTagLogWriter.java
mobile/android/base/sync/log/writers/StringLogWriter.java
mobile/android/base/sync/net/CompletedEntity.java
mobile/android/base/sync/net/SyncResponse.java
mobile/android/base/sync/net/TLSSocketFactory.java
mobile/android/base/sync/repositories/RepositorySessionBundle.java
mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java
mobile/android/base/sync/repositories/android/RepoUtils.java
mobile/android/base/sync/repositories/domain/BookmarkRecord.java
mobile/android/base/sync/repositories/domain/HistoryRecord.java
mobile/android/base/sync/setup/SyncAccounts.java
mobile/android/base/sync/setup/auth/AccountAuthenticator.java
mobile/android/base/sync/setup/auth/EnsureUserExistenceStage.java
mobile/android/base/sync/stage/EnsureClusterURLStage.java
mobile/android/base/sync/stage/FetchInfoCollectionsStage.java
mobile/android/base/sync/syncadapter/SyncAdapter.java
mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java
mobile/android/base/sync/synchronizer/SerialRecordConsumer.java
mobile/android/base/sync/synchronizer/Synchronizer.java
mobile/android/sync/java-sources.mn
--- a/mobile/android/base/android-sync-files.mk
+++ b/mobile/android/base/android-sync-files.mk
@@ -1,14 +1,14 @@
 # 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/.
 
 # These files are managed in the android-sync repo. Do not modify directly, or your changes will be lost.
-SYNC_JAVA_FILES := sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/crypto/PersistedCrypto5Keys.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/EngineSettings.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/DeleteChannel.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/ConnectionMonitorThread.java sync/net/HandleProgressException.java sync/net/HttpResponseObserver.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NodeAuthenticationException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/NullClusterURLException.java sync/PersistedMetaGlobal.java sync/PrefsSource.java sync/receivers/UpgradeReceiver.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BookmarksDeletionManager.java sync/repositories/android/BookmarksInsertionManager.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/FennecControlHelper.java sync/repositories/android/FennecTabsRepository.java sync/repositories/android/FormHistoryRepositorySession.java sync/repositories/android/PasswordsRepositorySession.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/FormHistoryRecord.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/domain/VersionConstants.java sync/repositories/FetchFailedException.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoContentProviderException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreFailedException.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/Server11PreviousPostFailedException.java sync/Server11RecordPostFailedException.java sync/setup/activities/AccountActivity.java sync/setup/activities/ActivityUtils.java sync/setup/activities/ClientRecordArrayAdapter.java sync/setup/activities/RedirectToSetupActivity.java sync/setup/activities/SendTabActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/auth/AccountAuthenticator.java sync/setup/auth/AuthenticateAccountStage.java sync/setup/auth/AuthenticationResult.java sync/setup/auth/AuthenticatorStage.java sync/setup/auth/EnsureUserExistenceStage.java sync/setup/auth/FetchUserNodeStage.java sync/setup/Constants.java sync/setup/InvalidSyncKeyException.java sync/setup/SyncAccounts.java sync/setup/SyncAuthenticatorService.java sync/stage/AbstractNonRepositorySyncStage.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureCrypto5KeysStage.java sync/stage/FennecTabsServerSyncStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/FormHistoryServerSyncStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/PasswordsServerSyncStage.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/stage/UploadMetaGlobalStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/ServerLocalSynchronizer.java sync/synchronizer/ServerLocalSynchronizerSession.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java
+SYNC_JAVA_FILES := sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/crypto/PersistedCrypto5Keys.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/EngineSettings.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/DeleteChannel.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/log/writers/AndroidLevelCachingLogWriter.java sync/log/writers/AndroidLogWriter.java sync/log/writers/LevelFilteringLogWriter.java sync/log/writers/LogWriter.java sync/log/writers/PrintLogWriter.java sync/log/writers/SingleTagLogWriter.java sync/log/writers/StringLogWriter.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/ConnectionMonitorThread.java sync/net/HandleProgressException.java sync/net/HttpResponseObserver.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NodeAuthenticationException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/NullClusterURLException.java sync/PersistedMetaGlobal.java sync/PrefsSource.java sync/receivers/UpgradeReceiver.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BookmarksDeletionManager.java sync/repositories/android/BookmarksInsertionManager.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/FennecControlHelper.java sync/repositories/android/FennecTabsRepository.java sync/repositories/android/FormHistoryRepositorySession.java sync/repositories/android/PasswordsRepositorySession.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/FormHistoryRecord.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/domain/VersionConstants.java sync/repositories/FetchFailedException.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoContentProviderException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreFailedException.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/Server11PreviousPostFailedException.java sync/Server11RecordPostFailedException.java sync/setup/activities/AccountActivity.java sync/setup/activities/ActivityUtils.java sync/setup/activities/ClientRecordArrayAdapter.java sync/setup/activities/RedirectToSetupActivity.java sync/setup/activities/SendTabActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/auth/AccountAuthenticator.java sync/setup/auth/AuthenticateAccountStage.java sync/setup/auth/AuthenticationResult.java sync/setup/auth/AuthenticatorStage.java sync/setup/auth/EnsureUserExistenceStage.java sync/setup/auth/FetchUserNodeStage.java sync/setup/Constants.java sync/setup/InvalidSyncKeyException.java sync/setup/SyncAccounts.java sync/setup/SyncAuthenticatorService.java sync/stage/AbstractNonRepositorySyncStage.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureCrypto5KeysStage.java sync/stage/FennecTabsServerSyncStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/FormHistoryServerSyncStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/PasswordsServerSyncStage.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/stage/UploadMetaGlobalStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/ServerLocalSynchronizer.java sync/synchronizer/ServerLocalSynchronizerSession.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java
 SYNC_PP_JAVA_FILES := sync/GlobalConstants.java
 SYNC_THIRDPARTY_JAVA_FILES := httpclientandroidlib/androidextra/HttpClientAndroidLog.java httpclientandroidlib/annotation/GuardedBy.java httpclientandroidlib/annotation/Immutable.java httpclientandroidlib/annotation/NotThreadSafe.java httpclientandroidlib/annotation/ThreadSafe.java httpclientandroidlib/auth/AUTH.java httpclientandroidlib/auth/AuthenticationException.java httpclientandroidlib/auth/AuthScheme.java httpclientandroidlib/auth/AuthSchemeFactory.java httpclientandroidlib/auth/AuthSchemeRegistry.java httpclientandroidlib/auth/AuthScope.java httpclientandroidlib/auth/AuthState.java httpclientandroidlib/auth/BasicUserPrincipal.java httpclientandroidlib/auth/ContextAwareAuthScheme.java httpclientandroidlib/auth/Credentials.java httpclientandroidlib/auth/InvalidCredentialsException.java httpclientandroidlib/auth/MalformedChallengeException.java httpclientandroidlib/auth/NTCredentials.java httpclientandroidlib/auth/NTUserPrincipal.java httpclientandroidlib/auth/params/AuthParamBean.java httpclientandroidlib/auth/params/AuthParams.java httpclientandroidlib/auth/params/AuthPNames.java httpclientandroidlib/auth/UsernamePasswordCredentials.java httpclientandroidlib/client/AuthCache.java httpclientandroidlib/client/AuthenticationHandler.java httpclientandroidlib/client/CircularRedirectException.java httpclientandroidlib/client/ClientProtocolException.java httpclientandroidlib/client/CookieStore.java httpclientandroidlib/client/CredentialsProvider.java httpclientandroidlib/client/entity/DecompressingEntity.java httpclientandroidlib/client/entity/DeflateDecompressingEntity.java httpclientandroidlib/client/entity/GzipDecompressingEntity.java httpclientandroidlib/client/entity/UrlEncodedFormEntity.java httpclientandroidlib/client/HttpClient.java httpclientandroidlib/client/HttpRequestRetryHandler.java httpclientandroidlib/client/HttpResponseException.java httpclientandroidlib/client/methods/AbortableHttpRequest.java httpclientandroidlib/client/methods/HttpDelete.java httpclientandroidlib/client/methods/HttpEntityEnclosingRequestBase.java httpclientandroidlib/client/methods/HttpGet.java httpclientandroidlib/client/methods/HttpHead.java httpclientandroidlib/client/methods/HttpOptions.java httpclientandroidlib/client/methods/HttpPost.java httpclientandroidlib/client/methods/HttpPut.java httpclientandroidlib/client/methods/HttpRequestBase.java httpclientandroidlib/client/methods/HttpTrace.java httpclientandroidlib/client/methods/HttpUriRequest.java httpclientandroidlib/client/NonRepeatableRequestException.java httpclientandroidlib/client/params/AllClientPNames.java httpclientandroidlib/client/params/AuthPolicy.java httpclientandroidlib/client/params/ClientParamBean.java httpclientandroidlib/client/params/ClientPNames.java httpclientandroidlib/client/params/CookiePolicy.java httpclientandroidlib/client/params/HttpClientParams.java httpclientandroidlib/client/protocol/ClientContext.java httpclientandroidlib/client/protocol/ClientContextConfigurer.java httpclientandroidlib/client/protocol/RequestAcceptEncoding.java httpclientandroidlib/client/protocol/RequestAddCookies.java httpclientandroidlib/client/protocol/RequestAuthCache.java httpclientandroidlib/client/protocol/RequestClientConnControl.java httpclientandroidlib/client/protocol/RequestDefaultHeaders.java httpclientandroidlib/client/protocol/RequestProxyAuthentication.java httpclientandroidlib/client/protocol/RequestTargetAuthentication.java httpclientandroidlib/client/protocol/ResponseAuthCache.java httpclientandroidlib/client/protocol/ResponseContentEncoding.java httpclientandroidlib/client/protocol/ResponseProcessCookies.java httpclientandroidlib/client/RedirectException.java httpclientandroidlib/client/RedirectHandler.java httpclientandroidlib/client/RedirectStrategy.java httpclientandroidlib/client/RequestDirector.java httpclientandroidlib/client/ResponseHandler.java httpclientandroidlib/client/UserTokenHandler.java httpclientandroidlib/client/utils/CloneUtils.java httpclientandroidlib/client/utils/Idn.java httpclientandroidlib/client/utils/JdkIdn.java httpclientandroidlib/client/utils/Punycode.java httpclientandroidlib/client/utils/Rfc3492Idn.java httpclientandroidlib/client/utils/URIUtils.java httpclientandroidlib/client/utils/URLEncodedUtils.java httpclientandroidlib/conn/BasicEofSensorWatcher.java httpclientandroidlib/conn/BasicManagedEntity.java httpclientandroidlib/conn/ClientConnectionManager.java httpclientandroidlib/conn/ClientConnectionManagerFactory.java httpclientandroidlib/conn/ClientConnectionOperator.java httpclientandroidlib/conn/ClientConnectionRequest.java httpclientandroidlib/conn/ConnectionKeepAliveStrategy.java httpclientandroidlib/conn/ConnectionPoolTimeoutException.java httpclientandroidlib/conn/ConnectionReleaseTrigger.java httpclientandroidlib/conn/ConnectTimeoutException.java httpclientandroidlib/conn/EofSensorInputStream.java httpclientandroidlib/conn/EofSensorWatcher.java httpclientandroidlib/conn/HttpHostConnectException.java httpclientandroidlib/conn/HttpRoutedConnection.java httpclientandroidlib/conn/ManagedClientConnection.java httpclientandroidlib/conn/MultihomePlainSocketFactory.java httpclientandroidlib/conn/OperatedClientConnection.java httpclientandroidlib/conn/params/ConnConnectionParamBean.java httpclientandroidlib/conn/params/ConnConnectionPNames.java httpclientandroidlib/conn/params/ConnManagerParamBean.java httpclientandroidlib/conn/params/ConnManagerParams.java httpclientandroidlib/conn/params/ConnManagerPNames.java httpclientandroidlib/conn/params/ConnPerRoute.java httpclientandroidlib/conn/params/ConnPerRouteBean.java httpclientandroidlib/conn/params/ConnRouteParamBean.java httpclientandroidlib/conn/params/ConnRouteParams.java httpclientandroidlib/conn/params/ConnRoutePNames.java httpclientandroidlib/conn/routing/BasicRouteDirector.java httpclientandroidlib/conn/routing/HttpRoute.java httpclientandroidlib/conn/routing/HttpRouteDirector.java httpclientandroidlib/conn/routing/HttpRoutePlanner.java httpclientandroidlib/conn/routing/RouteInfo.java httpclientandroidlib/conn/routing/RouteTracker.java httpclientandroidlib/conn/scheme/HostNameResolver.java httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactory.java httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactoryAdaptor.java httpclientandroidlib/conn/scheme/LayeredSocketFactory.java httpclientandroidlib/conn/scheme/LayeredSocketFactoryAdaptor.java httpclientandroidlib/conn/scheme/PlainSocketFactory.java httpclientandroidlib/conn/scheme/Scheme.java httpclientandroidlib/conn/scheme/SchemeRegistry.java httpclientandroidlib/conn/scheme/SchemeSocketFactory.java httpclientandroidlib/conn/scheme/SchemeSocketFactoryAdaptor.java httpclientandroidlib/conn/scheme/SocketFactory.java httpclientandroidlib/conn/scheme/SocketFactoryAdaptor.java httpclientandroidlib/conn/ssl/AbstractVerifier.java httpclientandroidlib/conn/ssl/AllowAllHostnameVerifier.java httpclientandroidlib/conn/ssl/BrowserCompatHostnameVerifier.java httpclientandroidlib/conn/ssl/SSLSocketFactory.java httpclientandroidlib/conn/ssl/StrictHostnameVerifier.java httpclientandroidlib/conn/ssl/TrustManagerDecorator.java httpclientandroidlib/conn/ssl/TrustSelfSignedStrategy.java httpclientandroidlib/conn/ssl/TrustStrategy.java httpclientandroidlib/conn/ssl/X509HostnameVerifier.java httpclientandroidlib/conn/util/InetAddressUtils.java httpclientandroidlib/ConnectionClosedException.java httpclientandroidlib/ConnectionReuseStrategy.java httpclientandroidlib/cookie/ClientCookie.java httpclientandroidlib/cookie/Cookie.java httpclientandroidlib/cookie/CookieAttributeHandler.java httpclientandroidlib/cookie/CookieIdentityComparator.java httpclientandroidlib/cookie/CookieOrigin.java httpclientandroidlib/cookie/CookiePathComparator.java httpclientandroidlib/cookie/CookieRestrictionViolationException.java httpclientandroidlib/cookie/CookieSpec.java httpclientandroidlib/cookie/CookieSpecFactory.java httpclientandroidlib/cookie/CookieSpecRegistry.java httpclientandroidlib/cookie/MalformedCookieException.java httpclientandroidlib/cookie/params/CookieSpecParamBean.java httpclientandroidlib/cookie/params/CookieSpecPNames.java httpclientandroidlib/cookie/SetCookie.java httpclientandroidlib/cookie/SetCookie2.java httpclientandroidlib/cookie/SM.java httpclientandroidlib/entity/AbstractHttpEntity.java httpclientandroidlib/entity/BasicHttpEntity.java httpclientandroidlib/entity/BufferedHttpEntity.java httpclientandroidlib/entity/ByteArrayEntity.java httpclientandroidlib/entity/ContentLengthStrategy.java httpclientandroidlib/entity/ContentProducer.java httpclientandroidlib/entity/EntityTemplate.java httpclientandroidlib/entity/FileEntity.java httpclientandroidlib/entity/HttpEntityWrapper.java httpclientandroidlib/entity/InputStreamEntity.java httpclientandroidlib/entity/SerializableEntity.java httpclientandroidlib/entity/StringEntity.java httpclientandroidlib/FormattedHeader.java httpclientandroidlib/Header.java httpclientandroidlib/HeaderElement.java httpclientandroidlib/HeaderElementIterator.java httpclientandroidlib/HeaderIterator.java httpclientandroidlib/HttpClientConnection.java httpclientandroidlib/HttpConnection.java httpclientandroidlib/HttpConnectionMetrics.java httpclientandroidlib/HttpEntity.java httpclientandroidlib/HttpEntityEnclosingRequest.java httpclientandroidlib/HttpException.java httpclientandroidlib/HttpHeaders.java httpclientandroidlib/HttpHost.java httpclientandroidlib/HttpInetConnection.java httpclientandroidlib/HttpMessage.java httpclientandroidlib/HttpRequest.java httpclientandroidlib/HttpRequestFactory.java httpclientandroidlib/HttpRequestInterceptor.java httpclientandroidlib/HttpResponse.java httpclientandroidlib/HttpResponseFactory.java httpclientandroidlib/HttpResponseInterceptor.java httpclientandroidlib/HttpServerConnection.java httpclientandroidlib/HttpStatus.java httpclientandroidlib/HttpVersion.java httpclientandroidlib/impl/AbstractHttpClientConnection.java httpclientandroidlib/impl/AbstractHttpServerConnection.java httpclientandroidlib/impl/auth/AuthSchemeBase.java httpclientandroidlib/impl/auth/BasicScheme.java httpclientandroidlib/impl/auth/BasicSchemeFactory.java httpclientandroidlib/impl/auth/DigestScheme.java httpclientandroidlib/impl/auth/DigestSchemeFactory.java httpclientandroidlib/impl/auth/NTLMEngine.java httpclientandroidlib/impl/auth/NTLMEngineException.java httpclientandroidlib/impl/auth/NTLMEngineImpl.java httpclientandroidlib/impl/auth/NTLMScheme.java httpclientandroidlib/impl/auth/NTLMSchemeFactory.java httpclientandroidlib/impl/auth/RFC2617Scheme.java httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java httpclientandroidlib/impl/client/AbstractAuthenticationHandler.java httpclientandroidlib/impl/client/AbstractHttpClient.java httpclientandroidlib/impl/client/BasicAuthCache.java httpclientandroidlib/impl/client/BasicCookieStore.java httpclientandroidlib/impl/client/BasicCredentialsProvider.java httpclientandroidlib/impl/client/BasicResponseHandler.java httpclientandroidlib/impl/client/ClientParamsStack.java httpclientandroidlib/impl/client/ContentEncodingHttpClient.java httpclientandroidlib/impl/client/DefaultConnectionKeepAliveStrategy.java httpclientandroidlib/impl/client/DefaultHttpClient.java httpclientandroidlib/impl/client/DefaultHttpRequestRetryHandler.java httpclientandroidlib/impl/client/DefaultProxyAuthenticationHandler.java httpclientandroidlib/impl/client/DefaultRedirectHandler.java httpclientandroidlib/impl/client/DefaultRedirectStrategy.java httpclientandroidlib/impl/client/DefaultRedirectStrategyAdaptor.java httpclientandroidlib/impl/client/DefaultRequestDirector.java httpclientandroidlib/impl/client/DefaultTargetAuthenticationHandler.java httpclientandroidlib/impl/client/DefaultUserTokenHandler.java httpclientandroidlib/impl/client/EntityEnclosingRequestWrapper.java httpclientandroidlib/impl/client/RedirectLocations.java httpclientandroidlib/impl/client/RequestWrapper.java httpclientandroidlib/impl/client/RoutedRequest.java httpclientandroidlib/impl/client/TunnelRefusedException.java httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java httpclientandroidlib/impl/conn/AbstractPoolEntry.java httpclientandroidlib/impl/conn/ConnectionShutdownException.java httpclientandroidlib/impl/conn/DefaultClientConnection.java httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java httpclientandroidlib/impl/conn/DefaultResponseParser.java httpclientandroidlib/impl/conn/HttpInetSocketAddress.java httpclientandroidlib/impl/conn/IdleConnectionHandler.java httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java httpclientandroidlib/impl/conn/SchemeRegistryFactory.java httpclientandroidlib/impl/conn/SingleClientConnManager.java httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java httpclientandroidlib/impl/conn/tsccm/RefQueueHandler.java httpclientandroidlib/impl/conn/tsccm/RefQueueWorker.java httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java httpclientandroidlib/impl/conn/tsccm/WaitingThread.java httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java httpclientandroidlib/impl/conn/Wire.java httpclientandroidlib/impl/cookie/AbstractCookieAttributeHandler.java httpclientandroidlib/impl/cookie/AbstractCookieSpec.java httpclientandroidlib/impl/cookie/BasicClientCookie.java httpclientandroidlib/impl/cookie/BasicClientCookie2.java httpclientandroidlib/impl/cookie/BasicCommentHandler.java httpclientandroidlib/impl/cookie/BasicDomainHandler.java httpclientandroidlib/impl/cookie/BasicExpiresHandler.java httpclientandroidlib/impl/cookie/BasicMaxAgeHandler.java httpclientandroidlib/impl/cookie/BasicPathHandler.java httpclientandroidlib/impl/cookie/BasicSecureHandler.java httpclientandroidlib/impl/cookie/BestMatchSpec.java httpclientandroidlib/impl/cookie/BestMatchSpecFactory.java httpclientandroidlib/impl/cookie/BrowserCompatSpec.java httpclientandroidlib/impl/cookie/BrowserCompatSpecFactory.java httpclientandroidlib/impl/cookie/CookieSpecBase.java httpclientandroidlib/impl/cookie/DateParseException.java httpclientandroidlib/impl/cookie/DateUtils.java httpclientandroidlib/impl/cookie/IgnoreSpec.java httpclientandroidlib/impl/cookie/IgnoreSpecFactory.java httpclientandroidlib/impl/cookie/NetscapeDomainHandler.java httpclientandroidlib/impl/cookie/NetscapeDraftHeaderParser.java httpclientandroidlib/impl/cookie/NetscapeDraftSpec.java httpclientandroidlib/impl/cookie/NetscapeDraftSpecFactory.java httpclientandroidlib/impl/cookie/PublicSuffixFilter.java httpclientandroidlib/impl/cookie/PublicSuffixListParser.java httpclientandroidlib/impl/cookie/RFC2109DomainHandler.java httpclientandroidlib/impl/cookie/RFC2109Spec.java httpclientandroidlib/impl/cookie/RFC2109SpecFactory.java httpclientandroidlib/impl/cookie/RFC2109VersionHandler.java httpclientandroidlib/impl/cookie/RFC2965CommentUrlAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965DiscardAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965DomainAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965PortAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965Spec.java httpclientandroidlib/impl/cookie/RFC2965SpecFactory.java httpclientandroidlib/impl/cookie/RFC2965VersionAttributeHandler.java httpclientandroidlib/impl/DefaultConnectionReuseStrategy.java httpclientandroidlib/impl/DefaultHttpClientConnection.java httpclientandroidlib/impl/DefaultHttpRequestFactory.java httpclientandroidlib/impl/DefaultHttpResponseFactory.java httpclientandroidlib/impl/DefaultHttpServerConnection.java httpclientandroidlib/impl/EnglishReasonPhraseCatalog.java httpclientandroidlib/impl/entity/EntityDeserializer.java httpclientandroidlib/impl/entity/EntitySerializer.java httpclientandroidlib/impl/entity/LaxContentLengthStrategy.java httpclientandroidlib/impl/entity/StrictContentLengthStrategy.java httpclientandroidlib/impl/HttpConnectionMetricsImpl.java httpclientandroidlib/impl/io/AbstractMessageParser.java httpclientandroidlib/impl/io/AbstractMessageWriter.java httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java httpclientandroidlib/impl/io/AbstractSessionOutputBuffer.java httpclientandroidlib/impl/io/ChunkedInputStream.java httpclientandroidlib/impl/io/ChunkedOutputStream.java httpclientandroidlib/impl/io/ContentLengthInputStream.java httpclientandroidlib/impl/io/ContentLengthOutputStream.java httpclientandroidlib/impl/io/HttpRequestParser.java httpclientandroidlib/impl/io/HttpRequestWriter.java httpclientandroidlib/impl/io/HttpResponseParser.java httpclientandroidlib/impl/io/HttpResponseWriter.java httpclientandroidlib/impl/io/HttpTransportMetricsImpl.java httpclientandroidlib/impl/io/IdentityInputStream.java httpclientandroidlib/impl/io/IdentityOutputStream.java httpclientandroidlib/impl/io/SocketInputBuffer.java httpclientandroidlib/impl/io/SocketOutputBuffer.java httpclientandroidlib/impl/NoConnectionReuseStrategy.java httpclientandroidlib/impl/SocketHttpClientConnection.java httpclientandroidlib/impl/SocketHttpServerConnection.java httpclientandroidlib/io/BufferInfo.java httpclientandroidlib/io/EofSensor.java httpclientandroidlib/io/HttpMessageParser.java httpclientandroidlib/io/HttpMessageWriter.java httpclientandroidlib/io/HttpTransportMetrics.java httpclientandroidlib/io/SessionInputBuffer.java httpclientandroidlib/io/SessionOutputBuffer.java httpclientandroidlib/MalformedChunkCodingException.java httpclientandroidlib/message/AbstractHttpMessage.java httpclientandroidlib/message/BasicHeader.java httpclientandroidlib/message/BasicHeaderElement.java httpclientandroidlib/message/BasicHeaderElementIterator.java httpclientandroidlib/message/BasicHeaderIterator.java httpclientandroidlib/message/BasicHeaderValueFormatter.java httpclientandroidlib/message/BasicHeaderValueParser.java httpclientandroidlib/message/BasicHttpEntityEnclosingRequest.java httpclientandroidlib/message/BasicHttpRequest.java httpclientandroidlib/message/BasicHttpResponse.java httpclientandroidlib/message/BasicLineFormatter.java httpclientandroidlib/message/BasicLineParser.java httpclientandroidlib/message/BasicListHeaderIterator.java httpclientandroidlib/message/BasicNameValuePair.java httpclientandroidlib/message/BasicRequestLine.java httpclientandroidlib/message/BasicStatusLine.java httpclientandroidlib/message/BasicTokenIterator.java httpclientandroidlib/message/BufferedHeader.java httpclientandroidlib/message/HeaderGroup.java httpclientandroidlib/message/HeaderValueFormatter.java httpclientandroidlib/message/HeaderValueParser.java httpclientandroidlib/message/LineFormatter.java httpclientandroidlib/message/LineParser.java httpclientandroidlib/message/ParserCursor.java httpclientandroidlib/MethodNotSupportedException.java httpclientandroidlib/NameValuePair.java httpclientandroidlib/NoHttpResponseException.java httpclientandroidlib/params/AbstractHttpParams.java httpclientandroidlib/params/BasicHttpParams.java httpclientandroidlib/params/CoreConnectionPNames.java httpclientandroidlib/params/CoreProtocolPNames.java httpclientandroidlib/params/DefaultedHttpParams.java httpclientandroidlib/params/HttpAbstractParamBean.java httpclientandroidlib/params/HttpConnectionParamBean.java httpclientandroidlib/params/HttpConnectionParams.java httpclientandroidlib/params/HttpParams.java httpclientandroidlib/params/HttpProtocolParamBean.java httpclientandroidlib/params/HttpProtocolParams.java httpclientandroidlib/params/SyncBasicHttpParams.java httpclientandroidlib/ParseException.java httpclientandroidlib/protocol/BasicHttpContext.java httpclientandroidlib/protocol/BasicHttpProcessor.java httpclientandroidlib/protocol/DefaultedHttpContext.java httpclientandroidlib/protocol/ExecutionContext.java httpclientandroidlib/protocol/HTTP.java httpclientandroidlib/protocol/HttpContext.java httpclientandroidlib/protocol/HttpDateGenerator.java httpclientandroidlib/protocol/HttpExpectationVerifier.java httpclientandroidlib/protocol/HttpProcessor.java httpclientandroidlib/protocol/HttpRequestExecutor.java httpclientandroidlib/protocol/HttpRequestHandler.java httpclientandroidlib/protocol/HttpRequestHandlerRegistry.java httpclientandroidlib/protocol/HttpRequestHandlerResolver.java httpclientandroidlib/protocol/HttpRequestInterceptorList.java httpclientandroidlib/protocol/HttpResponseInterceptorList.java httpclientandroidlib/protocol/HttpService.java httpclientandroidlib/protocol/ImmutableHttpProcessor.java httpclientandroidlib/protocol/RequestConnControl.java httpclientandroidlib/protocol/RequestContent.java httpclientandroidlib/protocol/RequestDate.java httpclientandroidlib/protocol/RequestExpectContinue.java httpclientandroidlib/protocol/RequestTargetHost.java httpclientandroidlib/protocol/RequestUserAgent.java httpclientandroidlib/protocol/ResponseConnControl.java httpclientandroidlib/protocol/ResponseContent.java httpclientandroidlib/protocol/ResponseDate.java httpclientandroidlib/protocol/ResponseServer.java httpclientandroidlib/protocol/SyncBasicHttpContext.java httpclientandroidlib/protocol/UriPatternMatcher.java httpclientandroidlib/ProtocolException.java httpclientandroidlib/ProtocolVersion.java httpclientandroidlib/ReasonPhraseCatalog.java httpclientandroidlib/RequestLine.java httpclientandroidlib/StatusLine.java httpclientandroidlib/TokenIterator.java httpclientandroidlib/TruncatedChunkException.java httpclientandroidlib/UnsupportedHttpVersionException.java httpclientandroidlib/util/ByteArrayBuffer.java httpclientandroidlib/util/CharArrayBuffer.java httpclientandroidlib/util/EncodingUtils.java httpclientandroidlib/util/EntityUtils.java httpclientandroidlib/util/ExceptionUtils.java httpclientandroidlib/util/LangUtils.java httpclientandroidlib/util/VersionInfo.java json-simple/ItemList.java json-simple/JSONArray.java json-simple/JSONAware.java json-simple/JSONObject.java json-simple/JSONStreamAware.java json-simple/JSONValue.java json-simple/parser/ContainerFactory.java json-simple/parser/ContentHandler.java json-simple/parser/JSONParser.java json-simple/parser/ParseException.java json-simple/parser/Yylex.java json-simple/parser/Yytoken.java apache/commons/codec/binary/Base32.java apache/commons/codec/binary/Base32InputStream.java apache/commons/codec/binary/Base32OutputStream.java apache/commons/codec/binary/Base64.java apache/commons/codec/binary/Base64InputStream.java apache/commons/codec/binary/Base64OutputStream.java apache/commons/codec/binary/BaseNCodec.java apache/commons/codec/binary/BaseNCodecInputStream.java apache/commons/codec/binary/BaseNCodecOutputStream.java apache/commons/codec/binary/BinaryCodec.java apache/commons/codec/binary/Hex.java apache/commons/codec/binary/StringUtils.java apache/commons/codec/BinaryDecoder.java apache/commons/codec/BinaryEncoder.java apache/commons/codec/CharEncoding.java apache/commons/codec/Decoder.java apache/commons/codec/DecoderException.java apache/commons/codec/digest/DigestUtils.java apache/commons/codec/Encoder.java apache/commons/codec/EncoderException.java apache/commons/codec/language/AbstractCaverphone.java apache/commons/codec/language/Caverphone.java apache/commons/codec/language/Caverphone1.java apache/commons/codec/language/Caverphone2.java apache/commons/codec/language/ColognePhonetic.java apache/commons/codec/language/DoubleMetaphone.java apache/commons/codec/language/Metaphone.java apache/commons/codec/language/RefinedSoundex.java apache/commons/codec/language/Soundex.java apache/commons/codec/language/SoundexUtils.java apache/commons/codec/net/BCodec.java apache/commons/codec/net/QCodec.java apache/commons/codec/net/QuotedPrintableCodec.java apache/commons/codec/net/RFC1522Codec.java apache/commons/codec/net/URLCodec.java apache/commons/codec/net/Utils.java apache/commons/codec/StringDecoder.java apache/commons/codec/StringEncoder.java apache/commons/codec/StringEncoderComparator.java
 SYNC_RES_DRAWABLE := mobile/android/base/resources/drawable/desktop.png mobile/android/base/resources/drawable/mobile.png mobile/android/base/resources/drawable/pin_background.xml mobile/android/base/resources/drawable/sync_ic_launcher.png
 SYNC_RES_DRAWABLE_LDPI := mobile/android/base/resources/drawable-ldpi/sync_fx_icon.png mobile/android/base/resources/drawable-ldpi/sync_ic_launcher.png
 SYNC_RES_DRAWABLE_MDPI := mobile/android/base/resources/drawable-mdpi/sync_fx_icon.png mobile/android/base/resources/drawable-mdpi/sync_ic_launcher.png
 SYNC_RES_DRAWABLE_HDPI := mobile/android/base/resources/drawable-hdpi/sync_fx_icon.png mobile/android/base/resources/drawable-hdpi/sync_ic_launcher.png
 SYNC_RES_LAYOUT := res/layout/sync_account.xml res/layout/sync_custom_popup.xml res/layout/sync_list_item.xml res/layout/sync_redirect_to_setup.xml res/layout/sync_send_tab.xml res/layout/sync_setup.xml res/layout/sync_setup_failure.xml res/layout/sync_setup_jpake_waiting.xml res/layout/sync_setup_nointernet.xml res/layout/sync_setup_pair.xml res/layout/sync_setup_success.xml res/layout/sync_stub.xml
 SYNC_RES_VALUES := res/values/sync_styles.xml
--- a/mobile/android/base/sync/GlobalSession.java
+++ b/mobile/android/base/sync/GlobalSession.java
@@ -375,17 +375,17 @@ public class GlobalSession implements Cr
   }
 
   public boolean hasUpdatedMetaGlobal() {
     if (enginesToUpdate.isEmpty()) {
       Logger.info(LOG_TAG, "Not uploading updated meta/global record since there are no engines requesting upload.");
       return false;
     }
 
-    if (Logger.logVerbose(LOG_TAG)) {
+    if (Logger.shouldLogVerbose(LOG_TAG)) {
       Logger.trace(LOG_TAG, "Uploading updated meta/global record since there are engines requesting upload: " +
           Utils.toCommaSeparatedString(enginesToUpdate.keySet()));
     }
 
     return true;
   }
 
   public void updateMetaGlobalInPlace() {
@@ -643,17 +643,17 @@ public class GlobalSession implements Cr
       config.purgeCryptoKeys();
       config.syncID = remoteSyncID;
     }
     // Persist enabled engine names.
     config.enabledEngineNames = global.getEnabledEngineNames();
     if (config.enabledEngineNames == null) {
       Logger.warn(LOG_TAG, "meta/global reported no enabled engine names!");
     } else {
-      if (Logger.logVerbose(LOG_TAG)) {
+      if (Logger.shouldLogVerbose(LOG_TAG)) {
         Logger.trace(LOG_TAG, "Persisting enabled engine names '" +
             Utils.toCommaSeparatedString(config.enabledEngineNames) + "' from meta/global.");
       }
     }
     config.persistToPrefs();
     advance();
   }
 
--- a/mobile/android/base/sync/InfoCollections.java
+++ b/mobile/android/base/sync/InfoCollections.java
@@ -11,18 +11,16 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.delegates.InfoCollectionsDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
 import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 
-import android.util.Log;
-
 public class InfoCollections implements SyncStorageRequestDelegate {
   private static final String LOG_TAG = "InfoCollections";
   protected String infoURL;
   protected String credentials;
 
   /**
    * Fields fetched from the server, or <code>null</code> if not yet fetched.
    * <p>
@@ -103,17 +101,17 @@ public class InfoCollections implements 
         }});
     } catch (Exception e) {
       callback.handleError(e);
     }
   }
 
   @SuppressWarnings("unchecked")
   public void setFromRecord(ExtendedJSONObject record) throws IllegalStateException, IOException, ParseException, NonObjectJSONException {
-    Log.i(LOG_TAG, "info/collections is " + record.toJSONString());
+    Logger.info(LOG_TAG, "info/collections is " + record.toJSONString());
     HashMap<String, Long> map = new HashMap<String, Long>();
 
     Set<Entry<String, Object>> entrySet = record.object.entrySet();
     for (Entry<String, Object> entry : entrySet) {
       // These objects are most likely going to be Doubles. Regardless, we
       // want to get them in a more sane time format.
       String key = entry.getKey();
       Object value = entry.getValue();
@@ -124,17 +122,17 @@ public class InfoCollections implements 
       if (value instanceof Long) {
         map.put(key, Utils.decimalSecondsToMilliseconds((Long) value));
         continue;
       }
       if (value instanceof Integer) {
         map.put(key, Utils.decimalSecondsToMilliseconds((Integer) value));
         continue;
       }
-      Log.w(LOG_TAG, "Skipping info/collections entry for " + key);
+      Logger.warn(LOG_TAG, "Skipping info/collections entry for " + key);
     }
     this.timestamps = map;
   }
 
   // SyncStorageRequestDelegate methods for fetching.
   public String credentials() {
     return this.credentials;
   }
--- a/mobile/android/base/sync/Logger.java
+++ b/mobile/android/base/sync/Logger.java
@@ -1,165 +1,181 @@
 /* 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.sync;
 
-import java.util.IdentityHashMap;
-import java.util.Map;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.mozilla.gecko.sync.log.writers.AndroidLevelCachingLogWriter;
+import org.mozilla.gecko.sync.log.writers.AndroidLogWriter;
+import org.mozilla.gecko.sync.log.writers.LogWriter;
+import org.mozilla.gecko.sync.log.writers.SingleTagLogWriter;
 
 import android.util.Log;
 
 /**
- * Logging helper class. Serializes all log operations (by synchronizing),
- * and caches log level settings.
- *
- * Ultimately this will also be a hook point for our own logging system.
- *
- * @author rnewman
- *
+ * Logging helper class. Serializes all log operations (by synchronizing).
  */
 public class Logger {
+  public static final String LOG_TAG = "Logger";
+
+  public static final String GLOBAL_LOG_TAG = "FxSync";
 
   // For extra debugging.
   public static boolean LOG_PERSONAL_INFORMATION = false;
 
-  // If true, log to System.out as well as using Android's Log.* calls.
-  public static boolean LOG_TO_STDOUT = false;
-
-  // 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 static Map<String, Boolean> isErrorLoggable   = new IdentityHashMap<String, Boolean>();
-  private static Map<String, Boolean> isWarnLoggable    = new IdentityHashMap<String, Boolean>();
-  private static Map<String, Boolean> isInfoLoggable    = new IdentityHashMap<String, Boolean>();
-  private static Map<String, Boolean> isDebugLoggable   = new IdentityHashMap<String, Boolean>();
-  private static Map<String, Boolean> isVerboseLoggable = new IdentityHashMap<String, Boolean>();
+  /**
+   * Current set of writers logged to.
+   * <p>
+   * We want logging to be available while running tests, so we set initialize
+   * this set statically.
+   */
+  protected final static Set<LogWriter> logWriters = new LinkedHashSet<LogWriter>(Logger.defaultLogWriters());
 
   /**
-   * Empty the caches of log levels.
+   * Default set of log writers to log to.
    */
-  public static synchronized 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>();
+  protected final static Set<LogWriter> defaultLogWriters() {
+    final Set<LogWriter> defaultLogWriters = new LinkedHashSet<LogWriter>();
+    LogWriter log = new AndroidLogWriter();
+    LogWriter cache = new AndroidLevelCachingLogWriter(log);
+    LogWriter single = new SingleTagLogWriter(GLOBAL_LOG_TAG, cache);
+    defaultLogWriters.add(single);
+    return defaultLogWriters;
   }
 
-  private static boolean shouldLogError(String logTag) {
-    Boolean out = isErrorLoggable.get(logTag);
-    if (out != null) {
-      return out.booleanValue();
-    }
-    out = Log.isLoggable(logTag, Log.ERROR);
-    isErrorLoggable.put(logTag, out);
-    return out;
+  public static synchronized void startLoggingTo(LogWriter logWriter) {
+    logWriters.add(logWriter);
   }
 
-  private static boolean shouldLogWarn(String logTag) {
-    Boolean out = isWarnLoggable.get(logTag);
-    if (out != null) {
-      return out.booleanValue();
+  public static synchronized void stopLoggingTo(LogWriter logWriter) {
+    try {
+      logWriter.close();
+    } catch (Exception e) {
+      Log.e(LOG_TAG, "Got exception closing and removing LogWriter " + logWriter + ".", e);
     }
-    out = Log.isLoggable(logTag, Log.WARN);
-    isWarnLoggable.put(logTag, out);
-    return out;
+    logWriters.remove(logWriter);
   }
 
-  private static boolean shouldLogInfo(String logTag) {
-    Boolean out = isInfoLoggable.get(logTag);
-    if (out != null) {
-      return out.booleanValue();
+  public static synchronized void stopLoggingToAll() {
+    for (LogWriter logWriter : logWriters) {
+      try {
+        logWriter.close();
+      } catch (Exception e) {
+        Log.e(LOG_TAG, "Got exception closing and removing LogWriter " + logWriter + ".", e);
+      }
     }
-    out = Log.isLoggable(logTag, Log.INFO);
-    isInfoLoggable.put(logTag, out);
-    return out;
+    logWriters.clear();
   }
 
-  private static boolean shouldLogDebug(String logTag) {
-    Boolean out = isDebugLoggable.get(logTag);
-    if (out != null) {
-      return out.booleanValue();
-    }
-    out = Log.isLoggable(logTag, Log.DEBUG);
-    isDebugLoggable.put(logTag, out);
-    return out;
-  }
-
-  private static boolean shouldLogVerbose(String logTag) {
-    Boolean out = isVerboseLoggable.get(logTag);
-    if (out != null) {
-      return out.booleanValue();
-    }
-    out = Log.isLoggable(logTag, Log.VERBOSE);
-    isVerboseLoggable.put(logTag, out);
-    return out;
+  /**
+   * Write to only the default log writers.
+   */
+  public static synchronized void resetLogging() {
+    stopLoggingToAll();
+    logWriters.addAll(Logger.defaultLogWriters());
   }
 
   // Synchronized version for other classes to use.
-  public static synchronized boolean logVerbose(String logTag) {
-    return shouldLogVerbose(logTag);
-  }
-
-  private static void logToStdout(String... s) {
-    if (LOG_TO_STDOUT) {
-      for (String string : s) {
-        System.out.print(string);
+  public static synchronized boolean shouldLogVerbose(String logTag) {
+    for (LogWriter logWriter : logWriters) {
+      if (logWriter.shouldLogVerbose(logTag)) {
+        return true;
       }
-      System.out.println("");
     }
+    return false;
   }
 
   public static void error(String logTag, String message) {
     Logger.error(logTag, message, null);
   }
 
-  public static synchronized void error(String logTag, String message, Throwable error) {
-    logToStdout(logTag, " :: ERROR: ", message);
-    if (shouldLogError(logTag)) {
-      Log.e(logTag, message, error);
-    }
-  }
-
   public static void warn(String logTag, String message) {
     Logger.warn(logTag, message, null);
   }
 
-  public static synchronized void warn(String logTag, String message, Throwable error) {
-    logToStdout(logTag, " :: WARN: ", message);
-    if (shouldLogWarn(logTag)) {
-      Log.w(logTag, message, error);
-    }
-  }
-
-  public static synchronized void info(String logTag, String message) {
-    logToStdout(logTag, " :: INFO: ", message);
-    if (shouldLogInfo(logTag)) {
-      Log.i(logTag, message);
-    }
+  public static void info(String logTag, String message) {
+    Logger.info(logTag, message, null);
   }
 
   public static void debug(String logTag, String message) {
     Logger.debug(logTag, message, null);
   }
 
-  public static synchronized void debug(String logTag, String message, Throwable error) {
-    logToStdout(logTag, " :: DEBUG: ", message);
-    if (shouldLogDebug(logTag)) {
-      Log.d(logTag, message, error);
-    }
-  }
-
-  public static synchronized void trace(String logTag, String message) {
-    logToStdout(logTag, " :: TRACE: ", message);
-    if (shouldLogVerbose(logTag)) {
-      Log.v(logTag, message);
-    }
+  public static void trace(String logTag, String message) {
+    Logger.trace(logTag, message, null);
   }
 
   public static void pii(String logTag, String message) {
     if (LOG_PERSONAL_INFORMATION) {
       Logger.debug(logTag, "$$PII$$: " + message);
     }
   }
+
+  public static synchronized void error(String logTag, String message, Throwable error) {
+    Iterator<LogWriter> it = logWriters.iterator();
+    while (it.hasNext()) {
+      LogWriter writer = it.next();
+      try {
+        writer.error(logTag, message, error);
+      } catch (Exception e) {
+        Log.e(LOG_TAG, "Got exception logging; removing LogWriter " + writer + ".", e);
+        it.remove();
+      }
+    }
+  }
+
+  public static synchronized void warn(String logTag, String message, Throwable error) {
+    Iterator<LogWriter> it = logWriters.iterator();
+    while (it.hasNext()) {
+      LogWriter writer = it.next();
+      try {
+        writer.warn(logTag, message, error);
+      } catch (Exception e) {
+        Log.e(LOG_TAG, "Got exception logging; removing LogWriter " + writer + ".", e);
+        it.remove();
+      }
+    }
+  }
+
+  public static synchronized void info(String logTag, String message, Throwable error) {
+    Iterator<LogWriter> it = logWriters.iterator();
+    while (it.hasNext()) {
+      LogWriter writer = it.next();
+      try {
+        writer.info(logTag, message, error);
+      } catch (Exception e) {
+        Log.e(LOG_TAG, "Got exception logging; removing LogWriter " + writer + ".", e);
+        it.remove();
+      }
+    }
+  }
+
+  public static synchronized void debug(String logTag, String message, Throwable error) {
+    Iterator<LogWriter> it = logWriters.iterator();
+    while (it.hasNext()) {
+      LogWriter writer = it.next();
+      try {
+        writer.debug(logTag, message, error);
+      } catch (Exception e) {
+        Log.e(LOG_TAG, "Got exception logging; removing LogWriter " + writer + ".", e);
+        it.remove();
+      }
+    }
+  }
+
+  public static synchronized void trace(String logTag, String message, Throwable error) {
+    Iterator<LogWriter> it = logWriters.iterator();
+    while (it.hasNext()) {
+      LogWriter writer = it.next();
+      try {
+        writer.trace(logTag, message, error);
+      } catch (Exception e) {
+        Log.e(LOG_TAG, "Got exception logging; removing LogWriter " + writer + ".", e);
+        it.remove();
+      }
+    }
+  }
 }
--- a/mobile/android/base/sync/StubActivity.java
+++ b/mobile/android/base/sync/StubActivity.java
@@ -3,25 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync;
 
 import org.mozilla.gecko.R;
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
 
 /*
  * Activity is just here for testing.
  */
 public class StubActivity extends Activity {
-  private static final String LOG_TAG = "StubActivity";
-
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
-      Log.i(LOG_TAG, "In StubActivity onCreate.");
       super.onCreate(savedInstanceState);
       setContentView(R.layout.sync_stub);
-      Log.i(LOG_TAG, "Done with onCreate.");
   }
 }
--- a/mobile/android/base/sync/jpake/JPakeCrypto.java
+++ b/mobile/android/base/sync/jpake/JPakeCrypto.java
@@ -2,28 +2,27 @@
  * 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.sync.jpake;
 
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.crypto.HKDF;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 
-import android.util.Log;
-import java.security.InvalidKeyException;
-
 public class JPakeCrypto {
   private static final String LOG_TAG = "JPakeCrypto";
 
   /*
    * Primes P and Q, and generator G - from original Mozilla J-PAKE
    * implementation.
    */
   public static final BigInteger P = new BigInteger(
@@ -86,17 +85,17 @@ public class JPakeCrypto {
    * Round 2 of J-PAKE protocol.
    * Generate A and ZKP for A.
    * Verify ZKP from other party. Does not check for replay ZKP.
    */
   public static void round2(BigInteger secretValue, JPakeParty jp, JPakeNumGenerator gen)
       throws IncorrectZkpException, NoSuchAlgorithmException,
       Gx3OrGx4IsZeroOrOneException, UnsupportedEncodingException {
 
-    Log.d(LOG_TAG, "round2 started.");
+    Logger.debug(LOG_TAG, "round2 started.");
 
     // checkZkp does some additional checks, but we can throw a more informative exception here.
     if (BigInteger.ZERO.compareTo(jp.gx3) == 0 || BigInteger.ONE.compareTo(jp.gx3) == 0 ||
         BigInteger.ZERO.compareTo(jp.gx4) == 0 || BigInteger.ONE.compareTo(jp.gx4) == 0) {
       throw new Gx3OrGx4IsZeroOrOneException();
     }
 
     // Check ZKP.
@@ -106,53 +105,53 @@ public class JPakeCrypto {
     // Compute a = g^[(x1+x3+x4)*(x2*secret)].
     BigInteger y1 = jp.gx3.multiply(jp.gx4).mod(P).multiply(jp.gx1).mod(P);
     BigInteger y2 = jp.x2.multiply(secretValue).mod(P);
 
     BigInteger a  = y1.modPow(y2, P);
     jp.thisZkpA = createZkp(y1, y2, a, jp.signerId, gen);
     jp.thisA = a;
 
-    Log.d(LOG_TAG, "round2 finished.");
+    Logger.debug(LOG_TAG, "round2 finished.");
   }
 
   /**
    * Final round of J-PAKE protocol.
    */
   public static KeyBundle finalRound(BigInteger secretValue, JPakeParty jp)
       throws IncorrectZkpException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
-    Log.d(LOG_TAG, "Final round started.");
+    Logger.debug(LOG_TAG, "Final round started.");
     BigInteger gb = jp.gx1.multiply(jp.gx2).mod(P).multiply(jp.gx3)
         .mod(P);
     checkZkp(gb, jp.otherA, jp.otherZkpA);
 
     // Calculate shared key g^(x1+x3)*x2*x4*secret, which is equivalent to
     // (B/g^(x2*x4*s))^x2 = (B*(g^x4)^x2^s^-1)^2.
     BigInteger k = jp.gx4.modPow(jp.x2.multiply(secretValue).negate().mod(Q), P).multiply(jp.otherA)
         .modPow(jp.x2, P);
 
     byte[] enc = new byte[32];
     byte[] hmac = new byte[32];
     generateKeyAndHmac(k, enc, hmac);
 
-    Log.d(LOG_TAG, "Final round finished; returning key.");
+    Logger.debug(LOG_TAG, "Final round finished; returning key.");
     return new KeyBundle(enc, hmac);
   }
 
   // TODO Replace this function with the one in the  crypto library
   private static byte[] HMACSHA256(byte[] data, byte[] key) {
     byte[] result = null;
     try {
       Mac hmacSha256;
       hmacSha256 = Mac.getInstance("HmacSHA256");
       SecretKeySpec secret_key = new SecretKeySpec(key, "HmacSHA256");
       hmacSha256.init(secret_key);
       result = hmacSha256.doFinal(data);
     } catch (GeneralSecurityException e) {
-      Log.d(LOG_TAG, e.toString());
+      Logger.debug(LOG_TAG, e.toString());
     }
     return result;
   }
 
   /* Helper Methods */
 
   /*
    * Generate the ZKP b = r - x*h, and g^r, where h = hash(g, g^r, g^x, id). (We
@@ -163,17 +162,17 @@ public class JPakeCrypto {
     // Generate random r for exponent.
     BigInteger r = gen.generateFromRange(Q);
 
     // Calculate g^r for ZKP.
     BigInteger gr = g.modPow(r, P);
 
     // Calculate the ZKP b value = (r-x*h) % q.
     BigInteger h = computeBHash(g, gr, gx, id);
-    Log.e(LOG_TAG, "myhash: " + h.toString(16));
+    Logger.error(LOG_TAG, "myhash: " + h.toString(16));
 
     // ZKP value = b = r-x*h
     BigInteger b = r.subtract(x.multiply(h)).mod(Q);
 
     return new Zkp(gr, b, id);
   }
 
   /*
@@ -182,41 +181,40 @@ public class JPakeCrypto {
   private static void checkZkp(BigInteger g, BigInteger gx, Zkp zkp)
       throws IncorrectZkpException, NoSuchAlgorithmException, UnsupportedEncodingException {
 
     BigInteger h = computeBHash(g, zkp.gr, gx, zkp.id);
 
     // Check parameters of zkp, and compare to computed hash. These shouldn't
     // fail.
     if (gx.compareTo(BigInteger.ONE) < 1) { // g^x > 1.
-      Log.e(LOG_TAG, "g^x > 1 fails.");
+      Logger.error(LOG_TAG, "g^x > 1 fails.");
       throw new IncorrectZkpException();
     }
     if (gx.compareTo(P.subtract(BigInteger.ONE)) > -1) { // g^x < p-1
-      Log.e(LOG_TAG, "g^x < p-1 fails.");
+      Logger.error(LOG_TAG, "g^x < p-1 fails.");
       throw new IncorrectZkpException();
     }
     if (gx.modPow(Q, P).compareTo(BigInteger.ONE) != 0) {
-      Log.e(LOG_TAG, "g^x^q % p = 1 fails.");
+      Logger.error(LOG_TAG, "g^x^q % p = 1 fails.");
       throw new IncorrectZkpException();
     }
     if (zkp.gr.compareTo(g.modPow(zkp.b, P).multiply(gx.modPow(h, P)).mod(P)) != 0) {
       // b = r-h*x ==> g^r = g^b*g^x^(h)
-      Log.i(LOG_TAG, "gb*g(xh) = "
-          + g.modPow(zkp.b, P).multiply(gx.modPow(h, P)).mod(P).toString(16));
-      Log.d(LOG_TAG, "gr = " + zkp.gr.toString(16));
-      Log.d(LOG_TAG, "b = " + zkp.b.toString(16));
-      Log.d(LOG_TAG, "g^b = " + g.modPow(zkp.b, P).toString(16));
-      Log.d(LOG_TAG, "g^(xh) = " + gx.modPow(h, P).toString(16));
-      Log.d(LOG_TAG, "gx = " + gx.toString(16));
-      Log.d(LOG_TAG, "h = " + h.toString(16));
-      Log.e(LOG_TAG, "zkp calculation incorrect.");
+      Logger.debug(LOG_TAG, "gb*g(xh) = " + g.modPow(zkp.b, P).multiply(gx.modPow(h, P)).mod(P).toString(16));
+      Logger.debug(LOG_TAG, "gr = " + zkp.gr.toString(16));
+      Logger.debug(LOG_TAG, "b = " + zkp.b.toString(16));
+      Logger.debug(LOG_TAG, "g^b = " + g.modPow(zkp.b, P).toString(16));
+      Logger.debug(LOG_TAG, "g^(xh) = " + gx.modPow(h, P).toString(16));
+      Logger.debug(LOG_TAG, "gx = " + gx.toString(16));
+      Logger.debug(LOG_TAG, "h = " + h.toString(16));
+      Logger.error(LOG_TAG, "zkp calculation incorrect.");
       throw new IncorrectZkpException();
     }
-    Log.d(LOG_TAG, "*** ZKP SUCCESS ***");
+    Logger.debug(LOG_TAG, "*** ZKP SUCCESS ***");
   }
 
   /*
    * Use SHA-256 to compute a BigInteger hash of g, gr, gx values with
    * mySignerId to prevent replay. Does not make a twos-complement BigInteger
    * form hash.
    */
   private static BigInteger computeBHash(BigInteger g, BigInteger gr, BigInteger gx,
--- a/mobile/android/base/sync/jpake/JPakeRequest.java
+++ b/mobile/android/base/sync/jpake/JPakeRequest.java
@@ -6,33 +6,29 @@ package org.mozilla.gecko.sync.jpake;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.Resource;
 import org.mozilla.gecko.sync.net.ResourceDelegate;
 
-import android.util.Log;
 import ch.boye.httpclientandroidlib.HttpEntity;
 
 public class JPakeRequest implements Resource {
-  private static String LOG_TAG = "JPakeRequest";
-
   private BaseResource resource;
   public JPakeRequestDelegate delegate;
 
   public JPakeRequest(String uri, ResourceDelegate delegate) throws URISyntaxException {
     this(new URI(uri), delegate);
   }
 
   public JPakeRequest(URI uri, ResourceDelegate delegate) {
     this.resource = new BaseResource(uri);
     this.resource.delegate = delegate;
-    Log.d(LOG_TAG, "new URI: " + uri);
   }
 
   @Override
   public void get() {
     this.resource.get();
   }
 
   @Override
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/AndroidLevelCachingLogWriter.java
@@ -0,0 +1,126 @@
+/* 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.sync.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.booleanValue();
+    }
+    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.booleanValue();
+    }
+    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.booleanValue();
+    }
+    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.booleanValue();
+    }
+    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.booleanValue();
+    }
+    out = Log.isLoggable(logTag, Log.VERBOSE);
+    isVerboseLoggable.put(logTag, out);
+    return out;
+  }
+
+  public void error(String tag, String message, Throwable error) {
+    if (shouldLogError(tag)) {
+      inner.error(tag, message, error);
+    }
+  }
+
+  public void warn(String tag, String message, Throwable error) {
+    if (shouldLogWarn(tag)) {
+      inner.warn(tag, message, error);
+    }
+  }
+
+  public void info(String tag, String message, Throwable error) {
+    if (shouldLogInfo(tag)) {
+      inner.info(tag, message, error);
+    }
+  }
+
+  public void debug(String tag, String message, Throwable error) {
+    if (shouldLogDebug(tag)) {
+      inner.debug(tag, message, error);
+    }
+  }
+
+  public void trace(String tag, String message, Throwable error) {
+    if (shouldLogVerbose(tag)) {
+      inner.trace(tag, message, error);
+    }
+  }
+
+  public void close() {
+    inner.close();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/AndroidLogWriter.java
@@ -0,0 +1,40 @@
+/* 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.sync.log.writers;
+
+import android.util.Log;
+
+/**
+ * Log to the Android log.
+ */
+public class AndroidLogWriter extends LogWriter {
+  @Override
+  public boolean shouldLogVerbose(String logTag) {
+    return true;
+  }
+
+  public void error(String tag, String message, Throwable error) {
+    Log.e(tag, message, error);
+  }
+
+  public void warn(String tag, String message, Throwable error) {
+    Log.w(tag, message, error);
+  }
+
+  public void info(String tag, String message, Throwable error) {
+    Log.i(tag, message, error);
+  }
+
+  public void debug(String tag, String message, Throwable error) {
+    Log.d(tag, message, error);
+  }
+
+  public void trace(String tag, String message, Throwable error) {
+    Log.v(tag, message, error);
+  }
+
+  public void close() {
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/LevelFilteringLogWriter.java
@@ -0,0 +1,66 @@
+/* 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.sync.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;
+  }
+
+  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;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/LogWriter.java
@@ -0,0 +1,29 @@
+/* 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.sync.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);
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/PrintLogWriter.java
@@ -0,0 +1,76 @@
+/* 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.sync.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;
+  }
+
+  public void close() {
+    if (closed) {
+      return;
+    }
+    if (pw != null) {
+      pw.close();
+    }
+    closed = true;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/SingleTagLogWriter.java
@@ -0,0 +1,53 @@
+/* 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.sync.log.writers;
+
+/**
+ * Make a <code>LogWriter</code> only log with a single tag.
+ */
+public class SingleTagLogWriter extends LogWriter {
+  protected final String tag;
+  protected final LogWriter inner;
+
+  public SingleTagLogWriter(String tag, LogWriter inner) {
+    this.tag = tag;
+    this.inner = inner;
+  }
+
+  @Override
+  public void error(String tag, String message, Throwable error) {
+    inner.error(this.tag, tag + " :: " + message, error);
+  }
+
+  @Override
+  public void warn(String tag, String message, Throwable error) {
+    inner.warn(this.tag, tag + " :: " + message, error);
+  }
+
+  @Override
+  public void info(String tag, String message, Throwable error) {
+    inner.info(this.tag, tag + " :: " + message, error);
+  }
+
+  @Override
+  public void debug(String tag, String message, Throwable error) {
+    inner.debug(this.tag, tag + " :: " + message, error);
+  }
+
+  @Override
+  public void trace(String tag, String message, Throwable error) {
+    inner.trace(this.tag, tag + " :: " + message, error);
+  }
+
+  @Override
+  public boolean shouldLogVerbose(String tag) {
+    return inner.shouldLogVerbose(this.tag);
+  }
+
+  @Override
+  public void close() {
+    inner.close();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/log/writers/StringLogWriter.java
@@ -0,0 +1,51 @@
+/* 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.sync.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;
+  }
+
+  public void error(String tag, String message, Throwable error) {
+    inner.error(tag, message, error);
+  }
+
+  public void warn(String tag, String message, Throwable error) {
+    inner.warn(tag, message, error);
+  }
+
+  public void info(String tag, String message, Throwable error) {
+    inner.info(tag, message, error);
+  }
+
+  public void debug(String tag, String message, Throwable error) {
+    inner.debug(tag, message, error);
+  }
+
+  public void trace(String tag, String message, Throwable error) {
+    inner.trace(tag, message, error);
+  }
+
+  public void close() {
+    inner.close();
+  }
+}
deleted file mode 100644
--- a/mobile/android/base/sync/net/CompletedEntity.java
+++ /dev/null
@@ -1,71 +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.sync.net;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import ch.boye.httpclientandroidlib.Header;
-import ch.boye.httpclientandroidlib.HttpEntity;
-
-/**
- * It's an entity without a body.
- * @author rnewman
- *
- */
-public class CompletedEntity implements HttpEntity {
-
-  protected HttpEntity innerEntity;
-  public CompletedEntity(HttpEntity entity) {
-    innerEntity = entity;
-  }
-
-  @Override
-  public void consumeContent() throws IOException {
-    throw new IOException("Already processed.");
-  }
-
-  @Override
-  public InputStream getContent() throws IOException, IllegalStateException {
-    return null;
-  }
-
-  @Override
-  public Header getContentEncoding() {
-    return innerEntity.getContentEncoding();
-  }
-
-  @Override
-  public long getContentLength() {
-    return innerEntity.getContentLength();
-  }
-
-  @Override
-  public Header getContentType() {
-    return innerEntity.getContentType();
-  }
-
-  @Override
-  public boolean isChunked() {
-    return innerEntity.isChunked();
-  }
-
-  @Override
-  public boolean isRepeatable() {
-    return false;
-  }
-
-  @Override
-  public boolean isStreaming() {
-    return false;
-  }
-
-  @Override
-  public void writeTo(OutputStream arg0) throws IOException {
-    throw new IOException("Already processed.");
-  }
-
-}
--- a/mobile/android/base/sync/net/SyncResponse.java
+++ b/mobile/android/base/sync/net/SyncResponse.java
@@ -6,20 +6,20 @@ package org.mozilla.gecko.sync.net;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.Scanner;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.Utils;
 
-import android.util.Log;
 import ch.boye.httpclientandroidlib.Header;
 import ch.boye.httpclientandroidlib.HttpEntity;
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.impl.cookie.DateParseException;
 import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
 
 public class SyncResponse {
   private static final String HEADER_RETRY_AFTER = "retry-after";
@@ -115,17 +115,17 @@ public class SyncResponse {
            value.trim().length() == 0;
   }
 
   private int getIntegerHeader(String h) throws NumberFormatException {
     if (this.hasHeader(h)) {
       Header header = this.response.getFirstHeader(h);
       String value  = header.getValue();
       if (missingHeader(value)) {
-        Log.w(LOG_TAG, h + " header present but empty.");
+        Logger.warn(LOG_TAG, h + " header present but empty.");
         return -1;
       }
       return Integer.parseInt(value, 10);
     }
     return -1;
   }
 
   /**
@@ -134,32 +134,32 @@ public class SyncResponse {
   public int retryAfterInSeconds() throws NumberFormatException {
     if (!this.hasHeader(HEADER_RETRY_AFTER)) {
       return -1;
     }
 
     Header header = this.response.getFirstHeader(HEADER_RETRY_AFTER);
     String retryAfter = header.getValue();
     if (missingHeader(retryAfter)) {
-      Log.w(LOG_TAG, "Retry-After header present but empty.");
+      Logger.warn(LOG_TAG, "Retry-After header present but empty.");
       return -1;
     }
 
     try {
       return Integer.parseInt(retryAfter, 10);
     } catch (NumberFormatException e) {
       // Fall through to try date format.
     }
 
     try {
       final long then = DateUtils.parseDate(retryAfter).getTime();
       final long now  = System.currentTimeMillis();
       return (int)((then - now) / 1000);     // Convert milliseconds to seconds.
     } catch (DateParseException e) {
-      Log.w(LOG_TAG, "Retry-After header neither integer nor date: " + retryAfter);
+      Logger.warn(LOG_TAG, "Retry-After header neither integer nor date: " + retryAfter);
       return -1;
     }
   }
 
   /**
    * @return A number of seconds, or -1 if the 'X-Weave-Backoff' header was not
    *         present.
    */
--- a/mobile/android/base/sync/net/TLSSocketFactory.java
+++ b/mobile/android/base/sync/net/TLSSocketFactory.java
@@ -5,17 +5,17 @@
 package org.mozilla.gecko.sync.net;
 
 import java.io.IOException;
 import java.net.Socket;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocket;
 
-import android.util.Log;
+import org.mozilla.gecko.sync.Logger;
 
 import ch.boye.httpclientandroidlib.conn.ssl.SSLSocketFactory;
 import ch.boye.httpclientandroidlib.params.HttpParams;
 
 public class TLSSocketFactory extends SSLSocketFactory {
   private static final String LOG_TAG = "TLSSocketFactory";
   private static final String[] DEFAULT_CIPHER_SUITES = new String[] {
     "SSL_RSA_WITH_RC4_128_SHA",        // "RC4_SHA"
@@ -47,22 +47,22 @@ public class TLSSocketFactory extends SS
    * @param socket
    *        The SSLSocket on which to operate.
    */
   public static synchronized void setEnabledCipherSuites(SSLSocket socket) {
     try {
       socket.setEnabledCipherSuites(cipherSuites);
     } catch (IllegalArgumentException e) {
       cipherSuites = socket.getSupportedCipherSuites();
-      Log.d(LOG_TAG, "Setting enabled cipher suites failed: " + e.getMessage());
-      Log.d(LOG_TAG, "Using " + cipherSuites.length + " supported suites.");
+      Logger.warn(LOG_TAG, "Setting enabled cipher suites failed: " + e.getMessage());
+      Logger.warn(LOG_TAG, "Using " + cipherSuites.length + " supported suites.");
       socket.setEnabledCipherSuites(cipherSuites);
     }
   }
 
   @Override
   public Socket createSocket(HttpParams params) throws IOException {
     SSLSocket socket = (SSLSocket) super.createSocket(params);
     socket.setEnabledProtocols(DEFAULT_PROTOCOLS);
     setEnabledCipherSuites(socket);
     return socket;
   }
-}
\ No newline at end of file
+}
--- a/mobile/android/base/sync/repositories/RepositorySessionBundle.java
+++ b/mobile/android/base/sync/repositories/RepositorySessionBundle.java
@@ -6,18 +6,16 @@ package org.mozilla.gecko.sync.repositor
 
 import java.io.IOException;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 
-import android.util.Log;
-
 public class RepositorySessionBundle extends ExtendedJSONObject {
 
   private static final String LOG_TAG = "RepositorySessionBundle";
 
   public RepositorySessionBundle() {
     super();
   }
 
@@ -33,17 +31,17 @@ public class RepositorySessionBundle ext
   public long getTimestamp() {
     if (this.containsKey("timestamp")) {
       return this.getLong("timestamp");
     }
     return -1;
   }
 
   public void setTimestamp(long timestamp) {
-    Log.d(LOG_TAG, "Setting timestamp on RepositorySessionBundle to " + timestamp);
+    Logger.debug(LOG_TAG, "Setting timestamp on RepositorySessionBundle to " + timestamp);
     this.put("timestamp", new Long(timestamp));
   }
 
   public void bumpTimestamp(long timestamp) {
     long existing = this.getTimestamp();
     if (timestamp > existing) {
       this.setTimestamp(timestamp);
     } else {
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
@@ -350,17 +350,17 @@ public class AndroidBrowserBookmarksRepo
 
         for (String guid : entry.getValue()) {
           if (!forbiddenGUID(guid)) {
             childArray.add(guid);
           }
         }
       }
 
-      if (Logger.logVerbose(LOG_TAG)) {
+      if (Logger.shouldLogVerbose(LOG_TAG)) {
         // Don't JSON-encode unless we're logging.
         Logger.trace(LOG_TAG, "Output child array: " + childArray.toJSONString());
       }
 
       if (!changed) {
         Logger.debug(LOG_TAG, "Nothing moved! Database reflects child array.");
         return true;
       }
@@ -704,17 +704,17 @@ public class AndroidBrowserBookmarksRepo
    */
   @Override
   protected void fixupRecord(Record record) {
     final BookmarkRecord r = (BookmarkRecord) record;
     final String parentName = SPECIAL_GUIDS_MAP.get(r.parentID);
     if (parentName == null) {
       return;
     }
-    if (Logger.logVerbose(LOG_TAG)) {
+    if (Logger.shouldLogVerbose(LOG_TAG)) {
       Logger.trace(LOG_TAG, "Replacing parent name \"" + r.parentName + "\" with \"" + parentName + "\".");
     }
     r.parentName = parentName;
   }
 
   @Override
   protected Record prepareRecord(Record record) {
     if (record.deleted) {
@@ -810,17 +810,17 @@ public class AndroidBrowserBookmarksRepo
     // Mappings between ID and GUID.
     // TODO: update our persisted children arrays!
     // TODO: if our Android ID just changed, replace parents for all of our children.
     parentGuidToIDMap.put(bmk.guid,      bmk.androidID);
     parentIDToGuidMap.put(bmk.androidID, bmk.guid);
 
     JSONArray childArray = bmk.children;
 
-    if (Logger.logVerbose(LOG_TAG)) {
+    if (Logger.shouldLogVerbose(LOG_TAG)) {
       Logger.trace(LOG_TAG, bmk.guid + " has children " + childArray.toJSONString());
     }
     parentToChildArray.put(bmk.guid, childArray);
 
     // Re-parent.
     if (missingParentToChildren.containsKey(bmk.guid)) {
       for (String child : missingParentToChildren.get(bmk.guid)) {
         // This might return -1; that's OK, the bookmark will
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java
@@ -18,17 +18,16 @@ import org.mozilla.gecko.sync.repositori
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate;
 import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 
 import android.content.Context;
 import android.database.Cursor;
-import android.util.Log;
 
 public class AndroidBrowserHistoryRepositorySession extends AndroidBrowserRepositorySession {
   public static final String LOG_TAG = "ABHistoryRepoSess";
 
   public static final String KEY_DATE = "date";
   public static final String KEY_TYPE = "type";
   public static final long DEFAULT_VISIT_TYPE = 1;
 
@@ -99,17 +98,17 @@ public class AndroidBrowserHistoryReposi
     addVisit(visits, date, DEFAULT_VISIT_TYPE);
   }
 
   private AndroidBrowserHistoryDataExtender getDataExtender() {
     return ((AndroidBrowserHistoryDataAccessor) dbHelper).getHistoryDataExtender();
   }
 
   private Record addVisitsToRecord(Record record) throws NullCursorException {
-    Log.d(LOG_TAG, "Adding visits for GUID " + record.guid);
+    Logger.debug(LOG_TAG, "Adding visits for GUID " + record.guid);
     HistoryRecord hist = (HistoryRecord) record;
     JSONArray visitsArray = getDataExtender().visitsForGUID(hist.guid);
     long missingRecords = hist.fennecVisitCount - visitsArray.size();
 
     // Note that Fennec visit times are milliseconds, and we are working
     // in microseconds. This is the point at which we translate.
 
     // Add (missingRecords - 1) fake visits...
--- a/mobile/android/base/sync/repositories/android/RepoUtils.java
+++ b/mobile/android/base/sync/repositories/android/RepoUtils.java
@@ -209,17 +209,17 @@ public class RepoUtils {
       }
     } catch (Exception e) {
       Logger.debug(LOG_TAG, "Exception logging history record " + rec, e);
     }
     return rec;
   }
 
   public static void logClient(ClientRecord rec) {
-    if (Logger.logVerbose(LOG_TAG)) {
+    if (Logger.shouldLogVerbose(LOG_TAG)) {
       Logger.trace(LOG_TAG, "Returning client record " + rec.guid + " (" + rec.androidID + ")");
       Logger.trace(LOG_TAG, "Client Name:   " + rec.name);
       Logger.trace(LOG_TAG, "Client Type:   " + rec.type);
       Logger.trace(LOG_TAG, "Last Modified: " + rec.lastModified);
       Logger.trace(LOG_TAG, "Deleted:       " + rec.deleted);
     }
   }
 
--- a/mobile/android/base/sync/repositories/domain/BookmarkRecord.java
+++ b/mobile/android/base/sync/repositories/domain/BookmarkRecord.java
@@ -10,18 +10,16 @@ import java.util.Map;
 
 import org.json.simple.JSONArray;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NonArrayJSONException;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.repositories.android.RepoUtils;
 
-import android.util.Log;
-
 /**
  * Covers the fields used by all bookmark objects.
  * @author rnewman
  *
  */
 public class BookmarkRecord extends Record {
   public static final String PLACES_URI_PREFIX = "places:";
 
@@ -175,17 +173,17 @@ public class BookmarkRecord extends Reco
     this.description = payload.getString("description");
     this.parentID    = payload.getString("parentid");
     this.parentName  = payload.getString("parentName");
 
     if (isFolder()) {
       try {
         this.children = payload.getArray("children");
       } catch (NonArrayJSONException e) {
-        Log.e(LOG_TAG, "Got non-array children in bookmark record " + this.guid, e);
+        Logger.error(LOG_TAG, "Got non-array children in bookmark record " + this.guid, e);
         // Let's see if we can recover later by using the parentid pointers.
         this.children = new JSONArray();
       }
       return;
     }
 
     final String bmkUri = payload.getString("bmkUri");
 
--- a/mobile/android/base/sync/repositories/domain/HistoryRecord.java
+++ b/mobile/android/base/sync/repositories/domain/HistoryRecord.java
@@ -141,17 +141,17 @@ public class HistoryRecord extends Recor
     }
     HistoryRecord h = (HistoryRecord) other;
     return this.fennecDateVisited == h.fennecDateVisited &&
            this.fennecVisitCount  == h.fennecVisitCount;
   }
 
   private boolean checkVisitsEquals(HistoryRecord other) {
     Logger.debug(LOG_TAG, "Checking visits.");
-    if (Logger.logVerbose(LOG_TAG)) {
+    if (Logger.shouldLogVerbose(LOG_TAG)) {
       // Don't JSON-encode unless we're logging.
       Logger.trace(LOG_TAG, ">> Mine:   " + ((this.visits == null) ? "null" : this.visits.toJSONString()));
       Logger.trace(LOG_TAG, ">> Theirs: " + ((other.visits == null) ? "null" : other.visits.toJSONString()));
     }
 
     // Handle nulls.
     if (this.visits == other.visits) {
       return true;
--- a/mobile/android/base/sync/setup/SyncAccounts.java
+++ b/mobile/android/base/sync/setup/SyncAccounts.java
@@ -25,17 +25,16 @@ import android.util.Log;
  * and Sync setup activities.
  * <p>
  * Do not break these APIs without correcting upstream code!
  */
 public class SyncAccounts {
 
   public final static String DEFAULT_SERVER = "https://auth.services.mozilla.com/";
   private static final String LOG_TAG = "SyncAccounts";
-  private static final String GLOBAL_LOG_TAG = "FxSync";
 
   /**
    * Returns true if a Sync account is set up.
    * <p>
    * Do not call this method from the main thread.
    */
   public static boolean syncAccountsExist(Context c) {
     return AccountManager.get(c).getAccountsByType(Constants.ACCOUNTTYPE_SYNC).length > 0;
@@ -141,17 +140,17 @@ public class SyncAccounts {
    */
   public static class CreateSyncAccountTask extends AsyncTask<SyncAccountParameters, Void, Account> {
     @Override
     protected Account doInBackground(SyncAccountParameters... params) {
       SyncAccountParameters syncAccount = params[0];
       try {
         return createSyncAccount(syncAccount);
       } catch (Exception e) {
-        Log.e(GLOBAL_LOG_TAG, "Unable to create account.", e);
+        Log.e(Logger.GLOBAL_LOG_TAG, "Unable to create account.", e);
         return null;
       }
     }
   }
 
   /**
    * Create a sync account.
    * <p>
@@ -186,23 +185,23 @@ public class SyncAccounts {
     Logger.debug(LOG_TAG, "Adding account for " + Constants.ACCOUNTTYPE_SYNC);
     boolean result = false;
     try {
       result = accountManager.addAccountExplicitly(account, password, userbundle);
     } catch (SecurityException e) {
       // We use Log rather than Logger here to avoid possibly hiding these errors.
       final String message = e.getMessage();
       if (message != null && (message.indexOf("is different than the authenticator's uid") > 0)) {
-        Log.wtf(GLOBAL_LOG_TAG,
+        Log.wtf(Logger.GLOBAL_LOG_TAG,
                 "Unable to create account. " +
                 "If you have more than one version of " +
                 "Firefox/Beta/Aurora/Nightly/Fennec installed, that's why.",
                 e);
       } else {
-        Log.e(GLOBAL_LOG_TAG, "Unable to create account.", e);
+        Log.e(Logger.GLOBAL_LOG_TAG, "Unable to create account.", e);
       }
     }
 
     if (!result) {
       Logger.error(LOG_TAG, "Failed to add account " + account + "!");
       return null;
     }
     Logger.debug(LOG_TAG, "Account " + account + " added successfully.");
--- a/mobile/android/base/sync/setup/auth/AccountAuthenticator.java
+++ b/mobile/android/base/sync/setup/auth/AccountAuthenticator.java
@@ -1,24 +1,22 @@
 /* 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/. */
+ * 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.sync.setup.auth;
 
 import java.util.LinkedList;
 import java.util.Queue;
 
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.ThreadPool;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.setup.activities.AccountActivity;
 
-import android.util.Log;
-
 public class AccountAuthenticator {
   private final String LOG_TAG = "AccountAuthenticator";
 
   private AccountActivity activityCallback;
   private Queue<AuthenticatorStage> stages;
 
   // Values for authentication.
   public String password;
@@ -54,17 +52,17 @@ public class AccountAuthenticator {
     try {
       username = Utils.usernameFromAccount(account);
     } catch (Exception e) {
       abort(AuthenticationResult.FAILURE_OTHER, e);
       return;
     }
     Logger.debug(LOG_TAG, "username:" + username);
 
-    Log.d(LOG_TAG, "running first stage.");
+    Logger.debug(LOG_TAG, "running first stage.");
     // Start first stage of authentication.
     runNextStage();
   }
 
   /**
    * Run next stage of authentication.
    */
   public void runNextStage() {
--- a/mobile/android/base/sync/setup/auth/EnsureUserExistenceStage.java
+++ b/mobile/android/base/sync/setup/auth/EnsureUserExistenceStage.java
@@ -1,28 +1,27 @@
 /* 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/. */
+ * 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.sync.setup.auth;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
 import java.security.GeneralSecurityException;
 
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.SyncResourceDelegate;
 import org.mozilla.gecko.sync.setup.Constants;
 
-import android.util.Log;
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.client.ClientProtocolException;
 
 public class EnsureUserExistenceStage implements AuthenticatorStage {
   private final String LOG_TAG = "EnsureUserExistence";
 
   public interface EnsureUserExistenceStageDelegate {
     public void handleSuccess();
@@ -32,17 +31,17 @@ public class EnsureUserExistenceStage im
   @Override
   public void execute(final AccountAuthenticator aa) throws URISyntaxException,
       UnsupportedEncodingException {
     final EnsureUserExistenceStageDelegate callbackDelegate = new EnsureUserExistenceStageDelegate() {
 
       @Override
       public void handleSuccess() {
         // User exists; now determine auth node.
-        Log.d(LOG_TAG, "handleSuccess()");
+        Logger.debug(LOG_TAG, "handleSuccess()");
         aa.runNextStage();
       }
 
       @Override
       public void handleFailure(AuthenticationResult result) {
         aa.abort(result, new Exception("Failure in EnsureUser"));
       }
 
--- a/mobile/android/base/sync/stage/EnsureClusterURLStage.java
+++ b/mobile/android/base/sync/stage/EnsureClusterURLStage.java
@@ -8,23 +8,23 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.GeneralSecurityException;
 
 import org.mozilla.gecko.sync.GlobalSession;
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NodeAuthenticationException;
 import org.mozilla.gecko.sync.NullClusterURLException;
 import org.mozilla.gecko.sync.ThreadPool;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.SyncResourceDelegate;
 
-import android.util.Log;
 import ch.boye.httpclientandroidlib.HttpEntity;
 import ch.boye.httpclientandroidlib.HttpResponse;
 import ch.boye.httpclientandroidlib.client.ClientProtocolException;
 
 public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
   public EnsureClusterURLStage(GlobalSession session) {
     super(session);
   }
@@ -64,17 +64,17 @@ public class EnsureClusterURLStage exten
    *
    * @param nodeWeaveURL
    *          Where to request the cluster URL from, usually something like:
    *          <code>https://server/pathname/version/username/node/weave</code>.
    * @throws URISyntaxException
    */
   public static void fetchClusterURL(final String nodeWeaveURL,
                                      final ClusterURLFetchDelegate delegate) throws URISyntaxException {
-    Log.d(LOG_TAG, "In fetchClusterURL: node/weave is " + nodeWeaveURL);
+    Logger.info(LOG_TAG, "In fetchClusterURL: node/weave is " + nodeWeaveURL);
 
     BaseResource resource = new BaseResource(nodeWeaveURL);
     resource.delegate = new SyncResourceDelegate(resource) {
 
       /**
        * Handle the response for GET https://server/pathname/version/username/node/weave.
        *
        * Returns the Sync Node that the client is located on.
@@ -96,17 +96,17 @@ public class EnsureClusterURLStage exten
        * {@link http://docs.services.mozilla.com/reg/apis.html}
        */
       @Override
       public void handleHttpResponse(HttpResponse response) {
         try {
           int status = response.getStatusLine().getStatusCode();
           switch (status) {
           case 200:
-            Log.i(LOG_TAG, "Got 200 for node/weave cluster URL request (user found; succeeding).");
+            Logger.info(LOG_TAG, "Got 200 for node/weave cluster URL request (user found; succeeding).");
             HttpEntity entity = response.getEntity();
             if (entity == null) {
               delegate.handleThrottled();
               BaseResource.consumeEntity(response);
               return;
             }
             String output = null;
             try {
@@ -134,25 +134,25 @@ public class EnsureClusterURLStage exten
               URI uri = new URI(output);
               delegate.handleSuccess(uri);
             } catch (URISyntaxException e) {
               delegate.handleError(e);
             }
             break;
           case 400:
           case 404:
-            Log.i(LOG_TAG, "Got " + status + " for node/weave cluster URL request (user not found; failing).");
+            Logger.info(LOG_TAG, "Got " + status + " for node/weave cluster URL request (user not found; failing).");
             delegate.handleFailure(response);
             break;
           case 503:
-            Log.i(LOG_TAG, "Got 503 for node/weave cluster URL request (error fetching node; failing).");
+            Logger.info(LOG_TAG, "Got 503 for node/weave cluster URL request (error fetching node; failing).");
             delegate.handleFailure(response);
             break;
           default:
-            Log.w(LOG_TAG, "Got " + status + " for node/weave cluster URL request (unexpected HTTP status; failing).");
+            Logger.warn(LOG_TAG, "Got " + status + " for node/weave cluster URL request (unexpected HTTP status; failing).");
             delegate.handleFailure(response);
           }
         } finally {
           BaseResource.consumeEntity(response);
         }
 
         BaseResource.consumeEntity(response);
       }
@@ -176,27 +176,27 @@ public class EnsureClusterURLStage exten
     resource.get();
   }
 
   public void execute() throws NoSuchStageException {
     final URI oldClusterURL = session.config.getClusterURL();
     final boolean wantNodeAssignment = session.callback.wantNodeAssignment();
 
     if (!wantNodeAssignment && oldClusterURL != null) {
-      Log.i(LOG_TAG, "Cluster URL is already set and not stale. Continuing with sync.");
+      Logger.info(LOG_TAG, "Cluster URL is already set and not stale. Continuing with sync.");
       session.advance();
       return;
     }
 
-    Log.i(LOG_TAG, "Fetching cluster URL.");
+    Logger.info(LOG_TAG, "Fetching cluster URL.");
     final ClusterURLFetchDelegate delegate = new ClusterURLFetchDelegate() {
 
       @Override
       public void handleSuccess(final URI url) {
-        Log.i(LOG_TAG, "Node assignment pointed us to " + url);
+        Logger.info(LOG_TAG, "Node assignment pointed us to " + url);
 
         if (oldClusterURL != null && oldClusterURL.equals(url)) {
           // Our cluster URL is marked as stale and the fresh cluster URL is the same -- this is the user's problem.
           session.callback.informNodeAuthenticationFailed(session, url);
           session.abort(new NodeAuthenticationException(), "User password has changed.");
           return;
         }
 
@@ -214,26 +214,26 @@ public class EnsureClusterURLStage exten
       @Override
       public void handleThrottled() {
         session.abort(new NullClusterURLException(), "Got 'null' cluster URL. Aborting.");
       }
 
       @Override
       public void handleFailure(HttpResponse response) {
         int statusCode = response.getStatusLine().getStatusCode();
-        Log.w(LOG_TAG, "Got HTTP failure fetching node assignment: " + statusCode);
+        Logger.warn(LOG_TAG, "Got HTTP failure fetching node assignment: " + statusCode);
         if (statusCode == 404) {
           URI serverURL = session.config.serverURL;
           if (serverURL != null) {
-            Log.i(LOG_TAG, "Using serverURL <" + serverURL.toASCIIString() + "> as clusterURL.");
+            Logger.info(LOG_TAG, "Using serverURL <" + serverURL.toASCIIString() + "> as clusterURL.");
             session.config.setClusterURL(serverURL);
             session.advance();
             return;
           }
-          Log.w(LOG_TAG, "No serverURL set to use as fallback cluster URL. Aborting sync.");
+          Logger.warn(LOG_TAG, "No serverURL set to use as fallback cluster URL. Aborting sync.");
           // Fallthrough to abort.
         } else {
           session.interpretHTTPFailure(response);
         }
         session.abort(new Exception("HTTP failure."), "Got failure fetching cluster URL.");
       }
 
       @Override
--- a/mobile/android/base/sync/stage/FetchInfoCollectionsStage.java
+++ b/mobile/android/base/sync/stage/FetchInfoCollectionsStage.java
@@ -6,32 +6,25 @@ package org.mozilla.gecko.sync.stage;
 
 import java.net.URISyntaxException;
 
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.delegates.InfoCollectionsDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 
-import android.util.Log;
-
 public class FetchInfoCollectionsStage extends AbstractNonRepositorySyncStage {
-  private static final String LOG_TAG = "FetchInfoCollStage";
-
   public FetchInfoCollectionsStage(GlobalSession session) {
     super(session);
   }
 
   public class StageInfoCollectionsDelegate implements InfoCollectionsDelegate {
 
     @Override
     public void handleSuccess(InfoCollections global) {
-      Log.i(LOG_TAG, "Got timestamps: ");
-
-      // TODO: decide whether more work needs to be done?
       session.advance();
     }
 
     @Override
     public void handleFailure(SyncStorageResponse response) {
       session.handleHTTPError(response, "Failure fetching info/collections.");
     }
 
--- a/mobile/android/base/sync/syncadapter/SyncAdapter.java
+++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java
@@ -244,17 +244,17 @@ public class SyncAdapter extends Abstrac
         long remainingSeconds = delay / 1000;
         syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS;
         return;
       }
     }
 
     // Pick up log level changes. Do this here so that we don't do extra work
     // if we're not going to be syncing.
-    Logger.refreshLogLevels();
+    Logger.resetLogging();
 
     // TODO: don't clear the auth token unless we have a sync error.
     Log.i(LOG_TAG, "Got onPerformSync. Extras bundle is " + extras);
     Log.i(LOG_TAG, "Account name: " + account.name);
 
     // TODO: don't always invalidate; use getShouldInvalidateAuthToken.
     // However, this fixes Bug 716815, so it'll do for now.
     Log.d(LOG_TAG, "Invalidating auth token.");
--- a/mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java
+++ b/mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java
@@ -2,18 +2,16 @@
  * 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.sync.synchronizer;
 
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 
-import android.util.Log;
-
 /**
  * Consume records from a queue inside a RecordsChannel, as fast as we can.
  * TODO: rewrite this in terms of an ExecutorService and a CompletionService.
  * See Bug 713483.
  *
  * @author rnewman
  *
  */
@@ -101,17 +99,17 @@ class ConcurrentRecordConsumer extends R
           debug("run() dropped monitor.");
         }
 
         trace("Storing record with guid " + record.guid + ".");
         try {
           delegate.store(record);
         } catch (Exception e) {
           // TODO: Bug 709371: track records that failed to apply.
-          Log.e(LOG_TAG, "Caught error in store.", e);
+          Logger.error(LOG_TAG, "Caught error in store.", e);
         }
         trace("Done with record.");
       }
       synchronized (monitor) {
         trace("run() took monitor.");
 
         if (allRecordsQueued) {
           debug("Done with records and no more to come. Notifying consumerIsDone.");
--- a/mobile/android/base/sync/synchronizer/SerialRecordConsumer.java
+++ b/mobile/android/base/sync/synchronizer/SerialRecordConsumer.java
@@ -1,147 +1,131 @@
 /* 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.sync.synchronizer;
 
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 
-import android.util.Log;
-
 /**
  * Consume records from a queue inside a RecordsChannel, storing them serially.
  * @author rnewman
  *
  */
 class SerialRecordConsumer extends RecordConsumer {
   private static final String LOG_TAG = "SerialRecordConsumer";
   protected boolean stopEventually = false;
   private volatile long counter = 0;
 
   public SerialRecordConsumer(RecordsConsumerDelegate delegate) {
     this.delegate = delegate;
   }
 
-  private static void info(String message) {
-    System.out.println("INFO: " + message);
-    Log.i(LOG_TAG, message);
-  }
-
-  private static void warn(String message, Exception ex) {
-    System.out.println("WARN: " + message);
-    Log.w(LOG_TAG, message, ex);
-  }
-
-  private static void debug(String message) {
-    System.out.println("DEBUG: " + message);
-    Log.d(LOG_TAG, message);
-  }
-
   private Object monitor = new Object();
   @Override
   public void doNotify() {
     synchronized (monitor) {
       monitor.notify();
     }
   }
 
   @Override
   public void queueFilled() {
-    debug("Queue filled.");
+    Logger.debug(LOG_TAG, "Queue filled.");
     synchronized (monitor) {
       this.stopEventually = true;
       monitor.notify();
     }
   }
 
   @Override
   public void halt() {
-    debug("Halting.");
+    Logger.debug(LOG_TAG, "Halting.");
     synchronized (monitor) {
       this.stopEventually = true;
       this.stopImmediately = true;
       monitor.notify();
     }
   }
 
   private Object storeSerializer = new Object();
   @Override
   public void stored() {
-    debug("Record stored. Notifying.");
+    Logger.debug(LOG_TAG, "Record stored. Notifying.");
     synchronized (storeSerializer) {
-      debug("stored() took storeSerializer.");
+      Logger.debug(LOG_TAG, "stored() took storeSerializer.");
       counter++;
       storeSerializer.notify();
-      debug("stored() dropped storeSerializer.");
+      Logger.debug(LOG_TAG, "stored() dropped storeSerializer.");
     }
   }
   private void storeSerially(Record record) {
-    debug("New record to store.");
+    Logger.debug(LOG_TAG, "New record to store.");
     synchronized (storeSerializer) {
-      debug("storeSerially() took storeSerializer.");
-      debug("Storing...");
+      Logger.debug(LOG_TAG, "storeSerially() took storeSerializer.");
+      Logger.debug(LOG_TAG, "Storing...");
       try {
         this.delegate.store(record);
       } catch (Exception e) {
-        warn("Got exception in store. Not waiting.", e);
+        Logger.warn(LOG_TAG, "Got exception in store. Not waiting.", e);
         return;      // So we don't block for a stored() that never comes.
       }
       try {
-        debug("Waiting...");
+        Logger.debug(LOG_TAG, "Waiting...");
         storeSerializer.wait();
       } catch (InterruptedException e) {
         // TODO
       }
-      debug("storeSerially() dropped storeSerializer.");
+      Logger.debug(LOG_TAG, "storeSerially() dropped storeSerializer.");
     }
   }
 
   private void consumerIsDone() {
     long counterNow = this.counter;
-    info("Consumer is done. Processed " + counterNow + ((counterNow == 1) ? " record." : " records."));
+    Logger.info(LOG_TAG, "Consumer is done. Processed " + counterNow + ((counterNow == 1) ? " record." : " records."));
     delegate.consumerIsDone(stopImmediately);
   }
 
   @Override
   public void run() {
     while (true) {
       synchronized (monitor) {
-        debug("run() took monitor.");
+        Logger.debug(LOG_TAG, "run() took monitor.");
         if (stopImmediately) {
-          debug("Stopping immediately. Clearing queue.");
+          Logger.debug(LOG_TAG, "Stopping immediately. Clearing queue.");
           delegate.getQueue().clear();
-          debug("Notifying consumer.");
+          Logger.debug(LOG_TAG, "Notifying consumer.");
           consumerIsDone();
           return;
         }
-        debug("run() dropped monitor.");
+        Logger.debug(LOG_TAG, "run() dropped monitor.");
       }
       // The queue is concurrent-safe.
       while (!delegate.getQueue().isEmpty()) {
-        debug("Grabbing record...");
+        Logger.debug(LOG_TAG, "Grabbing record...");
         Record record = delegate.getQueue().remove();
         // Block here, allowing us to process records
         // serially.
-        debug("Invoking storeSerially...");
+        Logger.debug(LOG_TAG, "Invoking storeSerially...");
         this.storeSerially(record);
-        debug("Done with record.");
+        Logger.debug(LOG_TAG, "Done with record.");
       }
       synchronized (monitor) {
-        debug("run() took monitor.");
+        Logger.debug(LOG_TAG, "run() took monitor.");
 
         if (stopEventually) {
-          debug("Done with records and told to stop. Notifying consumer.");
+          Logger.debug(LOG_TAG, "Done with records and told to stop. Notifying consumer.");
           consumerIsDone();
           return;
         }
         try {
-          debug("Not told to stop but no records. Waiting.");
+          Logger.debug(LOG_TAG, "Not told to stop but no records. Waiting.");
           monitor.wait(10000);
         } catch (InterruptedException e) {
           // TODO
         }
-        debug("run() dropped monitor.");
+        Logger.debug(LOG_TAG, "run() dropped monitor.");
       }
     }
   }
-}
\ No newline at end of file
+}
--- a/mobile/android/base/sync/synchronizer/Synchronizer.java
+++ b/mobile/android/base/sync/synchronizer/Synchronizer.java
@@ -1,20 +1,20 @@
 /* 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.sync.synchronizer;
 
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.SynchronizerConfiguration;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 
 import android.content.Context;
-import android.util.Log;
 
 /**
  * I perform a sync.
  *
  * Initialize me by calling `load` with a SynchronizerConfiguration.
  *
  * Start synchronizing by calling `synchronize` with a SynchronizerDelegate. I
  * provide coarse-grained feedback by calling my delegate's callback methods.
@@ -36,25 +36,25 @@ public class Synchronizer implements Syn
 
   @Override
   public void onInitialized(SynchronizerSession session) {
     session.synchronize();
   }
 
   @Override
   public void onSynchronized(SynchronizerSession synchronizerSession) {
-    Log.d(LOG_TAG, "Got onSynchronized.");
-    Log.d(LOG_TAG, "Notifying SynchronizerDelegate.");
+    Logger.debug(LOG_TAG, "Got onSynchronized.");
+    Logger.debug(LOG_TAG, "Notifying SynchronizerDelegate.");
     this.synchronizerDelegate.onSynchronized(synchronizerSession.getSynchronizer());
   }
 
   @Override
   public void onSynchronizeSkipped(SynchronizerSession synchronizerSession) {
-    Log.d(LOG_TAG, "Got onSynchronizeSkipped.");
-    Log.d(LOG_TAG, "Notifying SynchronizerDelegate as if on success.");
+    Logger.debug(LOG_TAG, "Got onSynchronizeSkipped.");
+    Logger.debug(LOG_TAG, "Notifying SynchronizerDelegate as if on success.");
     this.synchronizerDelegate.onSynchronized(synchronizerSession.getSynchronizer());
   }
 
   @Override
   public void onSynchronizeFailed(SynchronizerSession session,
       Exception lastException, String reason) {
     this.synchronizerDelegate.onSynchronizeFailed(session.getSynchronizer(), lastException, reason);
   }
--- a/mobile/android/sync/java-sources.mn
+++ b/mobile/android/sync/java-sources.mn
@@ -47,27 +47,33 @@ sync/jpake/stage/DecryptDataStage.java
 sync/jpake/stage/DeleteChannel.java
 sync/jpake/stage/GetChannelStage.java
 sync/jpake/stage/GetRequestStage.java
 sync/jpake/stage/JPakeStage.java
 sync/jpake/stage/PutRequestStage.java
 sync/jpake/stage/VerifyPairingStage.java
 sync/jpake/Zkp.java
 sync/KeyBundleProvider.java
+sync/log/writers/AndroidLevelCachingLogWriter.java
+sync/log/writers/AndroidLogWriter.java
+sync/log/writers/LevelFilteringLogWriter.java
+sync/log/writers/LogWriter.java
+sync/log/writers/PrintLogWriter.java
+sync/log/writers/SingleTagLogWriter.java
+sync/log/writers/StringLogWriter.java
 sync/Logger.java
 sync/MetaGlobal.java
 sync/MetaGlobalException.java
 sync/MetaGlobalMissingEnginesException.java
 sync/MetaGlobalNotSetException.java
 sync/middleware/Crypto5MiddlewareRepository.java
 sync/middleware/Crypto5MiddlewareRepositorySession.java
 sync/middleware/MiddlewareRepository.java
 sync/middleware/MiddlewareRepositorySession.java
 sync/net/BaseResource.java
-sync/net/CompletedEntity.java
 sync/net/ConnectionMonitorThread.java
 sync/net/HandleProgressException.java
 sync/net/HttpResponseObserver.java
 sync/net/Resource.java
 sync/net/ResourceDelegate.java
 sync/net/SyncResourceDelegate.java
 sync/net/SyncResponse.java
 sync/net/SyncStorageCollectionRequest.java