diff options
24 files changed, 465 insertions, 367 deletions
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 5083c43..fcf8a1f 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn @@ -315,6 +315,7 @@ android_library("chrome_shared_test_java") { "//third_party/jsr-305:jsr_305_javalib", "//ui/android:ui_java", "//ui/android:ui_javatests", + google_play_services_library, ] } diff --git a/chrome/android/chrome_apk.gyp b/chrome/android/chrome_apk.gyp index d643a9d..e1cf10c 100644 --- a/chrome/android/chrome_apk.gyp +++ b/chrome/android/chrome_apk.gyp @@ -295,6 +295,7 @@ '../../net/net.gyp:net_java_test_support', '../../sync/sync.gyp:sync_java_test_support', '../../third_party/android_tools/android_tools.gyp:android_support_v7_appcompat_javalib', + '../../third_party/android_tools/android_tools.gyp:google_play_services_javalib', '../../ui/android/ui_android.gyp:ui_javatests', ], 'includes': [ '../../build/java.gypi' ], diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 7525c59..537315e 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml @@ -45,6 +45,7 @@ by a child template that "extends" this file. <uses-permission android:name="android.permission.NFC"/> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.READ_SYNC_STATS"/> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.USE_CREDENTIALS"/> <uses-permission android:name="android.permission.VIBRATE"/> @@ -563,14 +564,14 @@ by a child template that "extends" this file. </intent-filter> </receiver> - <!-- Service Worker Background Sync service listener --> - <service android:name="org.chromium.content.browser.BackgroundSyncLauncherService" - android:exported="false" /> - <receiver android:name="org.chromium.content.browser.BackgroundSyncLauncherService$Receiver"> + <!-- Service Worker Background Sync GCM scheduler task --> + <service android:name="org.chromium.chrome.browser.BackgroundSyncLauncherService" + android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" + android:exported="true"> <intent-filter> - <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> + <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" /> </intent-filter> - </receiver> + </service> <service android:name="org.chromium.chrome.browser.prerender.ChromePrerenderService" android:exported="true" diff --git a/chrome/android/java/proguard.flags b/chrome/android/java/proguard.flags index 42f25bd..17a8e8b 100644 --- a/chrome/android/java/proguard.flags +++ b/chrome/android/java/proguard.flags @@ -113,6 +113,10 @@ *; } +-keepnames class com.google.android.gms.gcm.** { + *; +} + -keepnames class jp.tomorrowkey.android.gifplayer.** { public *; } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java new file mode 100644 index 0000000..944575f --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java @@ -0,0 +1,201 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.preference.PreferenceManager; + +import com.google.android.gms.gcm.GcmNetworkManager; +import com.google.android.gms.gcm.OneoffTask; +import com.google.android.gms.gcm.Task; + +import org.chromium.base.VisibleForTesting; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; +import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler; + +/** + * The {@link BackgroundSyncLauncher} singleton is created and owned by the C++ browser. It + * registers interest in waking up the browser the next time the device goes online after the + * browser closes via the {@link #setLaunchWhenNextOnline} method. + * + * Thread model: This class is to be run on the UI thread only. + */ +public class BackgroundSyncLauncher { + static final String PREF_BACKGROUND_SYNC_LAUNCH_NEXT_ONLINE = "bgsync_launch_next_online"; + // The instance of BackgroundSyncLauncher currently owned by a C++ + // BackgroundSyncLauncherAndroid, if any. If it is non-null then the browser is running. + private static BackgroundSyncLauncher sInstance; + + private GcmNetworkManager mScheduler; + + /** + * Disables the automatic use of the GCMNetworkManager. When disabled, the methods which + * interact with GCM can still be used, but will not be called automatically on creation, or by + * {@link #launchBrowserWhenNextOnlineIfStopped}. + * + * Automatic GCM use is disabled by tests, and also by this class if it is determined on + * creation that the installed Play Services library is out of date. + */ + private static boolean sGCMEnabled = true; + + /** + * Create a BackgroundSyncLauncher object, which is owned by C++. + * @param context The app context. + */ + @VisibleForTesting + @CalledByNative + protected static BackgroundSyncLauncher create(Context context) { + if (sInstance != null) { + throw new IllegalStateException("Already instantiated"); + } + + sInstance = new BackgroundSyncLauncher(context); + return sInstance; + } + + /** + * Called when the C++ counterpart is deleted. + */ + @VisibleForTesting + @CalledByNative + protected void destroy() { + assert sInstance == this; + sInstance = null; + } + + /** + * Callback for {@link #shouldLaunchWhenNextOnline}. The run method is invoked on the UI thread. + */ + public static interface ShouldLaunchCallback { public void run(Boolean shouldLaunch); } + + /** + * Returns whether the browser should be launched when the device next goes online. + * This is set by C++ and reset to false each time {@link BackgroundSyncLauncher}'s singleton is + * created (the native browser is started). This call is asynchronous and will run the callback + * on the UI thread when complete. + * @param context The application context. + * @param sharedPreferences The shared preferences. + */ + protected static void shouldLaunchWhenNextOnline( + final Context context, final ShouldLaunchCallback callback) { + new AsyncTask<Void, Void, Boolean>() { + @Override + protected Boolean doInBackground(Void... params) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(PREF_BACKGROUND_SYNC_LAUNCH_NEXT_ONLINE, false); + } + @Override + protected void onPostExecute(Boolean shouldLaunch) { + callback.run(shouldLaunch); + } + }.execute(); + } + + /** + * Manages the scheduled tasks which re-launch the browser when the device next goes online. + * This method is called by C++ as background sync registrations are added and removed. When the + * {@link BackgroundSyncLauncher} singleton is created (on browser start), this is called to + * remove any pre-existing scheduled tasks. + */ + @VisibleForTesting + @CalledByNative + protected void launchBrowserWhenNextOnlineIfStopped( + final Context context, final boolean shouldLaunch) { + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit() + .putBoolean(PREF_BACKGROUND_SYNC_LAUNCH_NEXT_ONLINE, shouldLaunch) + .apply(); + return null; + } + @Override + protected void onPostExecute(Void params) { + if (sGCMEnabled) { + if (shouldLaunch) { + scheduleLaunchTask(context, mScheduler); + } else { + removeScheduledTasks(mScheduler); + } + } + } + }.execute(); + } + + /** + * True if the native browser has started and created an instance of {@link + * BackgroundSyncLauncher}. + */ + protected static boolean hasInstance() { + return sInstance != null; + } + + protected BackgroundSyncLauncher(Context context) { + // Check to see if Play Services is up to date, and disable GCM if not. + // This will not automatically set {@link sGCMEnabled} to true, in case it has been disabled + // in tests. + if (sGCMEnabled && !canUseGooglePlayServices(context)) { + setGCMEnabled(false); + } + mScheduler = GcmNetworkManager.getInstance(context); + launchBrowserWhenNextOnlineIfStopped(context, false); + } + + private boolean canUseGooglePlayServices(Context context) { + return ExternalAuthUtils.getInstance().canUseGooglePlayServices( + context, new UserRecoverableErrorHandler.Silent()); + } + + private static void scheduleLaunchTask(Context context, GcmNetworkManager scheduler) { + // Google Play Services may not be up to date, if the application was not installed through + // the Play Store. In this case, scheduling the task will fail silently. + OneoffTask oneoff = new OneoffTask.Builder() + .setService(BackgroundSyncLauncherService.class) + .setTag("BackgroundSync Event") + // We have to set a non-zero execution window here + .setExecutionWindow(0, 1) + .setRequiredNetwork(Task.NETWORK_STATE_CONNECTED) + .setPersisted(true) + .setUpdateCurrent(true) + .build(); + scheduler.schedule(oneoff); + } + + private static void removeScheduledTasks(GcmNetworkManager scheduler) { + scheduler.cancelAllTasks(BackgroundSyncLauncherService.class); + } + + /** + * Reschedule any required background sync tasks, if they have been removed due to an + * application upgrade. + * + * This method checks the saved preferences, and reschedules the sync tasks as appropriate + * to match the preferences. + * This method is static so that it can be run without actually instantiating a + * BackgroundSyncLauncher. + */ + protected static void rescheduleTasksOnUpgrade(final Context context) { + final GcmNetworkManager scheduler = GcmNetworkManager.getInstance(context); + BackgroundSyncLauncher.ShouldLaunchCallback callback = + new BackgroundSyncLauncher.ShouldLaunchCallback() { + @Override + public void run(Boolean shouldLaunch) { + if (shouldLaunch) { + scheduleLaunchTask(context, scheduler); + } + } + }; + BackgroundSyncLauncher.shouldLaunchWhenNextOnline(context, callback); + } + + @VisibleForTesting + static void setGCMEnabled(boolean enabled) { + sGCMEnabled = enabled; + } +} diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncherService.java b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncherService.java new file mode 100644 index 0000000..3fcd324 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncherService.java @@ -0,0 +1,69 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser; + +import android.content.Context; + +import com.google.android.gms.gcm.GcmNetworkManager; +import com.google.android.gms.gcm.GcmTaskService; +import com.google.android.gms.gcm.TaskParams; + +import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; +import org.chromium.base.VisibleForTesting; +import org.chromium.base.annotations.SuppressFBWarnings; +import org.chromium.base.library_loader.LibraryProcessType; +import org.chromium.base.library_loader.ProcessInitException; +import org.chromium.content.app.ContentApplication; +import org.chromium.content.browser.BrowserStartupController; + +/** + * {@link BackgroundSyncLauncherService} is scheduled through the {@link GcmNetworkManager} + * when the browser needs to be launched in response to changing network or power conditions. + */ +public class BackgroundSyncLauncherService extends GcmTaskService { + private static final String TAG = "cr_BgSyncLauncher"; + + @Override + @VisibleForTesting + public int onRunTask(TaskParams params) { + // Start the browser. The browser's BackgroundSyncManager (for the active profile) will + // start, check the network, and run any necessary sync events. This task runs with a wake + // lock, but has a three minute timeout, so we need to start the browser in its own task. + // TODO(jkarlin): Protect the browser sync event with a wake lock. See crbug.com/486020. + Log.v(TAG, "Starting Browser after coming online"); + final Context context = this; + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + if (!BackgroundSyncLauncher.hasInstance()) { + launchBrowser(context); + } + } + }); + return GcmNetworkManager.RESULT_SUCCESS; + } + + @VisibleForTesting + @SuppressFBWarnings("DM_EXIT") + protected void launchBrowser(Context context) { + ContentApplication.initCommandLine(context); + try { + BrowserStartupController.get(context, LibraryProcessType.PROCESS_BROWSER) + .startBrowserProcessesSync(false); + } catch (ProcessInitException e) { + Log.e(TAG, "ProcessInitException while starting the browser process"); + // Since the library failed to initialize nothing in the application + // can work, so kill the whole application not just the activity. + System.exit(-1); + } + } + + @Override + @VisibleForTesting + public void onInitializeTasks() { + BackgroundSyncLauncher.rescheduleTasksOnUpgrade(this); + } +} diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherServiceTest.java new file mode 100644 index 0000000..cb647b6 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherServiceTest.java @@ -0,0 +1,80 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser; + +import android.content.Context; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.util.AdvancedMockContext; +import org.chromium.base.test.util.Feature; + +/** + * Tests {@link BackgroundSyncLauncherService}. + */ +public class BackgroundSyncLauncherServiceTest extends InstrumentationTestCase { + private Context mContext; + private BackgroundSyncLauncher mLauncher; + private MockLauncherService mLauncherService; + + static class MockLauncherService extends BackgroundSyncLauncherService { + private boolean mDidStartService = false; + + @Override + protected void launchBrowser(Context context) { + mDidStartService = true; + } + + // Posts an assertion task to the UI thread. Since this is only called after the call + // to onRunTask, it will be enqueued after any possible call to launchBrowser, and we + // can reliably check whether launchBrowser was called. + protected void checkExpectations(final boolean expectedStartService) { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + assertEquals("StartedService", expectedStartService, mDidStartService); + } + }); + } + } + + @Override + protected void setUp() throws Exception { + mContext = new AdvancedMockContext(getInstrumentation().getTargetContext()); + BackgroundSyncLauncher.setGCMEnabled(false); + mLauncher = BackgroundSyncLauncher.create(mContext); + mLauncherService = new MockLauncherService(); + } + + @Override + protected void tearDown() throws Exception { + BackgroundSyncLauncher.setGCMEnabled(true); + super.tearDown(); + } + + private void deleteLauncherInstance() { + mLauncher.destroy(); + mLauncher = null; + } + + private void startOnRunTaskAndVerify(boolean shouldStart) { + mLauncherService.onRunTask(null); + mLauncherService.checkExpectations(shouldStart); + } + + @SmallTest + @Feature({"BackgroundSync"}) + public void testNoFireWhenInstanceExists() { + startOnRunTaskAndVerify(false); + } + + @SmallTest + @Feature({"BackgroundSync"}) + public void testFiresWhenInstanceDoesNotExist() { + deleteLauncherInstance(); + startOnRunTaskAndVerify(true); + } +} diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BackgroundSyncLauncherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java index 0fc40e3..e78eea2 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/BackgroundSyncLauncherTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java @@ -2,11 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.content.browser; +package org.chromium.chrome.browser; import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -16,46 +14,24 @@ import org.chromium.base.test.util.Feature; import java.util.concurrent.Semaphore; /** - * Tests {@link BackgroundSyncLauncherService} and {@link BackgroundSyncLauncherService.Receiver}. + * Tests {@link BackgroundSyncLauncher}. */ public class BackgroundSyncLauncherTest extends InstrumentationTestCase { private Context mContext; private BackgroundSyncLauncher mLauncher; - private MockReceiver mLauncherServiceReceiver; private Boolean mShouldLaunchResult; - static class MockReceiver extends BackgroundSyncLauncherService.Receiver { - private boolean mIsOnline = true; - private boolean mDidStartService; - - public void setOnline(boolean online) { - mIsOnline = online; - } - - @Override - protected boolean isOnline(Context context) { - return mIsOnline; - } - - @Override - protected void startService(Context context) { - startServiceImpl(); - } - - private void startServiceImpl() { - mDidStartService = true; - } - - protected void checkExpectations(boolean expectedStartService) { - assertEquals("StartedService", expectedStartService, mDidStartService); - } - } - @Override protected void setUp() throws Exception { mContext = new AdvancedMockContext(getInstrumentation().getTargetContext()); + BackgroundSyncLauncher.setGCMEnabled(false); mLauncher = BackgroundSyncLauncher.create(mContext); - mLauncherServiceReceiver = new MockReceiver(); + } + + @Override + protected void tearDown() throws Exception { + BackgroundSyncLauncher.setGCMEnabled(true); + super.tearDown(); } private void deleteLauncherInstance() { @@ -63,12 +39,6 @@ public class BackgroundSyncLauncherTest extends InstrumentationTestCase { mLauncher = null; } - private void startOnReceiveAndVerify(boolean shouldStart) { - mLauncherServiceReceiver.onReceive( - mContext, new Intent(ConnectivityManager.CONNECTIVITY_ACTION)); - mLauncherServiceReceiver.checkExpectations(shouldStart); - } - private Boolean shouldLaunchWhenNextOnlineSync() { mShouldLaunchResult = false; @@ -112,16 +82,16 @@ public class BackgroundSyncLauncherTest extends InstrumentationTestCase { @Feature({"BackgroundSync"}) public void testSetLaunchWhenNextOnline() { assertFalse(shouldLaunchWhenNextOnlineSync()); - mLauncher.setLaunchWhenNextOnline(mContext, true); + mLauncher.launchBrowserWhenNextOnlineIfStopped(mContext, true); assertTrue(shouldLaunchWhenNextOnlineSync()); - mLauncher.setLaunchWhenNextOnline(mContext, false); + mLauncher.launchBrowserWhenNextOnlineIfStopped(mContext, false); assertFalse(shouldLaunchWhenNextOnlineSync()); } @SmallTest @Feature({"BackgroundSync"}) public void testNewLauncherDisablesNextOnline() { - mLauncher.setLaunchWhenNextOnline(mContext, true); + mLauncher.launchBrowserWhenNextOnlineIfStopped(mContext, true); assertTrue(shouldLaunchWhenNextOnlineSync()); // Simulate restarting the browser by deleting the launcher and creating a new one. @@ -129,40 +99,4 @@ public class BackgroundSyncLauncherTest extends InstrumentationTestCase { mLauncher = BackgroundSyncLauncher.create(mContext); assertFalse(shouldLaunchWhenNextOnlineSync()); } - - @SmallTest - @Feature({"BackgroundSync"}) - public void testNoFireWhenInstanceExists() { - mLauncher.setLaunchWhenNextOnline(mContext, true); - mLauncherServiceReceiver.setOnline(true); - startOnReceiveAndVerify(false); - - deleteLauncherInstance(); - startOnReceiveAndVerify(true); - } - - @SmallTest - @Feature({"BackgroundSync"}) - public void testReceiverOffline() { - mLauncher.setLaunchWhenNextOnline(mContext, true); - mLauncherServiceReceiver.setOnline(false); - deleteLauncherInstance(); - startOnReceiveAndVerify(false); - } - - @SmallTest - @Feature({"BackgroundSync"}) - public void testReceiverOnline() { - mLauncher.setLaunchWhenNextOnline(mContext, true); - mLauncherServiceReceiver.setOnline(true); - deleteLauncherInstance(); - startOnReceiveAndVerify(true); - } - - @SmallTest - @Feature({"BackgroundSync"}) - public void testStartingService() { - Intent serviceIntent = new Intent(mContext, BackgroundSyncLauncherService.class); - MockReceiver.startWakefulService(mContext, serviceIntent); - } } diff --git a/content/browser/android/background_sync_launcher_android.cc b/chrome/browser/android/background_sync_launcher_android.cc index 693817b..7d9c2e3 100644 --- a/content/browser/android/background_sync_launcher_android.cc +++ b/chrome/browser/android/background_sync_launcher_android.cc @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/android/background_sync_launcher_android.h" +#include "chrome/browser/android/background_sync_launcher_android.h" #include "content/public/browser/browser_thread.h" #include "jni/BackgroundSyncLauncher_jni.h" -namespace content { +using content::BrowserThread; namespace { base::LazyInstance<BackgroundSyncLauncherAndroid> g_background_sync_launcher = @@ -23,7 +23,7 @@ BackgroundSyncLauncherAndroid* BackgroundSyncLauncherAndroid::Get() { // static void BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnline( - const BackgroundSyncManager* registrant, + const content::BackgroundSyncManager* registrant, bool launch_when_next_online) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -31,7 +31,7 @@ void BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnline( } void BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnlineImpl( - const BackgroundSyncManager* registrant, + const content::BackgroundSyncManager* registrant, bool launch_when_next_online) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -45,7 +45,7 @@ void BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnlineImpl( bool now_launching = !launch_when_next_online_registrants_.empty(); if (was_launching != now_launching) { JNIEnv* env = base::android::AttachCurrentThread(); - Java_BackgroundSyncLauncher_setLaunchWhenNextOnline( + Java_BackgroundSyncLauncher_launchBrowserWhenNextOnlineIfStopped( env, java_launcher_.obj(), base::android::GetApplicationContext(), now_launching); } @@ -70,5 +70,3 @@ BackgroundSyncLauncherAndroid::~BackgroundSyncLauncherAndroid() { JNIEnv* env = base::android::AttachCurrentThread(); Java_BackgroundSyncLauncher_destroy(env, java_launcher_.obj()); } - -} // namespace content diff --git a/content/browser/android/background_sync_launcher_android.h b/chrome/browser/android/background_sync_launcher_android.h index 77018f4..ced3422 100644 --- a/content/browser/android/background_sync_launcher_android.h +++ b/chrome/browser/android/background_sync_launcher_android.h @@ -2,25 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_ANDROID_BACKGROUND_SYNC_LAUNCHER_ANDROID_H_ -#define CONTENT_BROWSER_ANDROID_BACKGROUND_SYNC_LAUNCHER_ANDROID_H_ +#ifndef CHROME_BROWSER_ANDROID_BACKGROUND_SYNC_LAUNCHER_ANDROID_H_ +#define CHROME_BROWSER_ANDROID_BACKGROUND_SYNC_LAUNCHER_ANDROID_H_ #include <set> #include "base/android/jni_android.h" #include "base/lazy_instance.h" #include "base/macros.h" -#include "content/common/content_export.h" namespace content { class BackgroundSyncManager; +} + // The BackgroundSyncLauncherAndroid singleton owns the Java // BackgroundSyncLauncher object and is used to register interest in starting // the browser the next time the device goes online. This class runs on the UI // thread. -class CONTENT_EXPORT BackgroundSyncLauncherAndroid { +class BackgroundSyncLauncherAndroid { public: static BackgroundSyncLauncherAndroid* Get(); @@ -32,7 +33,7 @@ class CONTENT_EXPORT BackgroundSyncLauncherAndroid { // created (browser restart). The caller can remove interest in launching the // browser by calling with |launch_when_next_online| set to false. static void LaunchBrowserWhenNextOnline( - const BackgroundSyncManager* registrant, + const content::BackgroundSyncManager* registrant, bool launch_when_next_online); static bool RegisterLauncher(JNIEnv* env); @@ -44,14 +45,14 @@ class CONTENT_EXPORT BackgroundSyncLauncherAndroid { BackgroundSyncLauncherAndroid(); ~BackgroundSyncLauncherAndroid(); - void LaunchBrowserWhenNextOnlineImpl(const BackgroundSyncManager* registrant, - bool launch_when_next_online); + void LaunchBrowserWhenNextOnlineImpl( + const content::BackgroundSyncManager* registrant, + bool launch_when_next_online); - std::set<const BackgroundSyncManager*> launch_when_next_online_registrants_; + std::set<const content::BackgroundSyncManager*> + launch_when_next_online_registrants_; base::android::ScopedJavaGlobalRef<jobject> java_launcher_; DISALLOW_COPY_AND_ASSIGN(BackgroundSyncLauncherAndroid); }; -} // namespace content - -#endif // CONTENT_BROWSER_ANDROID_BACKGROUND_SYNC_LAUNCHER_ANDROID_H_ +#endif // CHROME_BROWSER_ANDROID_BACKGROUND_SYNC_LAUNCHER_ANDROID_H_ diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc index 2ee7fa0..002172a 100644 --- a/chrome/browser/android/chrome_jni_registrar.cc +++ b/chrome/browser/android/chrome_jni_registrar.cc @@ -11,6 +11,7 @@ #include "chrome/browser/android/accessibility/font_size_prefs_android.h" #include "chrome/browser/android/accessibility_util.h" #include "chrome/browser/android/appmenu/app_menu_drag_helper.h" +#include "chrome/browser/android/background_sync_launcher_android.h" #include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h" #include "chrome/browser/android/banners/app_banner_manager_android.h" #include "chrome/browser/android/bookmarks/bookmarks_bridge.h" @@ -202,6 +203,8 @@ static base::android::RegistrationMethod kChromeRegisteredMethods[] = { autofill::AutofillPopupViewAndroid::RegisterAutofillPopupViewAndroid}, {"AutofillProfileBridge", autofill::RegisterAutofillProfileBridge}, {"AutoSigninSnackbarController", RegisterAutoSigninSnackbarController}, + {"BackgroundSyncLauncherAndroid", + BackgroundSyncLauncherAndroid::RegisterLauncher}, {"BluetoothChooserAndroid", BluetoothChooserAndroid::Register}, {"BookmarksBridge", BookmarksBridge::RegisterBookmarksBridge}, {"CardUnmaskPrompt", autofill::CardUnmaskPromptViewAndroid::Register}, diff --git a/chrome/browser/background_sync/background_sync_controller_impl.cc b/chrome/browser/background_sync/background_sync_controller_impl.cc index 7668229..7412edd 100644 --- a/chrome/browser/background_sync/background_sync_controller_impl.cc +++ b/chrome/browser/background_sync/background_sync_controller_impl.cc @@ -8,6 +8,10 @@ #include "chrome/browser/profiles/profile.h" #include "components/rappor/rappor_utils.h" +#if defined(OS_ANDROID) +#include "chrome/browser/android/background_sync_launcher_android.h" +#endif + BackgroundSyncControllerImpl::BackgroundSyncControllerImpl(Profile* profile) : profile_(profile) {} @@ -28,3 +32,13 @@ void BackgroundSyncControllerImpl::NotifyBackgroundSyncRegistered( rappor::SampleDomainAndRegistryFromGURL( GetRapporService(), "BackgroundSync.Register.Origin", origin); } + +#if defined(OS_ANDROID) +void BackgroundSyncControllerImpl::LaunchBrowserWhenNextOnline( + const content::BackgroundSyncManager* registrant, + bool launch_when_next_online) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnline( + registrant, launch_when_next_online); +} +#endif diff --git a/chrome/browser/background_sync/background_sync_controller_impl.h b/chrome/browser/background_sync/background_sync_controller_impl.h index 156a6244..0126149 100644 --- a/chrome/browser/background_sync/background_sync_controller_impl.h +++ b/chrome/browser/background_sync/background_sync_controller_impl.h @@ -25,6 +25,12 @@ class BackgroundSyncControllerImpl : public content::BackgroundSyncController, // content::BackgroundSyncController overrides. void NotifyBackgroundSyncRegistered(const GURL& origin) override; +#if defined(OS_ANDROID) + void LaunchBrowserWhenNextOnline( + const content::BackgroundSyncManager* registrant, + bool launch_when_next_online) override; +#endif + protected: // Virtual for testing. virtual rappor::RapporService* GetRapporService(); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index a909d3f..57f1400 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -854,6 +854,8 @@ 'browser/web_data_service_factory.h', ], 'chrome_browser_android_sources': [ + 'browser/android/background_sync_launcher_android.cc', + 'browser/android/background_sync_launcher_android.h', 'browser/download/download_request_infobar_delegate.cc', 'browser/download/download_request_infobar_delegate.h', 'browser/geolocation/geolocation_infobar_delegate.cc', @@ -1735,6 +1737,7 @@ 'android/java/src/org/chromium/chrome/browser/autofill/CreditCardScanner.java', 'android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java', 'android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java', + 'android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java', 'android/java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java', 'android/java/src/org/chromium/chrome/browser/BookmarksBridge.java', 'android/java/src/org/chromium/chrome/browser/bookmark/EditBookmarkHelper.java', diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc index bea2065..62b393c 100644 --- a/content/browser/android/browser_jni_registrar.cc +++ b/content/browser/android/browser_jni_registrar.cc @@ -8,7 +8,6 @@ #include "base/android/jni_registrar.h" #include "content/browser/accessibility/browser_accessibility_android.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h" -#include "content/browser/android/background_sync_launcher_android.h" #include "content/browser/android/background_sync_network_observer_android.h" #include "content/browser/android/browser_startup_controller.h" #include "content/browser/android/child_process_launcher_android.h" @@ -49,8 +48,6 @@ namespace { base::android::RegistrationMethod kContentRegisteredMethods[] = { {"AndroidLocationApiAdapter", content::AndroidLocationApiAdapter::RegisterGeolocationService}, - {"BackgroundSyncLauncherAndroid", - content::BackgroundSyncLauncherAndroid::RegisterLauncher}, {"BackgroundSyncNetworkObserverAndroid", content::BackgroundSyncNetworkObserverAndroid::Observer:: RegisterNetworkObserver}, diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc index d7fb0f9..96e41e8 100644 --- a/content/browser/background_sync/background_sync_manager.cc +++ b/content/browser/background_sync/background_sync_manager.cc @@ -23,7 +23,6 @@ #include "content/public/browser/browser_thread.h" #if defined(OS_ANDROID) -#include "content/browser/android/background_sync_launcher_android.h" #include "content/browser/android/background_sync_network_observer_android.h" #endif @@ -62,18 +61,29 @@ bool ShouldDisableForFieldTrial() { base::CompareCase::INSENSITIVE_ASCII); } +// Returns nullptr if the controller cannot be accessed for any reason. +BackgroundSyncController* GetBackgroundSyncControllerOnUIThread( + const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (!service_worker_context) + return nullptr; + StoragePartitionImpl* storage_partition_impl = + service_worker_context->storage_partition(); + if (!storage_partition_impl) // may be null in tests + return nullptr; + + return storage_partition_impl->browser_context() + ->GetBackgroundSyncController(); +} + void NotifyBackgroundSyncRegisteredOnUIThread( const scoped_refptr<ServiceWorkerContextWrapper>& sw_context_wrapper, const GURL& origin) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - StoragePartitionImpl* storage_partition_impl = - sw_context_wrapper->storage_partition(); - if (!storage_partition_impl) // happens in tests - return; - BackgroundSyncController* background_sync_controller = - storage_partition_impl->browser_context()->GetBackgroundSyncController(); + GetBackgroundSyncControllerOnUIThread(sw_context_wrapper); if (!background_sync_controller) return; @@ -982,17 +992,30 @@ void BackgroundSyncManager::SchedulePendingRegistrations() { } } - // TODO(jkarlin): Use the context's path instead of the 'this' pointer as an - // identifier. See crbug.com/489705. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnline, - this, keep_browser_alive_for_one_shot)); + base::Bind(&BackgroundSyncManager::SchedulePendingRegistrationsOnUIThread, + base::Unretained(this), keep_browser_alive_for_one_shot)); + #else // TODO(jkarlin): Toggle Chrome's background mode. #endif } +void BackgroundSyncManager::SchedulePendingRegistrationsOnUIThread( + bool keep_browser_alive_for_one_shot) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + BackgroundSyncController* background_sync_controller = + GetBackgroundSyncControllerOnUIThread(service_worker_context_); + if (background_sync_controller) { + // TODO(jkarlin): Use the context's path instead of the 'this' pointer as an + // identifier. See crbug.com/489705. + background_sync_controller->LaunchBrowserWhenNextOnline( + this, keep_browser_alive_for_one_shot); + } +} + void BackgroundSyncManager::FireReadyEvents() { DCHECK_CURRENTLY_ON(BrowserThread::IO); diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h index ee1bb84..65f5ff2 100644 --- a/content/browser/background_sync/background_sync_manager.h +++ b/content/browser/background_sync/background_sync_manager.h @@ -274,6 +274,8 @@ class CONTENT_EXPORT BackgroundSyncManager // seen (on Android the browser is instead woken up the next time it goes // online). For periodic syncs this means creating an alarm. void SchedulePendingRegistrations(); + void SchedulePendingRegistrationsOnUIThread( + bool keep_browser_alive_for_one_shot); // FireReadyEvents and callbacks void FireReadyEvents(); diff --git a/content/content_browser.gypi b/content/content_browser.gypi index d302f64..e7056cd 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -345,8 +345,6 @@ 'browser/accessibility/one_shot_accessibility_tree_search.cc', 'browser/accessibility/one_shot_accessibility_tree_search.h', 'browser/android/animation_utils.h', - 'browser/android/background_sync_launcher_android.cc', - 'browser/android/background_sync_launcher_android.h', 'browser/android/background_sync_network_observer_android.cc', 'browser/android/background_sync_network_observer_android.h', 'browser/android/browser_jni_registrar.cc', diff --git a/content/content_jni.gypi b/content/content_jni.gypi index 2d465d5..affde12 100644 --- a/content/content_jni.gypi +++ b/content/content_jni.gypi @@ -11,7 +11,6 @@ 'public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', - 'public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncher.java', 'public/android/java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', diff --git a/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncher.java b/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncher.java deleted file mode 100644 index bad2671..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncher.java +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.content.browser; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.AsyncTask; -import android.preference.PreferenceManager; - -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * The {@link BackgroundSyncLauncher} singleton is created and owned by the C++ browser. It - * registers interest in waking up the browser the next time the device goes online after the - *browser closes via the {@link #setLaunchWhenNextOnline} method. - * - * Thread model: This class is to be run on the UI thread only. - */ -@JNINamespace("content") -public class BackgroundSyncLauncher { - static final String PREF_BACKGROUND_SYNC_LAUNCH_NEXT_ONLINE = "bgsync_launch_next_online"; - - // The instance of BackgroundSyncLauncher currently owned by a C++ - // BackgroundSyncLauncherAndroid, if any. If it is non-null then the browser is running. - private static BackgroundSyncLauncher sInstance; - - /** - * Create a BackgroundSyncLauncher object, which is owned by C++. - * @param context The app context. - */ - @VisibleForTesting - @CalledByNative - protected static BackgroundSyncLauncher create(Context context) { - if (sInstance != null) { - throw new IllegalStateException("Already instantiated"); - } - - sInstance = new BackgroundSyncLauncher(context); - return sInstance; - } - - /** - * Called when the C++ counterpart is deleted. - */ - @VisibleForTesting - @CalledByNative - protected void destroy() { - assert sInstance == this; - sInstance = null; - } - - /** - * Callback for {@link #shouldLaunchWhenNextOnline}. The run method is invoked on the UI thread. - */ - public static interface ShouldLaunchCallback { public void run(Boolean shouldLaunch); } - - /** - * Returns whether the browser should be launched when the device next goes online. - * This is set by C++ and reset to false each time {@link BackgroundSyncLauncher}'s singleton is - * created (the native browser is started). This call is asynchronous and will run the callback - * on the UI thread when complete. - * @param context The application context. - * @param sharedPreferences The shared preferences. - */ - protected static void shouldLaunchWhenNextOnline( - final Context context, final ShouldLaunchCallback callback) { - new AsyncTask<Void, Void, Boolean>() { - @Override - protected Boolean doInBackground(Void... params) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - return prefs.getBoolean(PREF_BACKGROUND_SYNC_LAUNCH_NEXT_ONLINE, false); - } - @Override - protected void onPostExecute(Boolean shouldLaunch) { - callback.run(shouldLaunch); - } - }.execute(); - } - - /** - * Set interest (or disinterest) in launching the browser the next time the device goes online - * after the browser closes. On creation of the {@link BackgroundSyncLauncher} class (on browser - * start) this value is reset to false. This is set by C++ and reset to false each time - * {@link BackgroundSyncLauncher}'s singleton is created (the native browser is started). This - * call is asynchronous. - */ - @VisibleForTesting - @CalledByNative - protected void setLaunchWhenNextOnline(final Context context, final boolean shouldLaunch) { - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - prefs.edit() - .putBoolean(PREF_BACKGROUND_SYNC_LAUNCH_NEXT_ONLINE, shouldLaunch) - .apply(); - return null; - } - }.execute(); - } - - /** - * True if the native browser has started and created an instance of {@link - * BackgroundSyncLauncher}. - */ - protected static boolean hasInstance() { - return sInstance != null; - } - - private BackgroundSyncLauncher(Context context) { - setLaunchWhenNextOnline(context, false); - } -}
\ No newline at end of file diff --git a/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncherService.java b/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncherService.java deleted file mode 100644 index fd92e55..0000000 --- a/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncLauncherService.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.content.browser; - -import android.app.IntentService; -import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.support.v4.content.WakefulBroadcastReceiver; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.SuppressFBWarnings; -import org.chromium.base.library_loader.LibraryProcessType; -import org.chromium.base.library_loader.ProcessInitException; -import org.chromium.content.app.ContentApplication; - -/** - * {@link BackgroundSyncLauncherService} monitors network connectivity and launches - * the browser when it goes online if the {@link BackgroundSyncLauncher} requested it. - */ -public class BackgroundSyncLauncherService extends IntentService { - private static final String TAG = "cr.BgSyncLauncher"; - - /** - * Receiver for network connection change broadcasts. If the device is online - * and the browser isn't running it starts the BackgroundSyncLauncherService. - * The service will then launch the browser if necessary. - * - * This class is public so that it can be instantiated by the Android runtime. - */ - public static class Receiver extends WakefulBroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // If online, the browser isn't running, and the browser has requested - // it be launched the next time the device is online, start the browser. - if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()) - && isOnline(context) && !BackgroundSyncLauncher.hasInstance()) { - startService(context); - } - } - - @VisibleForTesting - protected void startService(Context context) { - Intent serviceIntent = new Intent(context, BackgroundSyncLauncherService.class); - startWakefulService(context, serviceIntent); - } - - @VisibleForTesting - protected boolean isOnline(Context context) { - ConnectivityManager connectivityManager = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); - return networkInfo != null && networkInfo.isConnected(); - } - } - - public BackgroundSyncLauncherService() { - super("BackgroundSyncLauncherService"); - } - - @Override - public void onHandleIntent(Intent intent) { - try { - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - onOnline(getApplicationContext()); - } - }); - } finally { - WakefulBroadcastReceiver.completeWakefulIntent(intent); - } - } - - private void onOnline(final Context context) { - ThreadUtils.assertOnUiThread(); - - BackgroundSyncLauncher.ShouldLaunchCallback callback = - new BackgroundSyncLauncher.ShouldLaunchCallback() { - @Override - public void run(Boolean shouldLaunch) { - if (!shouldLaunch) return; - // Start the browser. The browser's BackgroundSyncManager (for the active - // profile) will start, check the network, and run any necessary sync - // events. It runs without a wake lock. - // TODO(jkarlin): Protect the browser sync event with a wake lock. See - // crbug.com/486020. - Log.v(TAG, "Starting Browser after coming online"); - launchBrowser(context); - } - }; - BackgroundSyncLauncher.shouldLaunchWhenNextOnline(context, callback); - } - - @SuppressFBWarnings("DM_EXIT") - private void launchBrowser(Context context) { - ContentApplication.initCommandLine(context); - try { - BrowserStartupController.get(context, LibraryProcessType.PROCESS_BROWSER) - .startBrowserProcessesSync(false); - } catch (ProcessInitException e) { - Log.e(TAG, "ProcessInitException while starting the browser process"); - // Since the library failed to initialize nothing in the application - // can work, so kill the whole application not just the activity. - System.exit(-1); - } - } -} diff --git a/content/public/browser/background_sync_controller.h b/content/public/browser/background_sync_controller.h index 4b2ce63..fccaf44 100644 --- a/content/public/browser/background_sync_controller.h +++ b/content/public/browser/background_sync_controller.h @@ -10,6 +10,8 @@ namespace content { +class BackgroundSyncManager; + // An interface that the Background Sync API uses to access services from the // embedder. Must only be used on the UI thread. class CONTENT_EXPORT BackgroundSyncController { @@ -19,6 +21,15 @@ class CONTENT_EXPORT BackgroundSyncController { // Notification that a service worker registration with origin |origin| just // registered a background sync event. virtual void NotifyBackgroundSyncRegistered(const GURL& origin) {} + + // Register the |registrant|'s interest (or disinterest) in starting the + // browser the next time the device goes online after the browser has closed. + // This only needs to be implemented by browsers; WebView and other embedders + // which should not have their application relaunched by Background Sync can + // use the default implentation. + virtual void LaunchBrowserWhenNextOnline( + const BackgroundSyncManager* registrant, + bool launch_when_next_online) {} }; } // namespace content diff --git a/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2 b/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2 index fa1fc35..11e6b27 100644 --- a/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2 +++ b/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2 @@ -58,15 +58,6 @@ android:exported="false" /> {% endfor %} - <!-- Service Worker Background Sync service listener --> - <service android:name="org.chromium.content.browser.BackgroundSyncLauncherService" - android:exported="false" /> - <receiver android:name="org.chromium.content.browser.BackgroundSyncLauncherService$Receiver"> - <intent-filter> - <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> - </intent-filter> - </receiver> - </application> <instrumentation android:name="org.chromium.native_test.NativeTestInstrumentationTestRunner" diff --git a/content/shell/android/shell_apk/AndroidManifest.xml.jinja2 b/content/shell/android/shell_apk/AndroidManifest.xml.jinja2 index 8a71044..f2e149f 100644 --- a/content/shell/android/shell_apk/AndroidManifest.xml.jinja2 +++ b/content/shell/android/shell_apk/AndroidManifest.xml.jinja2 @@ -61,14 +61,5 @@ <meta-data android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER" android:value="org.chromium.content.browser.SmartClipProvider" /> - <!-- Service Worker Background Sync service listener --> - <service android:name="org.chromium.content.browser.BackgroundSyncLauncherService" - android:exported="false" /> - <receiver android:name="org.chromium.content.browser.BackgroundSyncLauncherService$Receiver"> - <intent-filter> - <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> - </intent-filter> - </receiver> - </application> </manifest> |