Expose the Site Engagement Service to Java.
This CL adds a Java-side interface to the Site Engagement Service, as
well as a basic integration test for the interface. This allows Java
code (particularly tests) to programatically adjust and retrieve the
score.
BUG=671100
Review-Url: https://codereview.chromium.org/2553013002
Cr-Commit-Position: refs/heads/master@{#437825}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java b/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
new file mode 100644
index 0000000..316aa147
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java
@@ -0,0 +1,72 @@
+// Copyright 2016 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.engagement;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
+
+import org.chromium.chrome.browser.profiles.Profile;
+
+/**
+ * Provides access to the Site Engagement Service for a profile.
+ *
+ * Site engagement measures the level of engagement that a user has with an origin. This class
+ * allows Java to retrieve and modify engagement scores for URLs.
+ */
+public class SiteEngagementService {
+
+ /** Pointer to the native side SiteEngagementServiceAndroid shim. */
+ private long mNativePointer;
+
+ /**
+ * Returns a SiteEngagementService for the provided profile.
+ * Must be called on the UI thread.
+ */
+ public static SiteEngagementService getForProfile(Profile profile) {
+ assert ThreadUtils.runningOnUiThread();
+ return nativeSiteEngagementServiceForProfile(profile);
+ }
+
+ /**
+ * Returns the engagement score for the provided URL.
+ * Must be called on the UI thread.
+ */
+ public double getScore(String url) {
+ assert ThreadUtils.runningOnUiThread();
+ if (mNativePointer == 0) return 0.0;
+ return nativeGetScore(mNativePointer, url);
+ }
+
+ /**
+ * Sets the provided URL to have the provided engagement score.
+ * Must be called on the UI thread.
+ */
+ public void resetScoreForUrl(String url, double score) {
+ assert ThreadUtils.runningOnUiThread();
+ if (mNativePointer == 0) return;
+ nativeResetScoreForURL(mNativePointer, url, score);
+ }
+
+ @CalledByNative
+ private static SiteEngagementService create(long nativePointer) {
+ return new SiteEngagementService(nativePointer);
+ }
+
+ /** This object may only be created via the static getForProfile method. */
+ private SiteEngagementService(long nativePointer) {
+ mNativePointer = nativePointer;
+ }
+
+ @CalledByNative
+ private void onNativeDestroyed() {
+ mNativePointer = 0;
+ }
+
+ private static native SiteEngagementService nativeSiteEngagementServiceForProfile(
+ Profile profile);
+ private native double nativeGetScore(long nativeSiteEngagementServiceAndroid, String url);
+ private native void nativeResetScoreForURL(
+ long nativeSiteEngagementServiceAndroid, String url, double score);
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index bc673e58..6ee404fe 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -341,6 +341,7 @@
"java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java",
"java/src/org/chromium/chrome/browser/download/ui/ThumbnailProvider.java",
"java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java",
+ "java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java",
"java/src/org/chromium/chrome/browser/externalauth/ExternalAuthUtils.java",
"java/src/org/chromium/chrome/browser/externalauth/UserRecoverableErrorHandler.java",
"java/src/org/chromium/chrome/browser/externalauth/VerifiedHandler.java",
@@ -1240,6 +1241,7 @@
"javatests/src/org/chromium/chrome/browser/download/SystemDownloadNotifierTest.java",
"javatests/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapterTest.java",
"javatests/src/org/chromium/chrome/browser/download/ui/StubbedProvider.java",
+ "javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java",
"javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImplTest.java",
"javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java",
"javatests/src/org/chromium/chrome/browser/externalnav/IntentWithGesturesHandlerTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
new file mode 100644
index 0000000..35e5468
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/engagement/SiteEngagementServiceTest.java
@@ -0,0 +1,65 @@
+// Copyright 2016 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.engagement;
+
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.test.ChromeActivityTestCaseBase;
+
+/**
+ * Test for the Site Engagement Service Java binding.
+ */
+public class SiteEngagementServiceTest extends ChromeActivityTestCaseBase<ChromeActivity> {
+
+ public SiteEngagementServiceTest() {
+ super(ChromeActivity.class);
+ }
+
+ /**
+ * Verify that setting the engagement score for a URL and reading it back it works.
+ */
+ @SmallTest
+ @UiThreadTest
+ @Feature({"Engagement"})
+ public void testSettingAndRetrievingScore() {
+ final String url = "https://www.google.com";
+ SiteEngagementService service = SiteEngagementService.getForProfile(
+ getActivity().getActivityTab().getProfile());
+
+ assertEquals(0.0, service.getScore(url));
+ service.resetScoreForUrl(url, 5.0);
+ assertEquals(5.0, service.getScore(url));
+
+ service.resetScoreForUrl(url, 2.0);
+ assertEquals(2.0, service.getScore(url));
+ }
+
+ /**
+ * Verify that repeatedly fetching and throwing away the SiteEngagementService works.
+ */
+ @SmallTest
+ @UiThreadTest
+ @Feature({"Engagement"})
+ public void testRepeatedlyGettingService() {
+ final String url = "https://www.google.com";
+ Profile profile = getActivity().getActivityTab().getProfile();
+
+ assertEquals(0.0, SiteEngagementService.getForProfile(profile).getScore(url));
+ SiteEngagementService.getForProfile(profile).resetScoreForUrl(url, 5.0);
+ assertEquals(5.0, SiteEngagementService.getForProfile(profile).getScore(url));
+
+ SiteEngagementService.getForProfile(profile).resetScoreForUrl(url, 2.0);
+ assertEquals(2.0, SiteEngagementService.getForProfile(profile).getScore(url));
+ }
+
+ @Override
+ public void startMainActivity() throws InterruptedException {
+ startMainActivityOnBlankPage();
+ }
+}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a166fed8..f59ef26 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3255,6 +3255,8 @@
"dom_distiller/tab_utils_android.h",
"download/download_request_infobar_delegate_android.cc",
"download/download_request_infobar_delegate_android.h",
+ "engagement/site_engagement_service_android.cc",
+ "engagement/site_engagement_service_android.h",
"geolocation/geolocation_infobar_delegate_android.cc",
"geolocation/geolocation_infobar_delegate_android.h",
"history/android/android_history_provider_service.cc",
@@ -3907,6 +3909,7 @@
"../android/java/src/org/chromium/chrome/browser/download/DownloadItem.java",
"../android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java",
"../android/java/src/org/chromium/chrome/browser/download/ui/ThumbnailProviderImpl.java",
+ "../android/java/src/org/chromium/chrome/browser/engagement/SiteEngagementService.java",
"../android/java/src/org/chromium/chrome/browser/favicon/FaviconHelper.java",
"../android/java/src/org/chromium/chrome/browser/favicon/LargeIconBridge.java",
"../android/java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java",
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 595bde4..c6abcc156 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -109,6 +109,7 @@
#include "chrome/browser/autofill/android/personal_data_manager_android.h"
#include "chrome/browser/dom_distiller/dom_distiller_service_factory_android.h"
#include "chrome/browser/dom_distiller/tab_utils_android.h"
+#include "chrome/browser/engagement/site_engagement_service_android.h"
#include "chrome/browser/history/android/sqlite_cursor.h"
#include "chrome/browser/invalidation/invalidation_service_factory_android.h"
#include "chrome/browser/media/android/cdm/media_drm_credential_manager.h"
@@ -373,6 +374,7 @@
{"SigninInvestigator", SigninInvestigatorAndroid::Register},
{"SigninManager", SigninManagerAndroid::Register},
{"SingleTabModel", RegisterSingleTabModel},
+ {"SiteEngagementService", SiteEngagementServiceAndroid::Register},
#if BUILDFLAG(ENABLE_SPELLCHECK)
{"SpellCheckerSessionBridge", spellcheck::android::RegisterSpellcheckJni},
#endif
diff --git a/chrome/browser/engagement/site_engagement_service.cc b/chrome/browser/engagement/site_engagement_service.cc
index 66a2b49..3fa29c7 100644
--- a/chrome/browser/engagement/site_engagement_service.cc
+++ b/chrome/browser/engagement/site_engagement_service.cc
@@ -36,6 +36,10 @@
#include "content/public/browser/web_contents.h"
#include "url/gurl.h"
+#if defined(OS_ANDROID)
+#include "chrome/browser/engagement/site_engagement_service_android.h"
+#endif
+
namespace {
const int FOUR_WEEKS_IN_DAYS = 28;
@@ -238,6 +242,17 @@
return total_score;
}
+#if defined(OS_ANDROID)
+SiteEngagementServiceAndroid* SiteEngagementService::GetAndroidService() const {
+ return android_service_.get();
+}
+
+void SiteEngagementService::SetAndroidService(
+ std::unique_ptr<SiteEngagementServiceAndroid> android_service) {
+ android_service_ = std::move(android_service);
+}
+#endif
+
SiteEngagementService::SiteEngagementService(Profile* profile,
std::unique_ptr<base::Clock> clock)
: profile_(profile), clock_(std::move(clock)), weak_factory_(this) {
diff --git a/chrome/browser/engagement/site_engagement_service.h b/chrome/browser/engagement/site_engagement_service.h
index 65e22334..350d30a9 100644
--- a/chrome/browser/engagement/site_engagement_service.h
+++ b/chrome/browser/engagement/site_engagement_service.h
@@ -14,6 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
+#include "build/build_config.h"
#include "chrome/browser/engagement/site_engagement_metrics.h"
#include "chrome/browser/engagement/site_engagement_observer.h"
#include "components/history/core/browser/history_service_observer.h"
@@ -37,6 +38,10 @@
class Profile;
class SiteEngagementScore;
+#if defined(OS_ANDROID)
+class SiteEngagementServiceAndroid;
+#endif
+
class SiteEngagementScoreProvider {
public:
// Returns a non-negative integer representing the engagement score of the
@@ -134,6 +139,7 @@
private:
friend class SiteEngagementObserver;
+ friend class SiteEngagementServiceAndroid;
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CheckHistograms);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CleanupEngagementScores);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
@@ -160,6 +166,13 @@
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetScoreFromSettings);
FRIEND_TEST_ALL_PREFIXES(AppBannerSettingsHelperTest, SiteEngagementTrigger);
+#if defined(OS_ANDROID)
+ // Shim class to expose the service to Java.
+ SiteEngagementServiceAndroid* GetAndroidService() const;
+ void SetAndroidService(
+ std::unique_ptr<SiteEngagementServiceAndroid> android_service);
+#endif
+
// Only used in tests.
SiteEngagementService(Profile* profile, std::unique_ptr<base::Clock> clock);
@@ -253,6 +266,10 @@
// The clock used to vend times.
std::unique_ptr<base::Clock> clock_;
+#if defined(OS_ANDROID)
+ std::unique_ptr<SiteEngagementServiceAndroid> android_service_;
+#endif
+
// Metrics are recorded at non-incognito browser startup, and then
// approximately once per hour thereafter. Store the local time at which
// metrics were previously uploaded: the first event which affects any
diff --git a/chrome/browser/engagement/site_engagement_service_android.cc b/chrome/browser/engagement/site_engagement_service_android.cc
new file mode 100644
index 0000000..9d71bf9
--- /dev/null
+++ b/chrome/browser/engagement/site_engagement_service_android.cc
@@ -0,0 +1,81 @@
+// Copyright 2016 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.
+
+#include "chrome/browser/engagement/site_engagement_service_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/profiles/profile_android.h"
+#include "jni/SiteEngagementService_jni.h"
+#include "url/gurl.h"
+
+using base::android::JavaParamRef;
+
+// static
+bool SiteEngagementServiceAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+// static
+const base::android::ScopedJavaGlobalRef<jobject>&
+SiteEngagementServiceAndroid::GetOrCreate(JNIEnv* env,
+ SiteEngagementService* service) {
+ SiteEngagementServiceAndroid* android_service = service->GetAndroidService();
+ if (!android_service) {
+ service->SetAndroidService(
+ base::MakeUnique<SiteEngagementServiceAndroid>(env, service));
+ android_service = service->GetAndroidService();
+ }
+
+ return android_service->java_service_;
+}
+
+SiteEngagementServiceAndroid::SiteEngagementServiceAndroid(
+ JNIEnv* env,
+ SiteEngagementService* service)
+ : service_(service) {
+ java_service_.Reset(Java_SiteEngagementService_create(
+ env, reinterpret_cast<uintptr_t>(this)));
+}
+
+SiteEngagementServiceAndroid::~SiteEngagementServiceAndroid() {
+ Java_SiteEngagementService_onNativeDestroyed(
+ base::android::AttachCurrentThread(), java_service_);
+ java_service_.Reset();
+}
+
+double SiteEngagementServiceAndroid::GetScore(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& caller,
+ const JavaParamRef<jstring>& jurl) const {
+ if (!jurl)
+ return 0;
+
+ return service_->GetScore(
+ GURL(base::android::ConvertJavaStringToUTF16(env, jurl)));
+}
+
+void SiteEngagementServiceAndroid::ResetScoreForURL(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& caller,
+ const JavaParamRef<jstring>& jurl,
+ double score) {
+ if (jurl) {
+ service_->ResetScoreForURL(
+ GURL(base::android::ConvertJavaStringToUTF16(env, jurl)), score);
+ }
+}
+
+base::android::ScopedJavaLocalRef<jobject> SiteEngagementServiceForProfile(
+ JNIEnv* env,
+ const JavaParamRef<jclass>& clazz,
+ const JavaParamRef<jobject>& jprofile) {
+ Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
+ SiteEngagementService* service = SiteEngagementService::Get(profile);
+ DCHECK(service);
+
+ return base::android::ScopedJavaLocalRef<jobject>(
+ SiteEngagementServiceAndroid::GetOrCreate(env, service));
+}
diff --git a/chrome/browser/engagement/site_engagement_service_android.h b/chrome/browser/engagement/site_engagement_service_android.h
new file mode 100644
index 0000000..c68906a
--- /dev/null
+++ b/chrome/browser/engagement/site_engagement_service_android.h
@@ -0,0 +1,51 @@
+// Copyright 2016 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.
+
+#ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
+#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "chrome/browser/engagement/site_engagement_service.h"
+
+// Wrapper class to expose the Site Engagement Service to Java. This object is
+// owned by the |service_| which it wraps, and is lazily created when
+// a Java-side SiteEngagementService is constructed. Once created, all future
+// Java-side requests for a SiteEngagementService will use the same native
+// object.
+//
+// This class may only be used on the UI thread.
+class SiteEngagementServiceAndroid {
+ public:
+ static bool Register(JNIEnv* env);
+
+ // Returns the Java-side SiteEngagementService object corresponding to
+ // |service|.
+ static const base::android::ScopedJavaGlobalRef<jobject>& GetOrCreate(
+ JNIEnv* env,
+ SiteEngagementService* service);
+
+ SiteEngagementServiceAndroid(JNIEnv* env, SiteEngagementService* service);
+
+ ~SiteEngagementServiceAndroid();
+
+ double GetScore(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& caller,
+ const base::android::JavaParamRef<jstring>& jurl) const;
+
+ void ResetScoreForURL(JNIEnv* env,
+ const base::android::JavaParamRef<jobject>& caller,
+ const base::android::JavaParamRef<jstring>& jurl,
+ double score);
+
+ private:
+ base::android::ScopedJavaGlobalRef<jobject> java_service_;
+ SiteEngagementService* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(SiteEngagementServiceAndroid);
+};
+
+#endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_ANDROID_H_