зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1131421 - Part 1: initial stub reading list service and SyncAdapter. r=nalexander
This commit is contained in:
Родитель
06584886b6
Коммит
ee6ea0102b
|
@ -882,6 +882,7 @@ sync_java_files = [
|
||||||
'fxa/sync/FxAccountNotificationManager.java',
|
'fxa/sync/FxAccountNotificationManager.java',
|
||||||
'fxa/sync/FxAccountSchedulePolicy.java',
|
'fxa/sync/FxAccountSchedulePolicy.java',
|
||||||
'fxa/sync/FxAccountSyncAdapter.java',
|
'fxa/sync/FxAccountSyncAdapter.java',
|
||||||
|
'fxa/sync/FxAccountSyncDelegate.java',
|
||||||
'fxa/sync/FxAccountSyncService.java',
|
'fxa/sync/FxAccountSyncService.java',
|
||||||
'fxa/sync/FxAccountSyncStatusHelper.java',
|
'fxa/sync/FxAccountSyncStatusHelper.java',
|
||||||
'fxa/sync/SchedulePolicy.java',
|
'fxa/sync/SchedulePolicy.java',
|
||||||
|
@ -1149,3 +1150,7 @@ sync_java_files = [
|
||||||
'tokenserver/TokenServerException.java',
|
'tokenserver/TokenServerException.java',
|
||||||
'tokenserver/TokenServerToken.java',
|
'tokenserver/TokenServerToken.java',
|
||||||
]
|
]
|
||||||
|
reading_list_service_java_files = [
|
||||||
|
'reading/ReadingListSyncAdapter.java',
|
||||||
|
'reading/ReadingListSyncService.java',
|
||||||
|
]
|
||||||
|
|
|
@ -45,7 +45,8 @@ import android.os.Bundle;
|
||||||
public class AndroidFxAccount {
|
public class AndroidFxAccount {
|
||||||
protected static final String LOG_TAG = AndroidFxAccount.class.getSimpleName();
|
protected static final String LOG_TAG = AndroidFxAccount.class.getSimpleName();
|
||||||
|
|
||||||
public static final int CURRENT_PREFS_VERSION = 1;
|
public static final int CURRENT_SYNC_PREFS_VERSION = 1;
|
||||||
|
public static final int CURRENT_RL_PREFS_VERSION = 1;
|
||||||
|
|
||||||
// When updating the account, do not forget to update AccountPickler.
|
// When updating the account, do not forget to update AccountPickler.
|
||||||
public static final int CURRENT_ACCOUNT_VERSION = 3;
|
public static final int CURRENT_ACCOUNT_VERSION = 3;
|
||||||
|
@ -241,10 +242,7 @@ public class AndroidFxAccount {
|
||||||
return accountManager.getUserData(account, ACCOUNT_KEY_TOKEN_SERVER);
|
return accountManager.getUserData(account, ACCOUNT_KEY_TOKEN_SERVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private String constructPrefsPath(String product, long version, String extra) throws GeneralSecurityException, UnsupportedEncodingException {
|
||||||
* This needs to return a string because of the tortured prefs access in GlobalSession.
|
|
||||||
*/
|
|
||||||
public String getSyncPrefsPath() throws GeneralSecurityException, UnsupportedEncodingException {
|
|
||||||
String profile = getProfile();
|
String profile = getProfile();
|
||||||
String username = account.name;
|
String username = account.name;
|
||||||
|
|
||||||
|
@ -256,28 +254,44 @@ public class AndroidFxAccount {
|
||||||
throw new IllegalStateException("Missing username. Cannot fetch prefs.");
|
throw new IllegalStateException("Missing username. Cannot fetch prefs.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final String tokenServerURI = getTokenServerURI();
|
|
||||||
if (tokenServerURI == null) {
|
|
||||||
throw new IllegalStateException("No token server URI. Cannot fetch prefs.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final String fxaServerURI = getAccountServerURI();
|
final String fxaServerURI = getAccountServerURI();
|
||||||
if (fxaServerURI == null) {
|
if (fxaServerURI == null) {
|
||||||
throw new IllegalStateException("No account server URI. Cannot fetch prefs.");
|
throw new IllegalStateException("No account server URI. Cannot fetch prefs.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final String product = GlobalConstants.BROWSER_INTENT_PACKAGE + ".fxa";
|
|
||||||
final long version = CURRENT_PREFS_VERSION;
|
|
||||||
|
|
||||||
// This is unique for each syncing 'view' of the account.
|
// This is unique for each syncing 'view' of the account.
|
||||||
final String serverURLThing = fxaServerURI + "!" + tokenServerURI;
|
final String serverURLThing = fxaServerURI + "!" + extra;
|
||||||
return Utils.getPrefsPath(product, username, serverURLThing, profile, version);
|
return Utils.getPrefsPath(product, username, serverURLThing, profile, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This needs to return a string because of the tortured prefs access in GlobalSession.
|
||||||
|
*/
|
||||||
|
public String getSyncPrefsPath() throws GeneralSecurityException, UnsupportedEncodingException {
|
||||||
|
final String tokenServerURI = getTokenServerURI();
|
||||||
|
if (tokenServerURI == null) {
|
||||||
|
throw new IllegalStateException("No token server URI. Cannot fetch prefs.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String product = GlobalConstants.BROWSER_INTENT_PACKAGE + ".fxa";
|
||||||
|
final long version = CURRENT_SYNC_PREFS_VERSION;
|
||||||
|
return constructPrefsPath(product, version, tokenServerURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReadingListPrefsPath() throws GeneralSecurityException, UnsupportedEncodingException {
|
||||||
|
final String product = GlobalConstants.BROWSER_INTENT_PACKAGE + ".reading";
|
||||||
|
final long version = CURRENT_RL_PREFS_VERSION;
|
||||||
|
return constructPrefsPath(product, version, "");
|
||||||
|
}
|
||||||
|
|
||||||
public SharedPreferences getSyncPrefs() throws UnsupportedEncodingException, GeneralSecurityException {
|
public SharedPreferences getSyncPrefs() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||||
return context.getSharedPreferences(getSyncPrefsPath(), Utils.SHARED_PREFERENCES_MODE);
|
return context.getSharedPreferences(getSyncPrefsPath(), Utils.SHARED_PREFERENCES_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SharedPreferences getReadingListPrefs() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||||
|
return context.getSharedPreferences(getReadingListPrefsPath(), Utils.SHARED_PREFERENCES_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a JSON dictionary of the string values associated to this account.
|
* Extract a JSON dictionary of the string values associated to this account.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -86,119 +86,45 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
this.notificationManager = new FxAccountNotificationManager(NOTIFICATION_ID);
|
this.notificationManager = new FxAccountNotificationManager(NOTIFICATION_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class SyncDelegate {
|
protected static class SyncDelegate extends FxAccountSyncDelegate {
|
||||||
protected final CountDownLatch latch;
|
@Override
|
||||||
protected final SyncResult syncResult;
|
public void handleSuccess() {
|
||||||
protected final AndroidFxAccount fxAccount;
|
Logger.info(LOG_TAG, "Sync succeeded.");
|
||||||
|
super.handleSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
Logger.error(LOG_TAG, "Got exception syncing.", e);
|
||||||
|
super.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCannotSync(State finalState) {
|
||||||
|
Logger.warn(LOG_TAG, "Cannot sync from state: " + finalState.getStateLabel());
|
||||||
|
super.handleCannotSync(finalState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postponeSync(long millis) {
|
||||||
|
if (millis <= 0) {
|
||||||
|
Logger.debug(LOG_TAG, "Asked to postpone sync, but zero delay.");
|
||||||
|
}
|
||||||
|
super.postponeSync(millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rejectSync() {
|
||||||
|
super.rejectSync();
|
||||||
|
}
|
||||||
|
|
||||||
protected final Collection<String> stageNamesToSync;
|
protected final Collection<String> stageNamesToSync;
|
||||||
|
|
||||||
public SyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount, Collection<String> stageNamesToSync) {
|
public SyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount, Collection<String> stageNamesToSync) {
|
||||||
if (latch == null) {
|
super(latch, syncResult, fxAccount);
|
||||||
throw new IllegalArgumentException("latch must not be null");
|
|
||||||
}
|
|
||||||
if (syncResult == null) {
|
|
||||||
throw new IllegalArgumentException("syncResult must not be null");
|
|
||||||
}
|
|
||||||
if (fxAccount == null) {
|
|
||||||
throw new IllegalArgumentException("fxAccount must not be null");
|
|
||||||
}
|
|
||||||
this.latch = latch;
|
|
||||||
this.syncResult = syncResult;
|
|
||||||
this.fxAccount = fxAccount;
|
|
||||||
this.stageNamesToSync = Collections.unmodifiableCollection(stageNamesToSync);
|
this.stageNamesToSync = Collections.unmodifiableCollection(stageNamesToSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* No error! Say that we made progress.
|
|
||||||
*/
|
|
||||||
protected void setSyncResultSuccess() {
|
|
||||||
syncResult.stats.numUpdates += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Soft error. Say that we made progress, so that Android will sync us again
|
|
||||||
* after exponential backoff.
|
|
||||||
*/
|
|
||||||
protected void setSyncResultSoftError() {
|
|
||||||
syncResult.stats.numUpdates += 1;
|
|
||||||
syncResult.stats.numIoExceptions += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hard error. We don't want Android to sync us again, even if we make
|
|
||||||
* progress, until the user intervenes.
|
|
||||||
*/
|
|
||||||
protected void setSyncResultHardError() {
|
|
||||||
syncResult.stats.numAuthExceptions += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleSuccess() {
|
|
||||||
Logger.info(LOG_TAG, "Sync succeeded.");
|
|
||||||
setSyncResultSuccess();
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleError(Exception e) {
|
|
||||||
Logger.error(LOG_TAG, "Got exception syncing.", e);
|
|
||||||
setSyncResultSoftError();
|
|
||||||
// This is awful, but we need to propagate bad assertions back up the
|
|
||||||
// chain somehow, and this will do for now.
|
|
||||||
if (e instanceof TokenServerException) {
|
|
||||||
// We should only get here *after* we're locked into the married state.
|
|
||||||
State state = fxAccount.getState();
|
|
||||||
if (state.getStateLabel() == StateLabel.Married) {
|
|
||||||
Married married = (Married) state;
|
|
||||||
fxAccount.setState(married.makeCohabitingState());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the login machine terminates, we might not be in the
|
|
||||||
* <code>Married</code> state, and therefore we can't sync. This method
|
|
||||||
* messages as much to the user.
|
|
||||||
* <p>
|
|
||||||
* To avoid stopping us syncing altogether, we set a soft error rather than
|
|
||||||
* a hard error. In future, we would like to set a hard error if we are in,
|
|
||||||
* for example, the <code>Separated</code> state, and then have some user
|
|
||||||
* initiated activity mark the Android account as ready to sync again. This
|
|
||||||
* is tricky, though, so we play it safe for now.
|
|
||||||
*
|
|
||||||
* @param finalState
|
|
||||||
* that login machine ended in.
|
|
||||||
*/
|
|
||||||
public void handleCannotSync(State finalState) {
|
|
||||||
Logger.warn(LOG_TAG, "Cannot sync from state: " + finalState.getStateLabel());
|
|
||||||
setSyncResultSoftError();
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void postponeSync(long millis) {
|
|
||||||
if (millis <= 0) {
|
|
||||||
Logger.debug(LOG_TAG, "Asked to postpone sync, but zero delay. Short-circuiting.");
|
|
||||||
} else {
|
|
||||||
// delayUntil is broken: https://code.google.com/p/android/issues/detail?id=65669
|
|
||||||
// So we don't bother doing this. Instead, we rely on the periodic sync
|
|
||||||
// we schedule, and the backoff handler for the rest.
|
|
||||||
/*
|
|
||||||
Logger.warn(LOG_TAG, "Postponing sync by " + millis + "ms.");
|
|
||||||
syncResult.delayUntil = millis / 1000;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
setSyncResultSoftError();
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simply don't sync, without setting any error flags.
|
|
||||||
* This is the appropriate behavior when a routine backoff has not yet
|
|
||||||
* been met.
|
|
||||||
*/
|
|
||||||
public void rejectSync() {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<String> getStageNamesToSync() {
|
public Collection<String> getStageNamesToSync() {
|
||||||
return this.stageNamesToSync;
|
return this.stageNamesToSync;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
/* 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.fxa.sync;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
|
import org.mozilla.gecko.fxa.login.Married;
|
||||||
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
|
import org.mozilla.gecko.fxa.login.State.StateLabel;
|
||||||
|
import org.mozilla.gecko.tokenserver.TokenServerException;
|
||||||
|
|
||||||
|
import android.content.SyncResult;
|
||||||
|
|
||||||
|
public class FxAccountSyncDelegate {
|
||||||
|
protected final CountDownLatch latch;
|
||||||
|
protected final SyncResult syncResult;
|
||||||
|
protected final AndroidFxAccount fxAccount;
|
||||||
|
|
||||||
|
public FxAccountSyncDelegate(CountDownLatch latch, SyncResult syncResult, AndroidFxAccount fxAccount) {
|
||||||
|
if (latch == null) {
|
||||||
|
throw new IllegalArgumentException("latch must not be null");
|
||||||
|
}
|
||||||
|
if (syncResult == null) {
|
||||||
|
throw new IllegalArgumentException("syncResult must not be null");
|
||||||
|
}
|
||||||
|
if (fxAccount == null) {
|
||||||
|
throw new IllegalArgumentException("fxAccount must not be null");
|
||||||
|
}
|
||||||
|
this.latch = latch;
|
||||||
|
this.syncResult = syncResult;
|
||||||
|
this.fxAccount = fxAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No error! Say that we made progress.
|
||||||
|
*/
|
||||||
|
protected void setSyncResultSuccess() {
|
||||||
|
syncResult.stats.numUpdates += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soft error. Say that we made progress, so that Android will sync us again
|
||||||
|
* after exponential backoff.
|
||||||
|
*/
|
||||||
|
protected void setSyncResultSoftError() {
|
||||||
|
syncResult.stats.numUpdates += 1;
|
||||||
|
syncResult.stats.numIoExceptions += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hard error. We don't want Android to sync us again, even if we make
|
||||||
|
* progress, until the user intervenes.
|
||||||
|
*/
|
||||||
|
protected void setSyncResultHardError() {
|
||||||
|
syncResult.stats.numAuthExceptions += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleSuccess() {
|
||||||
|
setSyncResultSuccess();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
setSyncResultSoftError();
|
||||||
|
// This is awful, but we need to propagate bad assertions back up the
|
||||||
|
// chain somehow, and this will do for now.
|
||||||
|
if (e instanceof TokenServerException) {
|
||||||
|
// We should only get here *after* we're locked into the married state.
|
||||||
|
State state = fxAccount.getState();
|
||||||
|
if (state.getStateLabel() == StateLabel.Married) {
|
||||||
|
Married married = (Married) state;
|
||||||
|
fxAccount.setState(married.makeCohabitingState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the login machine terminates, we might not be in the
|
||||||
|
* <code>Married</code> state, and therefore we can't sync. This method
|
||||||
|
* messages as much to the user.
|
||||||
|
* <p>
|
||||||
|
* To avoid stopping us syncing altogether, we set a soft error rather than
|
||||||
|
* a hard error. In future, we would like to set a hard error if we are in,
|
||||||
|
* for example, the <code>Separated</code> state, and then have some user
|
||||||
|
* initiated activity mark the Android account as ready to sync again. This
|
||||||
|
* is tricky, though, so we play it safe for now.
|
||||||
|
*
|
||||||
|
* @param finalState
|
||||||
|
* that login machine ended in.
|
||||||
|
*/
|
||||||
|
public void handleCannotSync(State finalState) {
|
||||||
|
setSyncResultSoftError();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postponeSync(long millis) {
|
||||||
|
if (millis > 0) {
|
||||||
|
// delayUntil is broken: https://code.google.com/p/android/issues/detail?id=65669
|
||||||
|
// So we don't bother doing this. Instead, we rely on the periodic sync
|
||||||
|
// we schedule, and the backoff handler for the rest.
|
||||||
|
/*
|
||||||
|
Logger.warn(LOG_TAG, "Postponing sync by " + millis + "ms.");
|
||||||
|
syncResult.delayUntil = millis / 1000;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
setSyncResultSoftError();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simply don't sync, without setting any error flags.
|
||||||
|
* This is the appropriate behavior when a routine backoff has not yet
|
||||||
|
* been met.
|
||||||
|
*/
|
||||||
|
public void rejectSync() {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* 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.reading;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SyncResult;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class ReadingListSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
public ReadingListSyncAdapter(Context context, boolean autoInitialize) {
|
||||||
|
super(context, autoInitialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||||
|
final AndroidFxAccount fxAccount = new AndroidFxAccount(getContext(), account);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* 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.reading;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public class ReadingListSyncService extends Service {
|
||||||
|
private static final Object syncAdapterLock = new Object();
|
||||||
|
private static ReadingListSyncAdapter syncAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
synchronized (syncAdapterLock) {
|
||||||
|
if (syncAdapter == null) {
|
||||||
|
syncAdapter = new ReadingListSyncAdapter(getApplicationContext(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return syncAdapter.getSyncAdapterBinder();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 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/. -->
|
||||||
|
|
||||||
|
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:accountType="@string/moz_android_shared_fxaccount_type"
|
||||||
|
android:contentAuthority="@string/content_authority_db_readinglist"
|
||||||
|
android:isAlwaysSyncable="true"
|
||||||
|
android:supportsUploading="true"
|
||||||
|
android:userVisible="true"
|
||||||
|
/>
|
|
@ -9,6 +9,13 @@
|
||||||
android:name="android.accounts.AccountAuthenticator"
|
android:name="android.accounts.AccountAuthenticator"
|
||||||
android:resource="@xml/fxaccount_authenticator" />
|
android:resource="@xml/fxaccount_authenticator" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:exported="false"
|
||||||
|
android:name="org.mozilla.gecko.fxa.receivers.FxAccountDeletedService" >
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- Firefox Sync. -->
|
||||||
<service
|
<service
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:name="org.mozilla.gecko.fxa.sync.FxAccountSyncService" >
|
android:name="org.mozilla.gecko.fxa.sync.FxAccountSyncService" >
|
||||||
|
@ -20,7 +27,18 @@
|
||||||
android:name="android.content.SyncAdapter"
|
android:name="android.content.SyncAdapter"
|
||||||
android:resource="@xml/fxaccount_syncadapter" />
|
android:resource="@xml/fxaccount_syncadapter" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<!-- Reading List. -->
|
||||||
|
#ifdef MOZ_ANDROID_READING_LIST_SERVICE
|
||||||
<service
|
<service
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:name="org.mozilla.gecko.fxa.receivers.FxAccountDeletedService" >
|
android:name="org.mozilla.gecko.reading.ReadingListSyncService" >
|
||||||
|
<intent-filter >
|
||||||
|
<action android:name="android.content.SyncAdapter" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.content.SyncAdapter"
|
||||||
|
android:resource="@xml/readinglist_syncadapter" />
|
||||||
</service>
|
</service>
|
||||||
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче