1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.chrome.browser.invalidation; 6 7import android.accounts.Account; 8import android.content.Context; 9import android.content.Intent; 10 11import org.chromium.base.ApplicationState; 12import org.chromium.base.ApplicationStatus; 13import org.chromium.base.CalledByNative; 14import org.chromium.base.VisibleForTesting; 15import org.chromium.sync.internal_api.pub.base.ModelType; 16import org.chromium.sync.notifier.InvalidationClientNameProvider; 17import org.chromium.sync.notifier.InvalidationIntentProtocol; 18import org.chromium.sync.notifier.InvalidationPreferences; 19import org.chromium.sync.notifier.InvalidationService; 20import org.chromium.sync.notifier.SyncStatusHelper; 21 22import java.util.Set; 23 24/** 25 * Controller used to send start, stop, and registration-change commands to the invalidation 26 * client library used by Sync. 27 */ 28public class InvalidationController implements ApplicationStatus.ApplicationStateListener { 29 private static final Object LOCK = new Object(); 30 31 private static InvalidationController sInstance; 32 33 private final Context mContext; 34 35 /** 36 * Sets the types for which the client should register for notifications. 37 * 38 * @param account Account of the user. 39 * @param allTypes If {@code true}, registers for all types, and {@code types} is ignored 40 * @param types Set of types for which to register. Ignored if {@code allTypes == true}. 41 */ 42 public void setRegisteredTypes(Account account, boolean allTypes, Set<ModelType> types) { 43 Intent registerIntent = 44 InvalidationIntentProtocol.createRegisterIntent(account, allTypes, types); 45 registerIntent.setClass(mContext, InvalidationService.class); 46 mContext.startService(registerIntent); 47 } 48 49 /** 50 * Reads all stored preferences and calls 51 * {@link #setRegisteredTypes(android.accounts.Account, boolean, java.util.Set)} with the stored 52 * values, refreshing the set of types with {@code types}. It can be used on startup of Chrome 53 * to ensure we always have a set of registrations consistent with the native code. 54 * @param types Set of types for which to register. 55 */ 56 public void refreshRegisteredTypes(Set<ModelType> types) { 57 InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext); 58 Set<String> savedSyncedTypes = invalidationPreferences.getSavedSyncedTypes(); 59 Account account = invalidationPreferences.getSavedSyncedAccount(); 60 boolean allTypes = savedSyncedTypes != null && 61 savedSyncedTypes.contains(ModelType.ALL_TYPES_TYPE); 62 setRegisteredTypes(account, allTypes, types); 63 } 64 65 /** 66 * Sets object ids for which the client should register for notification. This is intended for 67 * registering non-Sync types; Sync types are registered with {@code setRegisteredTypes}. 68 * 69 * @param objectSources The sources of the objects. 70 * @param objectNames The names of the objects. 71 */ 72 @CalledByNative 73 public void setRegisteredObjectIds(int[] objectSources, String[] objectNames) { 74 InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext); 75 Account account = invalidationPreferences.getSavedSyncedAccount(); 76 Intent registerIntent = 77 InvalidationIntentProtocol.createRegisterIntent( 78 account, objectSources, objectNames); 79 registerIntent.setClass(mContext, InvalidationService.class); 80 mContext.startService(registerIntent); 81 } 82 83 /** 84 * Starts the invalidation client. 85 */ 86 public void start() { 87 Intent intent = new Intent(mContext, InvalidationService.class); 88 mContext.startService(intent); 89 } 90 91 /** 92 * Stops the invalidation client. 93 */ 94 public void stop() { 95 Intent intent = new Intent(mContext, InvalidationService.class); 96 intent.putExtra(InvalidationIntentProtocol.EXTRA_STOP, true); 97 mContext.startService(intent); 98 } 99 100 /** 101 * Returns the instance that will use {@code context} to issue intents. 102 * 103 * Calling this method will create the instance if it does not yet exist. 104 */ 105 @CalledByNative 106 public static InvalidationController get(Context context) { 107 synchronized (LOCK) { 108 if (sInstance == null) { 109 sInstance = new InvalidationController(context); 110 } 111 return sInstance; 112 } 113 } 114 115 /** 116 * Creates an instance using {@code context} to send intents. 117 */ 118 @VisibleForTesting 119 InvalidationController(Context context) { 120 Context appContext = context.getApplicationContext(); 121 if (appContext == null) throw new NullPointerException("Unable to get application context"); 122 mContext = appContext; 123 ApplicationStatus.registerApplicationStateListener(this); 124 } 125 126 @Override 127 public void onApplicationStateChange(int newState) { 128 if (SyncStatusHelper.get(mContext).isSyncEnabled()) { 129 if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) { 130 start(); 131 } else if (newState == ApplicationState.HAS_PAUSED_ACTIVITIES) { 132 stop(); 133 } 134 } 135 } 136 137 /** 138 * Fetches the Invalidator client name. 139 * 140 * Note that there is a naming discrepancy here. In C++, we refer to the invalidation client 141 * identifier that is unique for every invalidation client instance in an account as the client 142 * ID. In Java, we call it the client name. 143 */ 144 @CalledByNative 145 public byte[] getInvalidatorClientId() { 146 return InvalidationClientNameProvider.get().getInvalidatorClientName(); 147 } 148} 149