From aaf4a4129a00fc8f791deb304c8c8ce2bba6b0a0 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Wed, 8 Aug 2012 17:13:20 -0700 Subject: [PATCH] Bug 761682, Bug 777973 - Version prefs; don't always invalidate auth token. r=rnewman --- mobile/android/base/android-sync-files.mk | 2 +- .../base/resources/layout/sync_send_tab.xml | 4 +- .../base/sync/CredentialException.java | 55 +++ .../android/base/sync/SyncConfiguration.java | 10 + mobile/android/base/sync/SyncException.java | 21 +- mobile/android/base/sync/Utils.java | 36 +- .../sync/config/ConfigurationMigrator.java | 382 ++++++++++++++++++ .../base/sync/receivers/UpgradeReceiver.java | 45 ++- mobile/android/base/sync/setup/Constants.java | 5 +- .../android/base/sync/setup/SyncAccounts.java | 207 ++++++++-- .../sync/setup/SyncAuthenticatorService.java | 132 +++--- .../setup/activities/SendTabActivity.java | 80 +++- .../base/sync/syncadapter/SyncAdapter.java | 341 +++++++--------- mobile/android/sync/java-sources.mn | 2 + 14 files changed, 1003 insertions(+), 319 deletions(-) create mode 100644 mobile/android/base/sync/CredentialException.java create mode 100644 mobile/android/base/sync/config/ConfigurationMigrator.java diff --git a/mobile/android/base/android-sync-files.mk b/mobile/android/base/android-sync-files.mk index db6bbc1f398e..9d5afd64fa3f 100644 --- a/mobile/android/base/android-sync-files.mk +++ b/mobile/android/base/android-sync-files.mk @@ -3,7 +3,7 @@ # 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/config/AccountPickler.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_JAVA_FILES := sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/config/AccountPickler.java sync/config/ConfigurationMigrator.java sync/CredentialException.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 diff --git a/mobile/android/base/resources/layout/sync_send_tab.xml b/mobile/android/base/resources/layout/sync_send_tab.xml index 378eb1e0d6a4..3f108caf0e99 100644 --- a/mobile/android/base/resources/layout/sync_send_tab.xml +++ b/mobile/android/base/resources/layout/sync_send_tab.xml @@ -8,7 +8,7 @@ @@ -25,4 +25,4 @@ android:enabled="false" android:text="@string/sync_button_send" /> - + \ No newline at end of file diff --git a/mobile/android/base/sync/CredentialException.java b/mobile/android/base/sync/CredentialException.java new file mode 100644 index 000000000000..58cb65bdccff --- /dev/null +++ b/mobile/android/base/sync/CredentialException.java @@ -0,0 +1,55 @@ +/* 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 android.content.SyncResult; + +/** + * There was a problem with the Sync account's credentials: bad username, + * missing password, malformed sync key, etc. + */ +public abstract class CredentialException extends SyncException { + private static final long serialVersionUID = 833010553314100538L; + + public CredentialException() { + super(); + } + + public CredentialException(final Throwable e) { + super(e); + } + + public void updateStats(GlobalSession globalSession, SyncResult syncResult) { + syncResult.stats.numAuthExceptions += 1; + } + + /** + * No credentials at all. + */ + public static class MissingAllCredentialsException extends CredentialException { + private static final long serialVersionUID = 3763937096217604611L; + + public MissingAllCredentialsException() { + super(); + } + + public MissingAllCredentialsException(final Throwable e) { + super(e); + } + } + + /** + * Some credential is missing. + */ + public static class MissingCredentialException extends CredentialException { + private static final long serialVersionUID = -7543031216547596248L; + + public final String missingCredential; + + public MissingCredentialException(final String missingCredential) { + this.missingCredential = missingCredential; + } + } +} diff --git a/mobile/android/base/sync/SyncConfiguration.java b/mobile/android/base/sync/SyncConfiguration.java index ee81e25e6ce9..c087f3b53d5f 100644 --- a/mobile/android/base/sync/SyncConfiguration.java +++ b/mobile/android/base/sync/SyncConfiguration.java @@ -216,6 +216,9 @@ public class SyncConfiguration implements CredentialsSource { public String prefsPath; public PrefsSource prefsSource; + public static final String PREF_PREFS_VERSION = "prefs.version"; + public static final long CURRENT_PREFS_VERSION = 1; + public static final String CLIENTS_COLLECTION_TIMESTAMP = "serverClientsTimestamp"; // When the collection was touched. public static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp"; // When our record was touched. @@ -223,6 +226,13 @@ public class SyncConfiguration implements CredentialsSource { public static final String PREF_SYNC_ID = "syncID"; public static final String PREF_ENABLED_ENGINE_NAMES = "enabledEngineNames"; + public static final String PREF_EARLIEST_NEXT_SYNC = "earliestnextsync"; + public static final String PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale"; + + public static final String PREF_ACCOUNT_GUID = "account.guid"; + public static final String PREF_CLIENT_NAME = "account.clientName"; + public static final String PREF_NUM_CLIENTS = "account.numClients"; + /** * Create a new SyncConfiguration instance. Pass in a PrefsSource to * provide access to preferences. diff --git a/mobile/android/base/sync/SyncException.java b/mobile/android/base/sync/SyncException.java index 9b65358c2205..ee0902568111 100644 --- a/mobile/android/base/sync/SyncException.java +++ b/mobile/android/base/sync/SyncException.java @@ -7,14 +7,25 @@ package org.mozilla.gecko.sync; import android.content.SyncResult; public abstract class SyncException extends Exception { - public Exception cause = null; - public SyncException(Exception ex) { - cause = ex; - } + private static final long serialVersionUID = -6928990004393234738L; + public SyncException() { + super(); } - private static final long serialVersionUID = -6928990004393234738L; + public SyncException(final Throwable e) { + super(e); + } + + /** + * Update sync result statistics with information particular to this + * exception. + * + * @param globalSession + * current session, or null. + * @param syncResult + * Android sync result to update. + */ public void updateStats(GlobalSession globalSession, SyncResult syncResult) { // Assume storage error. // TODO: this logic is overly simplistic. diff --git a/mobile/android/base/sync/Utils.java b/mobile/android/base/sync/Utils.java index e12711b6bc84..1bd8620e321e 100644 --- a/mobile/android/base/sync/Utils.java +++ b/mobile/android/base/sync/Utils.java @@ -215,34 +215,34 @@ public class Utils { return sha1Base32(account.toLowerCase(Locale.US)); } + public static SharedPreferences getSharedPreferences(final Context context, final String product, final String username, final String serverURL, final String profile, final long version) + throws NoSuchAlgorithmException, UnsupportedEncodingException { + String prefsPath = getPrefsPath(product, username, serverURL, profile, version); + return context.getSharedPreferences(prefsPath, SHARED_PREFERENCES_MODE); + } + /** * Get shared preferences path for a Sync account. * + * @param product the Firefox Sync product package name (like "org.mozilla.firefox"). * @param username the Sync account name, optionally encoded with Utils.usernameFromAccount. * @param serverURL the Sync account server URL. + * @param profile the Firefox profile name. + * @param version the version of preferences to reference. * @return the path. * @throws NoSuchAlgorithmException * @throws UnsupportedEncodingException */ - public static String getPrefsPath(String username, String serverURL) - throws NoSuchAlgorithmException, UnsupportedEncodingException { - return "sync.prefs." + sha1Base32(serverURL + ":" + usernameFromAccount(username)); - } + public static String getPrefsPath(final String product, final String username, final String serverURL, final String profile, final long version) + throws NoSuchAlgorithmException, UnsupportedEncodingException { + final String encodedAccount = sha1Base32(serverURL + ":" + usernameFromAccount(username)); - /** - * Get shared preferences for a Sync account. - * - * @param context Android Context. - * @param username the Sync account name, optionally encoded with Utils.usernameFromAccount. - * @param serverURL the Sync account server URL. - * @return a SharedPreferences instance. - * @throws NoSuchAlgorithmException - * @throws UnsupportedEncodingException - */ - public static SharedPreferences getSharedPreferences(Context context, String username, String serverURL) throws NoSuchAlgorithmException, UnsupportedEncodingException { - String prefsPath = getPrefsPath(username, serverURL); - Logger.debug(LOG_TAG, "Shared preferences: " + prefsPath); - return context.getSharedPreferences(prefsPath, SHARED_PREFERENCES_MODE); + if (version <= 0) { + return "sync.prefs." + encodedAccount; + } else { + final String sanitizedProduct = product.replace('.', '!').replace(' ', '!'); + return "sync.prefs." + sanitizedProduct + "." + encodedAccount + "." + profile + "." + version; + } } public static void addToIndexBucketMap(TreeMap> map, long index, String value) { diff --git a/mobile/android/base/sync/config/ConfigurationMigrator.java b/mobile/android/base/sync/config/ConfigurationMigrator.java new file mode 100644 index 000000000000..4b14b069105d --- /dev/null +++ b/mobile/android/base/sync/config/ConfigurationMigrator.java @@ -0,0 +1,382 @@ +/* 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.config; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.SyncConfiguration; +import org.mozilla.gecko.sync.Utils; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; + +/** + * Migrate Sync preferences between versions. + *

+ * The original preferences were un-versioned; we refer to that as "version 0". + * The original preferences were stored in three places: + *

    + *
  • most prefs were kept in per-Sync account Android shared prefs;
  • + *
  • some prefs were kept in per-App Android shared prefs;
  • + *
  • some client prefs were kept in the (assumed unique) Android Account.
  • + *
+ *

+ * Post version 0, all preferences are stored in per-Sync account Android shared prefs. + */ +public class ConfigurationMigrator { + public static final String LOG_TAG = "ConfigMigrator"; + + /** + * Copy and rename preferences. + * + * @param from source. + * @param to sink. + * @param map map from old preference names to new preference names. + * @return the number of preferences migrated. + */ + protected static int copyPreferences(final SharedPreferences from, final Map map, final Editor to) { + int count = 0; + + // SharedPreferences has no way to get a key/value pair without specifying the value type, so we do this instead. + for (Entry entry : from.getAll().entrySet()) { + String fromKey = entry.getKey(); + String toKey = map.get(fromKey); + if (toKey == null) { + continue; + } + + Object value = entry.getValue(); + if (value instanceof Boolean) { + to.putBoolean(toKey, ((Boolean) value).booleanValue()); + } else if (value instanceof Float) { + to.putFloat(toKey, ((Float) value).floatValue()); + } else if (value instanceof Integer) { + to.putInt(toKey, ((Integer) value).intValue()); + } else if (value instanceof Long) { + to.putLong(toKey, ((Long) value).longValue()); + } else if (value instanceof String) { + to.putString(toKey, (String) value); + } else { + // Do nothing -- perhaps SharedPreferences accepts types we don't know about. + } + + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + value + ")."); + } else { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'."); + } + count += 1; + } + + return count; + } + + protected final static String V0_PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale"; + protected final static String V1_PREF_CLUSTER_URL_IS_STALE = V0_PREF_CLUSTER_URL_IS_STALE; + protected final static String V0_PREF_EARLIEST_NEXT_SYNC = "earliestnextsync"; + protected final static String V1_PREF_EARLIEST_NEXT_SYNC = V0_PREF_EARLIEST_NEXT_SYNC; + + /** + * Extract version 0 preferences from per-App Android shared prefs and write to version 1 per-Sync account shared prefs. + * + * @param from per-App version 0 Android shared prefs. + * @param to per-Sync account version 1 shared prefs. + * @return the number of preferences migrated. + * @throws Exception + */ + protected static int upgradeGlobals0to1(final SharedPreferences from, final SharedPreferences to) throws Exception { + Map map = new HashMap(); + map.put(V0_PREF_CLUSTER_URL_IS_STALE, V1_PREF_CLUSTER_URL_IS_STALE); + map.put(V0_PREF_EARLIEST_NEXT_SYNC, V1_PREF_EARLIEST_NEXT_SYNC); + + Editor editor = to.edit(); + int count = copyPreferences(from, map, editor); + if (count > 0) { + editor.commit(); + } + return count; + } + + /** + * Extract version 1 per-Sync account shared prefs and write to version 0 preferences from per-App Android shared prefs. + * + * @param from per-Sync account version 1 shared prefs. + * @param to per-App version 0 Android shared prefs. + * @return the number of preferences migrated. + * @throws Exception + */ + protected static int downgradeGlobals1to0(final SharedPreferences from, final SharedPreferences to) throws Exception { + Map map = new HashMap(); + map.put(V1_PREF_CLUSTER_URL_IS_STALE, V0_PREF_CLUSTER_URL_IS_STALE); + map.put(V1_PREF_EARLIEST_NEXT_SYNC, V0_PREF_EARLIEST_NEXT_SYNC); + + Editor editor = to.edit(); + int count = copyPreferences(from, map, editor); + if (count > 0) { + editor.commit(); + } + return count; + } + + protected static final String V0_PREF_ACCOUNT_GUID = "account.guid"; + protected static final String V1_PREF_ACCOUNT_GUID = V0_PREF_ACCOUNT_GUID; + protected static final String V0_PREF_CLIENT_NAME = "account.clientName"; + protected static final String V1_PREF_CLIENT_NAME = V0_PREF_CLIENT_NAME; + protected static final String V0_PREF_NUM_CLIENTS = "account.numClients"; + protected static final String V1_PREF_NUM_CLIENTS = V0_PREF_NUM_CLIENTS; + + /** + * Extract version 0 per-Android account user data and write to version 1 per-Sync account shared prefs. + * + * @param accountManager Android account manager. + * @param account Android account. + * @param to per-Sync account version 1 shared prefs. + * @return the number of preferences migrated. + * @throws Exception + */ + protected static int upgradeAndroidAccount0to1(final AccountManager accountManager, final Account account, final SharedPreferences to) throws Exception { + final String V0_PREF_ACCOUNT_GUID = "account.guid"; + final String V1_PREF_ACCOUNT_GUID = V0_PREF_ACCOUNT_GUID; + final String V0_PREF_CLIENT_NAME = "account.clientName"; + final String V1_PREF_CLIENT_NAME = V0_PREF_CLIENT_NAME; + final String V0_PREF_NUM_CLIENTS = "account.numClients"; + final String V1_PREF_NUM_CLIENTS = V0_PREF_NUM_CLIENTS; + + String accountGUID = null; + String clientName = null; + long numClients = -1; + try { + accountGUID = accountManager.getUserData(account, V0_PREF_ACCOUNT_GUID); + } catch (Exception e) { + // Do nothing. + } + try { + clientName = accountManager.getUserData(account, V0_PREF_CLIENT_NAME); + } catch (Exception e) { + // Do nothing. + } + try { + numClients = Long.parseLong(accountManager.getUserData(account, V0_PREF_NUM_CLIENTS)); + } catch (Exception e) { + // Do nothing. + } + + final Editor editor = to.edit(); + + int count = 0; + if (accountGUID != null) { + final String fromKey = V0_PREF_ACCOUNT_GUID; + final String toKey = V1_PREF_ACCOUNT_GUID; + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + accountGUID + ")."); + } else { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'."); + } + editor.putString(toKey, accountGUID); + count += 1; + } + if (clientName != null) { + final String fromKey = V0_PREF_CLIENT_NAME; + final String toKey = V1_PREF_CLIENT_NAME; + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + clientName + ")."); + } else { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'."); + } + editor.putString(toKey, clientName); + count += 1; + } + if (numClients > -1) { + final String fromKey = V0_PREF_NUM_CLIENTS; + final String toKey = V1_PREF_NUM_CLIENTS; + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "' (" + numClients + ")."); + } else { + Logger.debug(LOG_TAG, "Migrated '" + fromKey + "' to '" + toKey + "'."); + } + editor.putLong(toKey, numClients); + count += 1; + } + + if (count > 0) { + editor.commit(); + } + return count; + } + + /** + * Extract version 1 per-Sync account shared prefs and write to version 0 per-Android account user data. + * + * @param from per-Sync account version 1 shared prefs. + * @param accountManager Android account manager. + * @param account Android account. + * @return the number of preferences migrated. + * @throws Exception + */ + protected static int downgradeAndroidAccount1to0(final SharedPreferences from, final AccountManager accountManager, final Account account) throws Exception { + final String accountGUID = from.getString(V1_PREF_ACCOUNT_GUID, null); + final String clientName = from.getString(V1_PREF_CLIENT_NAME, null); + final long numClients = from.getLong(V1_PREF_NUM_CLIENTS, -1L); + + int count = 0; + if (accountGUID != null) { + Logger.debug(LOG_TAG, "Migrated account GUID."); + accountManager.setUserData(account, V0_PREF_ACCOUNT_GUID, accountGUID); + count += 1; + } + if (clientName != null) { + Logger.debug(LOG_TAG, "Migrated client name."); + accountManager.setUserData(account, V1_PREF_CLIENT_NAME, clientName); + count += 1; + } + if (numClients > -1) { + Logger.debug(LOG_TAG, "Migrated clients count."); + accountManager.setUserData(account, V1_PREF_NUM_CLIENTS, new Long(numClients).toString()); + count += 1; + } + return count; + } + + /** + * Extract version 0 per-Android account user data and write to version 1 per-Sync account shared prefs. + * + * @param from per-Sync account version 0 shared prefs. + * @param to per-Sync account version 1 shared prefs. + * @return the number of preferences migrated. + * @throws Exception + */ + protected static int upgradeShared0to1(final SharedPreferences from, final SharedPreferences to) { + final Map map = new HashMap(); + final String[] prefs = new String [] { + "syncID", + "clusterURL", + "enabledEngineNames", + + "metaGlobalLastModified", "metaGlobalServerResponseBody", + + "crypto5KeysLastModified", "crypto5KeysServerResponseBody", + + "serverClientsTimestamp", "serverClientRecordTimestamp", + + "forms.remote", "forms.local", "forms.syncID", + "tabs.remote", "tabs.local", "tabs.syncID", + "passwords.remote", "passwords.local", "passwords.syncID", + "history.remote", "history.local", "history.syncID", + "bookmarks.remote", "bookmarks.local", "bookmarks.syncID", + }; + for (String pref : prefs) { + map.put(pref, pref); + } + + Editor editor = to.edit(); + int count = copyPreferences(from, map, editor); + if (count > 0) { + editor.commit(); + } + return count; + } + + /** + * Extract version 1 per-Sync account shared prefs and write to version 0 per-Android account user data. + * + * @param from per-Sync account version 1 shared prefs. + * @param to per-Sync account version 0 shared prefs. + * @return the number of preferences migrated. + * @throws Exception + */ + protected static int downgradeShared1to0(final SharedPreferences from, final SharedPreferences to) { + // Strictly a copy, no re-naming, no deletions -- so just invert. + return upgradeShared0to1(from, to); + } + + public static void upgrade0to1(final Context context, final AccountManager accountManager, final Account account, + final String product, final String username, final String serverURL, final String profile) throws Exception { + + final String GLOBAL_SHARED_PREFS = "sync.prefs.global"; + + final SharedPreferences globalPrefs = context.getSharedPreferences(GLOBAL_SHARED_PREFS, Utils.SHARED_PREFERENCES_MODE); + final SharedPreferences accountPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 0); + final SharedPreferences newPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 1); + + upgradeGlobals0to1(globalPrefs, newPrefs); + upgradeAndroidAccount0to1(accountManager, account, newPrefs); + upgradeShared0to1(accountPrefs, newPrefs); + } + + public static void downgrade1to0(final Context context, final AccountManager accountManager, final Account account, + final String product, final String username, final String serverURL, final String profile) throws Exception { + + final String GLOBAL_SHARED_PREFS = "sync.prefs.global"; + + final SharedPreferences globalPrefs = context.getSharedPreferences(GLOBAL_SHARED_PREFS, Utils.SHARED_PREFERENCES_MODE); + final SharedPreferences accountPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 0); + final SharedPreferences oldPrefs = Utils.getSharedPreferences(context, product, username, serverURL, profile, 1); + + downgradeGlobals1to0(oldPrefs, globalPrefs); + downgradeAndroidAccount1to0(oldPrefs, accountManager, account); + downgradeShared1to0(oldPrefs, accountPrefs); + } + + /** + * Migrate, if necessary, existing prefs to a certain version. + *

+ * Stores current prefs version in Android shared prefs with root + * "sync.prefs.version", which corresponds to the file + * "sync.prefs.version.xml". + * + * @param desiredVersion + * version to finish it. + * @param context + * @param accountManager + * @param account + * @param product + * @param username + * @param serverURL + * @param profile + * @throws Exception + */ + public static void ensurePrefsAreVersion(final long desiredVersion, + final Context context, final AccountManager accountManager, final Account account, + final String product, final String username, final String serverURL, final String profile) throws Exception { + if (desiredVersion < 0 || desiredVersion > SyncConfiguration.CURRENT_PREFS_VERSION) { + throw new IllegalArgumentException("Cannot migrate to unknown version " + desiredVersion + "."); + } + + SharedPreferences versionPrefs = context.getSharedPreferences("sync.prefs.version", Utils.SHARED_PREFERENCES_MODE); + + // We default to 0 since clients getting this code for the first time will + // not have "sync.prefs.version.xml" *at all*, and upgrading when all old + // data is missing is expected to be safe. + long currentVersion = versionPrefs.getLong(SyncConfiguration.PREF_PREFS_VERSION, 0); + if (currentVersion == desiredVersion) { + Logger.info(LOG_TAG, "Current version (" + currentVersion + ") is desired version; no need to migrate."); + return; + } + + if (currentVersion < 0 || currentVersion > SyncConfiguration.CURRENT_PREFS_VERSION) { + throw new IllegalStateException("Cannot migrate from unknown version " + currentVersion + "."); + } + + // Now we're down to either version 0 or version 1. + if (currentVersion == 0 && desiredVersion == 1) { + Logger.info(LOG_TAG, "Upgrading from version 0 to version 1."); + upgrade0to1(context, accountManager, account, product, username, serverURL, profile); + } else if (currentVersion == 1 && desiredVersion == 0) { + Logger.info(LOG_TAG, "Upgrading from version 0 to version 1."); + upgrade0to1(context, accountManager, account, product, username, serverURL, profile); + } else { + Logger.warn(LOG_TAG, "Don't know how to migrate from version " + currentVersion + " to " + desiredVersion + "."); + } + + Logger.info(LOG_TAG, "Migrated from version " + currentVersion + " to version " + desiredVersion + "."); + versionPrefs.edit().putLong(SyncConfiguration.PREF_PREFS_VERSION, desiredVersion).commit(); + } +} diff --git a/mobile/android/base/sync/receivers/UpgradeReceiver.java b/mobile/android/base/sync/receivers/UpgradeReceiver.java index 567f699713b8..dcd26977b629 100644 --- a/mobile/android/base/sync/receivers/UpgradeReceiver.java +++ b/mobile/android/base/sync/receivers/UpgradeReceiver.java @@ -4,10 +4,15 @@ package org.mozilla.gecko.sync.receivers; +import org.mozilla.gecko.sync.CredentialException; +import org.mozilla.gecko.sync.GlobalConstants; import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.SyncConfiguration; import org.mozilla.gecko.sync.ThreadPool; +import org.mozilla.gecko.sync.config.ConfigurationMigrator; import org.mozilla.gecko.sync.setup.Constants; import org.mozilla.gecko.sync.setup.SyncAccounts; +import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; import android.accounts.Account; import android.accounts.AccountManager; @@ -26,8 +31,9 @@ public class UpgradeReceiver extends BroadcastReceiver { ThreadPool.run(new Runnable() { @Override public void run() { - AccountManager accountManager = AccountManager.get(context); - Account[] accounts = accountManager.getAccounts(); + final AccountManager accountManager = AccountManager.get(context); + final Account[] accounts = SyncAccounts.syncAccounts(context); + for (Account a : accounts) { if ("1".equals(accountManager.getUserData(a, Constants.DATA_ENABLE_ON_UPGRADE))) { SyncAccounts.setSyncAutomatically(a, true); @@ -36,5 +42,40 @@ public class UpgradeReceiver extends BroadcastReceiver { } } }); + + /** + * Bug 761682: migrate preferences forward. + */ + ThreadPool.run(new Runnable() { + @Override + public void run() { + AccountManager accountManager = AccountManager.get(context); + final Account[] accounts = SyncAccounts.syncAccounts(context); + + for (Account account : accounts) { + Logger.info(LOG_TAG, "Migrating preferences on upgrade for Android account named " + account.name + "."); + + SyncAccountParameters params; + try { + params = SyncAccounts.blockingFromAndroidAccountV0(context, accountManager, account); + } catch (CredentialException e) { + Logger.warn(LOG_TAG, "Caught exception fetching account parameters while trying to migrate preferences; ignoring.", e); + continue; + } + + final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; + final String username = params.username; + final String serverURL = params.serverURL; + final String profile = "default"; + try { + ConfigurationMigrator.ensurePrefsAreVersion(SyncConfiguration.CURRENT_PREFS_VERSION, context, accountManager, account, + product, username, serverURL, profile); + } catch (Exception e) { + Logger.warn(LOG_TAG, "Caught exception trying to migrate preferences; ignoring.", e); + continue; + } + } + } + }); } } diff --git a/mobile/android/base/sync/setup/Constants.java b/mobile/android/base/sync/setup/Constants.java index 7dc800951818..fa76adb73e6a 100644 --- a/mobile/android/base/sync/setup/Constants.java +++ b/mobile/android/base/sync/setup/Constants.java @@ -12,11 +12,10 @@ public class Constants { public static final String OPTION_USERNAME = "option.username"; public static final String AUTHTOKEN_TYPE_PLAIN = "auth.plain"; public static final String OPTION_SERVER = "option.serverUrl"; - public static final String ACCOUNT_GUID = "account.guid"; - public static final String CLIENT_NAME = "account.clientName"; - public static final String NUM_CLIENTS = "account.numClients"; public static final String DATA_ENABLE_ON_UPGRADE = "data.enableOnUpgrade"; + public static final String DEFAULT_PROFILE = "default"; + /** * Name of file to pickle current account preferences to each sync. *

diff --git a/mobile/android/base/sync/setup/SyncAccounts.java b/mobile/android/base/sync/setup/SyncAccounts.java index 331e58733709..2778cf2711f7 100644 --- a/mobile/android/base/sync/setup/SyncAccounts.java +++ b/mobile/android/base/sync/setup/SyncAccounts.java @@ -5,19 +5,26 @@ package org.mozilla.gecko.sync.setup; import java.io.File; +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.mozilla.gecko.db.BrowserContract; +import org.mozilla.gecko.sync.CredentialException; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.GlobalConstants; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.SyncConfiguration; +import org.mozilla.gecko.sync.ThreadPool; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.config.AccountPickler; import org.mozilla.gecko.sync.repositories.android.RepoUtils; -import org.mozilla.gecko.sync.syncadapter.SyncAdapter; import android.accounts.Account; import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; @@ -43,6 +50,44 @@ public class SyncAccounts { public final static String DEFAULT_SERVER = "https://auth.services.mozilla.com/"; + /** + * Return Sync accounts. + * + * @param c Android context. + * @return Sync accounts. + */ + public static Account[] syncAccounts(final Context c) { + return AccountManager.get(c).getAccountsByType(GlobalConstants.ACCOUNTTYPE_SYNC); + } + + /** + * Asynchronously invalidate the auth token for a Sync account. + * + * @param accountManager Android account manager. + * @param account Android account. + */ + public static void invalidateAuthToken(final AccountManager accountManager, final Account account) { + if (account == null) { + return; + } + + // blockingGetAuthToken must not be called from the main thread. + ThreadPool.run(new Runnable() { + @Override + public void run() { + String authToken; + try { + authToken = accountManager.blockingGetAuthToken(account, Constants.AUTHTOKEN_TYPE_PLAIN, true); + } catch (Exception e) { + Logger.warn(LOG_TAG, "Got exception while invalidating auth token.", e); + return; + } + + accountManager.invalidateAuthToken(GlobalConstants.ACCOUNTTYPE_SYNC, authToken); + } + }); + } + /** * Returns true if a Sync account is set up, or we have a pickled Sync account * on disk that should be un-pickled (Bug 769745). If we have a pickled Sync @@ -321,28 +366,30 @@ public class SyncAccounts { setIsSyncable(account, syncAutomatically); Logger.debug(LOG_TAG, "Set account to sync automatically? " + syncAutomatically + "."); - setClientRecord(context, accountManager, account, syncAccount.clientName, syncAccount.clientGuid); - - // TODO: add other ContentProviders as needed (e.g. passwords) - // TODO: for each, also add to res/xml to make visible in account settings - Logger.debug(LOG_TAG, "Finished setting syncables."); - - // Purging global prefs assumes we have only a single Sync account at one time. - // TODO: Bug 761682: don't do anything with global prefs here. - if (clearPreferences) { - Logger.info(LOG_TAG, "Clearing global prefs."); - SyncAdapter.purgeGlobalPrefs(context); - } - try { - SharedPreferences.Editor editor = Utils.getSharedPreferences(context, username, serverURL).edit(); + final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; + final String profile = Constants.DEFAULT_PROFILE; + final long version = SyncConfiguration.CURRENT_PREFS_VERSION; + + final SharedPreferences.Editor editor = Utils.getSharedPreferences(context, product, username, serverURL, profile, version).edit(); if (clearPreferences) { - Logger.info(LOG_TAG, "Clearing preferences path " + Utils.getPrefsPath(username, serverURL) + " for this account."); + final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version); + Logger.info(LOG_TAG, "Clearing preferences path " + prefsPath + " for this account."); editor.clear(); } + if (syncAccount.clusterURL != null) { editor.putString(SyncConfiguration.PREF_CLUSTER_URL, syncAccount.clusterURL); } + + if (syncAccount.clientName != null && syncAccount.clientGuid != null) { + Logger.debug(LOG_TAG, "Setting client name to " + syncAccount.clientName + " and client GUID to " + syncAccount.clientGuid + "."); + editor.putString(SyncConfiguration.PREF_CLIENT_NAME, syncAccount.clientName); + editor.putString(SyncConfiguration.PREF_ACCOUNT_GUID, syncAccount.clientGuid); + } else { + Logger.debug(LOG_TAG, "Client name and guid not both non-null, so not setting client data."); + } + editor.commit(); } catch (Exception e) { Logger.error(LOG_TAG, "Could not clear prefs path!", e); @@ -433,14 +480,126 @@ public class SyncAccounts { return intent; } - protected static void setClientRecord(Context context, AccountManager accountManager, Account account, - String clientName, String clientGuid) { - if (clientName != null && clientGuid != null) { - Logger.debug(LOG_TAG, "Setting client name to " + clientName + " and client GUID to " + clientGuid + "."); - SyncAdapter.setAccountGUID(accountManager, account, clientGuid); - SyncAdapter.setClientName(accountManager, account, clientName); - return; + protected static class SyncAccountVersion0Callback implements AccountManagerCallback { + protected final Context context; + protected final CountDownLatch latch; + + public String authToken = null; + + public SyncAccountVersion0Callback(final Context context, final CountDownLatch latch) { + this.context = context; + this.latch = latch; + } + + @Override + public void run(AccountManagerFuture future) { + try { + Bundle bundle = future.getResult(60L, TimeUnit.SECONDS); + if (bundle.containsKey(AccountManager.KEY_INTENT)) { + throw new IllegalStateException("KEY_INTENT included in AccountManagerFuture bundle."); + } + if (bundle.containsKey(AccountManager.KEY_ERROR_MESSAGE)) { + throw new IllegalStateException("KEY_ERROR_MESSAGE (= " + bundle.getString(AccountManager.KEY_ERROR_MESSAGE) + ") " + + " included in AccountManagerFuture bundle."); + } + + authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); + } catch (Exception e) { + // Do nothing -- caller will find null authToken. + Logger.warn(LOG_TAG, "Got exception fetching auth token; ignoring and returning null auth token instead.", e); + } finally { + latch.countDown(); + } + } + } + + /** + * Synchronously extract Sync account parameters from Android account version + * 0, using plain auth token type. + *

+ * Safe to call from main thread. + * + * @param context + * @param accountManager + * Android account manager. + * @param account + * Android account. + * @return Sync account parameters, always non-null; fields username, + * password, serverURL, and syncKey always non-null. + */ + public static SyncAccountParameters blockingFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account) + throws CredentialException { + final CountDownLatch latch = new CountDownLatch(1); + final SyncAccountVersion0Callback callback = new SyncAccountVersion0Callback(context, latch); + + new Thread(new Runnable() { + @Override + public void run() { + // Get an auth token. + accountManager.getAuthToken(account, Constants.AUTHTOKEN_TYPE_PLAIN, true, callback, null); + } + }).start(); + + try { + latch.await(); + } catch (InterruptedException e) { + Logger.warn(LOG_TAG, "Got exception waiting for Sync account parameters; throwing."); + throw new CredentialException.MissingAllCredentialsException(e); + } + + String username; + try { + username = Utils.usernameFromAccount(account.name); + } catch (NoSuchAlgorithmException e) { + throw new CredentialException.MissingCredentialException("username"); + } catch (UnsupportedEncodingException e) { + throw new CredentialException.MissingCredentialException("username"); + } + + final String password = callback.authToken; + + /* + * If we are accessing an Account that we don't own, Android will throw an + * unchecked SecurityException saying + * "W FxSync(XXXX) java.lang.SecurityException: caller uid XXXXX is different than the authenticator's uid". + * We catch that error and throw accordingly. + */ + String syncKey; + String serverURL; + try { + syncKey = accountManager.getUserData(account, Constants.OPTION_SYNCKEY); + serverURL = accountManager.getUserData(account, Constants.OPTION_SERVER); + } catch (SecurityException e) { + Logger.warn(LOG_TAG, "Got security exception fetching Sync account parameters; throwing."); + throw new CredentialException.MissingAllCredentialsException(e); + } + + if (password == null && + username == null && + syncKey == null && + serverURL == null) { + throw new CredentialException.MissingAllCredentialsException(); + } + + if (password == null) { + throw new CredentialException.MissingCredentialException("password"); + } + + if (syncKey == null) { + throw new CredentialException.MissingCredentialException("syncKey"); + } + + if (serverURL == null) { + throw new CredentialException.MissingCredentialException("serverURL"); + } + + try { + // SyncAccountParameters constructor throws on null inputs. This shouldn't + // happen, but let's be safe. + return new SyncAccountParameters(context, accountManager, username, syncKey, password, serverURL); + } catch (Exception e) { + Logger.warn(LOG_TAG, "Got exception fetching Sync account parameters; throwing."); + throw new CredentialException.MissingAllCredentialsException(e); } - Logger.debug(LOG_TAG, "Client name and guid not both non-null, so not setting client data."); } } diff --git a/mobile/android/base/sync/setup/SyncAuthenticatorService.java b/mobile/android/base/sync/setup/SyncAuthenticatorService.java index 3e155f01f7df..ab3281e81430 100644 --- a/mobile/android/base/sync/setup/SyncAuthenticatorService.java +++ b/mobile/android/base/sync/setup/SyncAuthenticatorService.java @@ -49,6 +49,87 @@ public class SyncAuthenticatorService extends Service { return sAccountAuthenticator; } + /** + * Generate a "plain" auth token. + *

+ * Android caches only the value of the key + * AccountManager.KEY_AUTHTOKEN, so if a caller needs the other + * keys in this bundle, it needs to invalidate the token (so that the bundle + * is re-generated). + * + * @param context + * Android context. + * @param account + * Android account. + * @return a Bundle instance containing a subset of the following + * keys: (caller's must check for missing keys) + *

    + *
  • AccountManager.KEY_ACCOUNT_TYPE: the Android + * Account's type
  • + * + *
  • AccountManager.KEY_ACCOUNT_NAME: the Android + * Account's name
  • + * + *
  • AccountManager.KEY_AUTHTOKEN: the Sync account's + * password
  • + * + *
  • Constants.OPTION_USERNAME: the Sync account's + * hashed username
  • + * + *
  • Constants.OPTION_SERVER: the Sync account's + * server
  • + * + *
  • Constants.OPTION_SYNCKEY: the Sync account's + * sync key
  • + * + *
+ * @throws NetworkErrorException + */ + public static Bundle getPlainAuthToken(final Context context, final Account account) + throws NetworkErrorException { + // Extract the username and password from the Account Manager, and ask + // the server for an appropriate AuthToken. + final AccountManager am = AccountManager.get(context); + final String password = am.getPassword(account); + if (password == null) { + Logger.warn(LOG_TAG, "Returning null bundle for getPlainAuthToken since Account password is null."); + return null; + } + + final Bundle result = new Bundle(); + + // This is a Sync account. + result.putString(AccountManager.KEY_ACCOUNT_TYPE, GlobalConstants.ACCOUNTTYPE_SYNC); + + // Server. + String serverURL = am.getUserData(account, Constants.OPTION_SERVER); + result.putString(Constants.OPTION_SERVER, serverURL); + + // Full username, before hashing. + result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); + + // Username after hashing. + try { + String username = Utils.usernameFromAccount(account.name); + Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username + "."); + Logger.debug(LOG_TAG, "Setting username. Null? " + (username == null)); + result.putString(Constants.OPTION_USERNAME, username); + } catch (NoSuchAlgorithmException e) { + // Do nothing. Calling code must check for missing value. + } catch (UnsupportedEncodingException e) { + // Do nothing. Calling code must check for missing value. + } + + // Sync key. + final String syncKey = am.getUserData(account, Constants.OPTION_SYNCKEY); + Logger.debug(LOG_TAG, "Setting sync key. Null? " + (syncKey == null)); + result.putString(Constants.OPTION_SYNCKEY, syncKey); + + // Password. + result.putString(AccountManager.KEY_AUTHTOKEN, password); + return result; + } + private static class SyncAccountAuthenticator extends AbstractAccountAuthenticator { private Context mContext; public SyncAccountAuthenticator(Context context) { @@ -93,53 +174,14 @@ public class SyncAuthenticatorService extends Service { Account account, String authTokenType, Bundle options) throws NetworkErrorException { Logger.debug(LOG_TAG, "getAuthToken()"); - if (!authTokenType.equals(Constants.AUTHTOKEN_TYPE_PLAIN)) { - final Bundle result = new Bundle(); - result.putString(AccountManager.KEY_ERROR_MESSAGE, - "invalid authTokenType"); - return result; + + if (Constants.AUTHTOKEN_TYPE_PLAIN.equals(authTokenType)) { + return getPlainAuthToken(mContext, account); } - // Extract the username and password from the Account Manager, and ask - // the server for an appropriate AuthToken. - final AccountManager am = AccountManager.get(mContext); - final String password = am.getPassword(account); - if (password != null) { - final Bundle result = new Bundle(); - - // This is a Sync account. - result.putString(AccountManager.KEY_ACCOUNT_TYPE, GlobalConstants.ACCOUNTTYPE_SYNC); - - // Server. - String serverURL = am.getUserData(account, Constants.OPTION_SERVER); - result.putString(Constants.OPTION_SERVER, serverURL); - - // Full username, before hashing. - result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); - - // Username after hashing. - try { - String username = Utils.usernameFromAccount(account.name); - Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username + "."); - Logger.debug(LOG_TAG, "Setting username. Null? " + (username == null)); - result.putString(Constants.OPTION_USERNAME, username); - } catch (NoSuchAlgorithmException e) { - // Do nothing. Calling code must check for missing value. - } catch (UnsupportedEncodingException e) { - // Do nothing. Calling code must check for missing value. - } - - // Sync key. - final String syncKey = am.getUserData(account, Constants.OPTION_SYNCKEY); - Logger.debug(LOG_TAG, "Setting sync key. Null? " + (syncKey == null)); - result.putString(Constants.OPTION_SYNCKEY, syncKey); - - // Password. - result.putString(AccountManager.KEY_AUTHTOKEN, password); - return result; - } - Logger.warn(LOG_TAG, "Returning null bundle for getAuthToken."); - return null; + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType"); + return result; } @Override diff --git a/mobile/android/base/sync/setup/activities/SendTabActivity.java b/mobile/android/base/sync/setup/activities/SendTabActivity.java index e68beeb22934..574c32d391a1 100644 --- a/mobile/android/base/sync/setup/activities/SendTabActivity.java +++ b/mobile/android/base/sync/setup/activities/SendTabActivity.java @@ -9,13 +9,18 @@ import java.util.List; import org.mozilla.gecko.R; import org.mozilla.gecko.sync.CommandProcessor; import org.mozilla.gecko.sync.CommandRunner; +import org.mozilla.gecko.sync.CredentialException; import org.mozilla.gecko.sync.GlobalConstants; import org.mozilla.gecko.sync.GlobalSession; import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.SyncConfiguration; +import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.repositories.NullCursorException; import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor; import org.mozilla.gecko.sync.repositories.domain.ClientRecord; import org.mozilla.gecko.sync.setup.Constants; +import org.mozilla.gecko.sync.setup.SyncAccounts; +import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters; import org.mozilla.gecko.sync.stage.SyncClientsEngineStage; import org.mozilla.gecko.sync.syncadapter.SyncAdapter; @@ -24,6 +29,7 @@ import android.accounts.AccountManager; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; @@ -106,17 +112,47 @@ public class SendTabActivity extends Activity { * @return Return null if there is no account set up. Return the account GUID otherwise. */ private String getAccountGUID() { - if (accountManager == null || localAccount == null) { + if (localAccount == null) { + Logger.warn(LOG_TAG, "Null local account; aborting."); + return null; + } + + SyncAccountParameters params; + try { + params = SyncAccounts.blockingFromAndroidAccountV0(this, accountManager, localAccount); + } catch (CredentialException e) { + Logger.warn(LOG_TAG, "Could not get sync account parameters; aborting."); + return null; + } + + SharedPreferences prefs; + try { + final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; + final String profile = Constants.DEFAULT_PROFILE; + final long version = SyncConfiguration.CURRENT_PREFS_VERSION; + prefs = Utils.getSharedPreferences(getApplicationContext(), product, params.username, params.serverURL, profile, version); + return prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null); + } catch (Exception e) { return null; } - return accountManager.getUserData(localAccount, Constants.ACCOUNT_GUID); } public void sendClickHandler(View view) { Logger.info(LOG_TAG, "Send was clicked."); Bundle extras = this.getIntent().getExtras(); + if (extras == null) { + Logger.warn(LOG_TAG, "extras was null; aborting without sending tab."); + notifyAndFinish(false); + return; + } + final String uri = extras.getString(Intent.EXTRA_TEXT); final String title = extras.getString(Intent.EXTRA_SUBJECT); + final List guids = arrayAdapter.getCheckedGUIDs(); + + if (title == null) { + Logger.warn(LOG_TAG, "title was null; ignoring and sending tab anyway."); + } if (uri == null) { Logger.warn(LOG_TAG, "uri was null; aborting without sending tab."); @@ -124,37 +160,43 @@ public class SendTabActivity extends Activity { return; } - if (title == null) { - Logger.warn(LOG_TAG, "title was null; ignoring and sending tab anyway."); - } - - final String clientGUID = getAccountGUID(); - final List guids = arrayAdapter.getCheckedGUIDs(); - - if (clientGUID == null || guids == null) { + if (guids == null) { // Should never happen. - Logger.warn(LOG_TAG, "clientGUID? " + (clientGUID == null) + " or guids? " + (guids == null) + - " was null; aborting without sending tab."); + Logger.warn(LOG_TAG, "guids was null; aborting without sending tab."); notifyAndFinish(false); return; } - // Perform tab sending on another thread. - new Thread() { + // Fetching local client GUID hits the DB, and we want to update the UI + // afterward, so we perform the tab sending on another thread. + new AsyncTask() { + @Override - public void run() { + protected Boolean doInBackground(Void... params) { final CommandProcessor processor = CommandProcessor.getProcessor(); + final String accountGUID = getAccountGUID(); + Logger.debug(LOG_TAG, "Retrieved local account GUID '" + accountGUID + "'."); + if (accountGUID == null) { + return false; + } + for (String guid : guids) { - processor.sendURIToClientForDisplay(uri, guid, title, clientGUID, getApplicationContext()); + processor.sendURIToClientForDisplay(uri, guid, title, accountGUID, getApplicationContext()); } Logger.info(LOG_TAG, "Requesting immediate clients stage sync."); SyncAdapter.requestImmediateSync(localAccount, new String[] { SyncClientsEngineStage.COLLECTION_NAME }); - } - }.start(); - notifyAndFinish(true); + return true; + } + + @Override + protected void onPostExecute(final Boolean success) { + // We're allowed to update the UI from here. + notifyAndFinish(success.booleanValue()); + } + }.execute(); } /** diff --git a/mobile/android/base/sync/syncadapter/SyncAdapter.java b/mobile/android/base/sync/syncadapter/SyncAdapter.java index 98bc0cb952ba..1ebd8d614e40 100644 --- a/mobile/android/base/sync/syncadapter/SyncAdapter.java +++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java @@ -7,11 +7,12 @@ package org.mozilla.gecko.sync.syncadapter; import java.io.IOException; import java.net.URI; import java.security.NoSuchAlgorithmException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.json.simple.parser.ParseException; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.AlreadySyncingException; +import org.mozilla.gecko.sync.CredentialException; import org.mozilla.gecko.sync.GlobalConstants; import org.mozilla.gecko.sync.GlobalSession; import org.mozilla.gecko.sync.Logger; @@ -34,8 +35,6 @@ import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.content.AbstractThreadedSyncAdapter; @@ -48,21 +47,14 @@ import android.content.SyncResult; import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteException; import android.os.Bundle; -import android.os.Handler; public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSessionCallback, ClientsDataDelegate { private static final String LOG_TAG = "SyncAdapter"; - private static final String PREFS_EARLIEST_NEXT_SYNC = "earliestnextsync"; - private static final String PREFS_INVALIDATE_AUTH_TOKEN = "invalidateauthtoken"; - private static final String PREFS_CLUSTER_URL_IS_STALE = "clusterurlisstale"; - - private static final int SHARED_PREFERENCES_MODE = 0; private static final int BACKOFF_PAD_SECONDS = 5; public static final int MULTI_DEVICE_INTERVAL_MILLISECONDS = 5 * 60 * 1000; // 5 minutes. public static final int SINGLE_DEVICE_INTERVAL_MILLISECONDS = 24 * 60 * 60 * 1000; // 24 hours. - private final AccountManager mAccountManager; private final Context mContext; protected long syncStartTimestamp; @@ -70,60 +62,43 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe public SyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); mContext = context; - mAccountManager = AccountManager.get(context); - } - - public static SharedPreferences getGlobalPrefs(Context context) { - return context.getSharedPreferences("sync.prefs.global", SHARED_PREFERENCES_MODE); - } - - public static void purgeGlobalPrefs(Context context) { - getGlobalPrefs(context).edit().clear().commit(); } /** * Backoff. */ public synchronized long getEarliestNextSync() { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - return sharedPreferences.getLong(PREFS_EARLIEST_NEXT_SYNC, 0); + return accountSharedPreferences.getLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, 0); } + public synchronized void setEarliestNextSync(long next) { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - Editor edit = sharedPreferences.edit(); - edit.putLong(PREFS_EARLIEST_NEXT_SYNC, next); + Editor edit = accountSharedPreferences.edit(); + edit.putLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, next); edit.commit(); } + public synchronized void extendEarliestNextSync(long next) { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - if (sharedPreferences.getLong(PREFS_EARLIEST_NEXT_SYNC, 0) >= next) { + if (accountSharedPreferences.getLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, 0) >= next) { return; } - Editor edit = sharedPreferences.edit(); - edit.putLong(PREFS_EARLIEST_NEXT_SYNC, next); + Editor edit = accountSharedPreferences.edit(); + edit.putLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, next); edit.commit(); } - public synchronized boolean getShouldInvalidateAuthToken() { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - return sharedPreferences.getBoolean(PREFS_INVALIDATE_AUTH_TOKEN, false); - } - public synchronized void clearShouldInvalidateAuthToken() { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - Editor edit = sharedPreferences.edit(); - edit.remove(PREFS_INVALIDATE_AUTH_TOKEN); - edit.commit(); - } - public synchronized void setShouldInvalidateAuthToken() { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - Editor edit = sharedPreferences.edit(); - edit.putBoolean(PREFS_INVALIDATE_AUTH_TOKEN, true); - edit.commit(); - } - - private void handleException(Exception e, SyncResult syncResult) { - setShouldInvalidateAuthToken(); + /** + * Handle an exception: update stats, invalidate auth token, log errors, etc. + * + * @param globalSession + * current global session, or null. + * @param e + * Exception to handle. + */ + protected void processException(final GlobalSession globalSession, final Exception e) { try { + // Just in case, invalidate auth token. + SyncAccounts.invalidateAuthToken(AccountManager.get(mContext), localAccount); + if (e instanceof SQLiteConstraintException) { Logger.error(LOG_TAG, "Constraint exception. Aborting sync.", e); syncResult.stats.numParseExceptions++; // This is as good as we can do. @@ -149,27 +124,50 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe e.printStackTrace(); return; } - syncResult.stats.numIoExceptions++; + + // Blanket stats updating for SyncException subclasses. + if (e instanceof SyncException) { + ((SyncException) e).updateStats(globalSession, syncResult); + } else { + // Generic exception. + syncResult.stats.numIoExceptions++; + } + + if (e instanceof CredentialException.MissingAllCredentialsException) { + // This is bad: either we couldn't fetch credentials, or the credentials + // were totally blank. Most likely the user has two copies of Firefox + // installed, and something is misbehaving. + // Either way, disable this account. + if (localAccount == null) { + // Should not happen, but be safe. + Logger.error(LOG_TAG, "No credentials attached to account. Aborting sync."); + return; + } + + Logger.error(LOG_TAG, "No credentials attached to account " + localAccount.name + ". Aborting sync."); + try { + SyncAccounts.setSyncAutomatically(localAccount, false); + } catch (Exception ex) { + Logger.error(LOG_TAG, "Unable to disable account " + localAccount.name + ".", ex); + } + return; + } + + if (e instanceof CredentialException.MissingCredentialException) { + Logger.error(LOG_TAG, "Credentials attached to account, but missing " + + ((CredentialException.MissingCredentialException) e).missingCredential + ". Aborting sync."); + return; + } + + if (e instanceof CredentialException) { + Logger.error(LOG_TAG, "Credentials attached to account were bad."); + return; + } + + // Generic exception. + Logger.error(LOG_TAG, "Unknown exception. Aborting sync.", e); + } catch (Exception ex) { Logger.error(LOG_TAG, "Unknown exception. Aborting sync.", e); - } finally { - notifyMonitor(); - } - } - - private AccountManagerFuture getAuthToken(final Account account, - AccountManagerCallback callback, - Handler handler) { - return mAccountManager.getAuthToken(account, Constants.AUTHTOKEN_TYPE_PLAIN, true, callback, handler); - } - - private void invalidateAuthToken(Account account) { - AccountManagerFuture future = getAuthToken(account, null, null); - String token; - try { - token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN); - mAccountManager.invalidateAuthToken(GlobalConstants.ACCOUNTTYPE_SYNC, token); - } catch (Exception e) { - Logger.error(LOG_TAG, "Couldn't invalidate auth token: " + e); } } @@ -187,6 +185,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe public Account localAccount; protected boolean thisSyncIsForced = false; + public SharedPreferences accountSharedPreferences; /** * Return the number of milliseconds until we're allowed to sync again, @@ -277,112 +276,86 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe this.syncResult = syncResult; this.localAccount = account; - Logger.info(LOG_TAG, - "Syncing account named " + account.name + - " for client named '" + getClientName() + - "' with client guid " + getAccountGUID() + - " (sync account has " + getClientsCount() + " clients)."); - - thisSyncIsForced = (extras != null) && (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)); - long delay = delayMilliseconds(); - if (delay > 0) { - if (thisSyncIsForced) { - Logger.info(LOG_TAG, "Forced sync: overruling remaining backoff of " + delay + "ms."); - } else { - Logger.info(LOG_TAG, "Not syncing: must wait another " + delay + "ms."); - long remainingSeconds = delay / 1000; - syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS; - return; - } + SyncAccountParameters params; + try { + params = SyncAccounts.blockingFromAndroidAccountV0(mContext, AccountManager.get(mContext), this.localAccount); + } catch (Exception e) { + // Updates syncResult and (harmlessly) calls notifyMonitor(). + processException(null, e); + return; } - // TODO: don't clear the auth token unless we have a sync error. - Logger.debug(LOG_TAG, "Got onPerformSync. Extras bundle is " + extras); - - // TODO: don't always invalidate; use getShouldInvalidateAuthToken. - // However, this fixes Bug 716815, so it'll do for now. - Logger.trace(LOG_TAG, "Invalidating auth token."); - invalidateAuthToken(account); + // params and the following fields are non-null at this point. + final String username = params.username; // Encoded with Utils.usernameFromAccount. + final String password = params.password; + final String serverURL = params.serverURL; + final String syncKey = params.syncKey; + final AtomicBoolean setNextSync = new AtomicBoolean(true); final SyncAdapter self = this; - final AccountManagerCallback callback = new AccountManagerCallback() { + final Runnable runnable = new Runnable() { @Override - public void run(AccountManagerFuture future) { + public void run() { Logger.trace(LOG_TAG, "AccountManagerCallback invoked."); // TODO: N.B.: Future must not be used on the main thread. try { - Bundle bundle = future.getResult(60L, TimeUnit.SECONDS); - if (bundle.containsKey("KEY_INTENT")) { - Logger.warn(LOG_TAG, "KEY_INTENT included in AccountManagerFuture bundle. Problem?"); - } - String username = bundle.getString(Constants.OPTION_USERNAME); - String syncKey = bundle.getString(Constants.OPTION_SYNCKEY); - String serverURL = bundle.getString(Constants.OPTION_SERVER); - String password = bundle.getString(AccountManager.KEY_AUTHTOKEN); + Logger.info(LOG_TAG, "Syncing account named " + account.name + + " for authority " + authority + "."); + + // We dump this information right away to help with debugging. Logger.debug(LOG_TAG, "Username: " + username); Logger.debug(LOG_TAG, "Server: " + serverURL); - Logger.debug(LOG_TAG, "Password? " + (password != null)); - Logger.debug(LOG_TAG, "Key? " + (syncKey != null)); - - if (password == null && - username == null && - syncKey == null && - serverURL == null) { - - // Totally blank. Most likely the user has two copies of Firefox - // installed, and something is misbehaving. - // Disable this account. - Logger.error(LOG_TAG, "No credentials attached to account. Aborting sync."); - try { - SyncAccounts.setSyncAutomatically(account, false); - } catch (Exception e) { - Logger.error(LOG_TAG, "Unable to disable account " + account.name + " for " + authority + ".", e); - } - syncResult.stats.numAuthExceptions++; - localAccount = null; - notifyMonitor(); - return; - } - - // Now catch the individual cases. - if (password == null) { - Logger.error(LOG_TAG, "No password: aborting sync."); - syncResult.stats.numAuthExceptions++; - notifyMonitor(); - return; - } - - if (syncKey == null) { - Logger.error(LOG_TAG, "No Sync Key: aborting sync."); - syncResult.stats.numAuthExceptions++; - notifyMonitor(); - return; + if (Logger.LOG_PERSONAL_INFORMATION) { + Logger.debug(LOG_TAG, "Password: " + password); + Logger.debug(LOG_TAG, "Sync key: " + syncKey); + } else { + Logger.debug(LOG_TAG, "Password? " + (password != null)); + Logger.debug(LOG_TAG, "Sync key? " + (syncKey != null)); } // Support multiple accounts by mapping each server/account pair to a branch of the // shared preferences space. - String prefsPath = Utils.getPrefsPath(username, serverURL); + final String product = GlobalConstants.BROWSER_INTENT_PACKAGE; + final String profile = Constants.DEFAULT_PROFILE; + final long version = SyncConfiguration.CURRENT_PREFS_VERSION; + self.accountSharedPreferences = Utils.getSharedPreferences(mContext, product, username, serverURL, profile, version); + + Logger.info(LOG_TAG, + "Client is named '" + getClientName() + "'" + + ", has client guid " + getAccountGUID() + + ", and has " + getClientsCount() + " clients."); + + thisSyncIsForced = (extras != null) && (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)); + long delay = delayMilliseconds(); + if (delay > 0) { + if (thisSyncIsForced) { + Logger.info(LOG_TAG, "Forced sync: overruling remaining backoff of " + delay + "ms."); + } else { + Logger.info(LOG_TAG, "Not syncing: must wait another " + delay + "ms."); + long remainingSeconds = delay / 1000; + syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS; + setNextSync.set(false); + self.notifyMonitor(); + return; + } + } + + final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version); self.performSync(account, extras, authority, provider, syncResult, username, password, prefsPath, serverURL, syncKey); } catch (Exception e) { - self.handleException(e, syncResult); + self.processException(null, e); + notifyMonitor(); return; } } }; - final Handler handler = null; - final Runnable fetchAuthToken = new Runnable() { - @Override - public void run() { - getAuthToken(account, callback, handler); - } - }; synchronized (syncMonitor) { // Perform the work in a new thread from within this synchronized block, // which allows us to be waiting on the monitor before the callback can // notify us in a failure case. Oh, concurrent programming. - new Thread(fetchAuthToken).start(); + new Thread(runnable).start(); // Start our stale connection monitor thread. ConnectionMonitorThread stale = new ConnectionMonitorThread(); @@ -391,10 +364,13 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe Logger.trace(LOG_TAG, "Waiting on sync monitor."); try { syncMonitor.wait(); - long interval = getSyncInterval(); - long next = System.currentTimeMillis() + interval; - Logger.info(LOG_TAG, "Setting minimum next sync time to " + next + " (" + interval + "ms from now)."); - extendEarliestNextSync(next); + + if (setNextSync.get()) { + long interval = getSyncInterval(); + long next = System.currentTimeMillis() + interval; + Logger.info(LOG_TAG, "Setting minimum next sync time to " + next + " (" + interval + "ms from now)."); + extendEarliestNextSync(next); + } Logger.info(LOG_TAG, "Sync took " + Utils.formatDuration(syncStartTimestamp, System.currentTimeMillis()) + "."); } catch (InterruptedException e) { Logger.warn(LOG_TAG, "Waiting on sync monitor interrupted.", e); @@ -403,7 +379,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe stale.shutdown(); } } - } + } public int getSyncInterval() { // Must have been a problem that means we can't access the Account. @@ -419,10 +395,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe return MULTI_DEVICE_INTERVAL_MILLISECONDS; } - /** * Now that we have a sync key and password, go ahead and do the work. - * @param prefsPath TODO * @throws NoSuchAlgorithmException * @throws IllegalArgumentException * @throws SyncConfigurationException @@ -457,7 +431,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe */ try { // Constructor can throw on nulls, which should not happen -- but let's be safe. - final SyncAccountParameters params = new SyncAccountParameters(mContext, mAccountManager, + final SyncAccountParameters params = new SyncAccountParameters(mContext, null, account.name, // Un-encoded, like "test@mozilla.com". syncKey, password, @@ -503,9 +477,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe // Implementing GlobalSession callbacks. @Override public void handleError(GlobalSession globalSession, Exception ex) { - Logger.info(LOG_TAG, "GlobalSession indicated error. Flagging auth token as invalid, just in case."); - setShouldInvalidateAuthToken(); - this.updateStats(globalSession, ex); + Logger.info(LOG_TAG, "GlobalSession indicated error."); + this.processException(globalSession, ex); notifyMonitor(); } @@ -515,22 +488,6 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe notifyMonitor(); } - /** - * Introspect the exception, incrementing the appropriate stat counters. - * TODO: increment number of inserts, deletes, conflicts. - * - * @param globalSession - * @param ex - */ - private void updateStats(GlobalSession globalSession, - Exception ex) { - if (ex instanceof SyncException) { - ((SyncException) ex).updateStats(globalSession, syncResult); - } - // TODO: non-SyncExceptions. - // TODO: wouldn't it be nice to update stats for *every* exception we get? - } - @Override public void handleSuccess(GlobalSession globalSession) { Logger.info(LOG_TAG, "GlobalSession indicated success."); @@ -547,37 +504,28 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe @Override public synchronized String getAccountGUID() { - String accountGUID = mAccountManager.getUserData(localAccount, Constants.ACCOUNT_GUID); + String accountGUID = accountSharedPreferences.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null); if (accountGUID == null) { Logger.debug(LOG_TAG, "Account GUID was null. Creating a new one."); accountGUID = Utils.generateGuid(); - setAccountGUID(mAccountManager, localAccount, accountGUID); + accountSharedPreferences.edit().putString(SyncConfiguration.PREF_ACCOUNT_GUID, accountGUID).commit(); } return accountGUID; } - public static void setAccountGUID(AccountManager accountManager, Account account, String accountGUID) { - accountManager.setUserData(account, Constants.ACCOUNT_GUID, accountGUID); - } - @Override public synchronized String getClientName() { - String clientName = mAccountManager.getUserData(localAccount, Constants.CLIENT_NAME); + String clientName = accountSharedPreferences.getString(SyncConfiguration.PREF_CLIENT_NAME, null); if (clientName == null) { clientName = GlobalConstants.PRODUCT_NAME + " on " + android.os.Build.MODEL; - setClientName(mAccountManager, localAccount, clientName); + accountSharedPreferences.edit().putString(SyncConfiguration.PREF_CLIENT_NAME, clientName).commit(); } return clientName; } - public static void setClientName(AccountManager accountManager, Account account, String clientName) { - accountManager.setUserData(account, Constants.CLIENT_NAME, clientName); - } - @Override public synchronized void setClientsCount(int clientsCount) { - mAccountManager.setUserData(localAccount, Constants.NUM_CLIENTS, - Integer.toString(clientsCount)); + accountSharedPreferences.edit().putLong(SyncConfiguration.PREF_NUM_CLIENTS, (long) clientsCount).commit(); } @Override @@ -587,23 +535,16 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe @Override public synchronized int getClientsCount() { - String clientsCount = mAccountManager.getUserData(localAccount, Constants.NUM_CLIENTS); - if (clientsCount == null) { - clientsCount = "0"; - mAccountManager.setUserData(localAccount, Constants.NUM_CLIENTS, clientsCount); - } - return Integer.parseInt(clientsCount); + return (int) accountSharedPreferences.getLong(SyncConfiguration.PREF_NUM_CLIENTS, 0); } public synchronized boolean getClusterURLIsStale() { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - return sharedPreferences.getBoolean(PREFS_CLUSTER_URL_IS_STALE, false); + return accountSharedPreferences.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, false); } public synchronized void setClusterURLIsStale(boolean clusterURLIsStale) { - SharedPreferences sharedPreferences = getGlobalPrefs(mContext); - Editor edit = sharedPreferences.edit(); - edit.putBoolean(PREFS_CLUSTER_URL_IS_STALE, clusterURLIsStale); + Editor edit = accountSharedPreferences.edit(); + edit.putBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, clusterURLIsStale); edit.commit(); } @@ -631,7 +572,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe @Override public void informUpgradeRequiredResponse(final GlobalSession session) { - final AccountManager manager = mAccountManager; + final AccountManager manager = AccountManager.get(mContext); final Account toDisable = localAccount; if (toDisable == null || manager == null) { Logger.warn(LOG_TAG, "Attempting to disable account, but null found."); diff --git a/mobile/android/sync/java-sources.mn b/mobile/android/sync/java-sources.mn index 343857351ffd..c30af8e8e523 100644 --- a/mobile/android/sync/java-sources.mn +++ b/mobile/android/sync/java-sources.mn @@ -3,6 +3,8 @@ sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/config/AccountPickler.java +sync/config/ConfigurationMigrator.java +sync/CredentialException.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java