Skip to content

Commit

Permalink
[Android] Adding content settings provider for notification channels
Browse files Browse the repository at this point in the history
- Right now it just attempts to create channels on grant, and delete
them on resetting the permission.

- Java side is not yet implemented so this is actually a no-op.

BUG=700377

Review-Url: https://codereview.chromium.org/2886433002
Cr-Commit-Position: refs/heads/master@{#478267}
  • Loading branch information
awdf authored and Commit Bot committed Jun 9, 2017
1 parent b706bcf commit 338aa5e
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 2 deletions.
1 change: 1 addition & 0 deletions chrome/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ java_cpp_enum("chrome_android_java_enums_srcjar") {
"//chrome/browser/android/webapk/chrome_webapk_host.h",
"//chrome/browser/android/webapk/webapk_install_service.h",
"//chrome/browser/banners/app_banner_settings_helper.h",
"//chrome/browser/notifications/notification_channels_provider_android.h",
"//chrome/browser/notifications/notification_platform_bridge_android.cc",
"//chrome/browser/ntp_snippets/ntp_snippets_metrics.h",
"//chrome/browser/profiles/profile_metrics.h",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2017 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.notifications;

import org.chromium.base.BuildInfo;
import org.chromium.base.annotations.CalledByNative;

/**
* Interface for native code to interact with Android notification channels.
*/
public class NotificationSettingsBridge {
// TODO(awdf): Remove this and check BuildInfo.sdk_int() from native instead, once SdkVersion
// enum includes Android O.
@CalledByNative
static boolean shouldUseChannelSettings() {
return BuildInfo.isAtLeastO();
}

/**
* Creates a notification channel for the given origin.
* @param origin The site origin to be used as the channel name.
* @param enabled True if the channel should be initially enabled, false if
* it should start off as blocked.
* @return true if the channel was successfully created, false otherwise.
*/
@CalledByNative
static boolean createChannel(String origin, boolean enabled) {
// TODO(crbug.com/700377) Actually construct a channel.
return false;
}

@CalledByNative
static @NotificationChannelStatus int getChannelStatus(String origin) {
// TODO(crbug.com/700377) Actually check channel status.
return NotificationChannelStatus.UNAVAILABLE;
}

@CalledByNative
static void deleteChannel(String origin) {
// TODO(crbug.com/700377) Actually delete channel.
}
}
1 change: 1 addition & 0 deletions chrome/android/java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java",
"java/src/org/chromium/chrome/browser/notifications/NotificationJobService.java",
"java/src/org/chromium/chrome/browser/notifications/NotificationService.java",
"java/src/org/chromium/chrome/browser/notifications/NotificationSettingsBridge.java",
"java/src/org/chromium/chrome/browser/notifications/NotificationSystemStatusUtil.java",
"java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java",
"java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilder.java",
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ split_static_library("browser") {
"notifications/non_persistent_notification_handler.h",
"notifications/notification.cc",
"notifications/notification.h",
"notifications/notification_channels_provider_android.cc",
"notifications/notification_channels_provider_android.h",
"notifications/notification_common.cc",
"notifications/notification_common.h",
"notifications/notification_delegate.h",
Expand Down Expand Up @@ -4254,6 +4256,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
"../android/java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
"../android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java",
"../android/java/src/org/chromium/chrome/browser/notifications/NotificationSettingsBridge.java",
"../android/java/src/org/chromium/chrome/browser/ntp/ContentSuggestionsNotificationHelper.java",
"../android/java/src/org/chromium/chrome/browser/ntp/ForeignSessionHelper.java",
"../android/java/src/org/chromium/chrome/browser/ntp/LogoBridge.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
#endif

#if defined(OS_ANDROID)
#include "chrome/browser/notifications/notification_channels_provider_android.h"
#endif // OS_ANDROID

HostContentSettingsMapFactory::HostContentSettingsMapFactory()
: RefcountedBrowserContextKeyedServiceFactory(
"HostContentSettingsMap",
Expand Down Expand Up @@ -109,6 +113,15 @@ scoped_refptr<RefcountedKeyedService>
}
#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS)

#if defined(OS_ANDROID)
if (base::FeatureList::IsEnabled(features::kSiteNotificationChannels)) {
auto channels_provider =
base::MakeUnique<NotificationChannelsProviderAndroid>();
settings_map->RegisterProvider(
HostContentSettingsMap::NOTIFICATION_ANDROID_PROVIDER,
std::move(channels_provider));
}
#endif // defined (OS_ANDROID)
return settings_map;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ TEST_F(SiteEngagementServiceTest, NotificationPermission) {

settings_map->SetContentSettingDefaultScope(
url3, GURL(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
content_settings::ResourceIdentifier(), CONTENT_SETTING_ASK);
content_settings::ResourceIdentifier(), CONTENT_SETTING_DEFAULT);

EXPECT_EQ(5, service_->GetScore(url1));
EXPECT_EQ(0, service_->GetScore(url2));
Expand Down
147 changes: 147 additions & 0 deletions chrome/browser/notifications/notification_channels_provider_android.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright 2017 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/notifications/notification_channels_provider_android.h"

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/content_settings/core/browser/content_settings_details.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "jni/NotificationSettingsBridge_jni.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"

using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;

namespace {

class NotificationChannelsBridgeImpl
: public NotificationChannelsProviderAndroid::NotificationChannelsBridge {
public:
NotificationChannelsBridgeImpl() = default;
~NotificationChannelsBridgeImpl() override = default;

bool ShouldUseChannelSettings() override {
return Java_NotificationSettingsBridge_shouldUseChannelSettings(
AttachCurrentThread());
}

void CreateChannel(const std::string& origin, bool enabled) override {
JNIEnv* env = AttachCurrentThread();
Java_NotificationSettingsBridge_createChannel(
env, ConvertUTF8ToJavaString(env, origin), enabled);
}

NotificationChannelStatus GetChannelStatus(
const std::string& origin) override {
JNIEnv* env = AttachCurrentThread();
return static_cast<NotificationChannelStatus>(
Java_NotificationSettingsBridge_getChannelStatus(
env, ConvertUTF8ToJavaString(env, origin)));
}

void DeleteChannel(const std::string& origin) override {
JNIEnv* env = AttachCurrentThread();
Java_NotificationSettingsBridge_deleteChannel(
env, ConvertUTF8ToJavaString(env, origin));
}
};

} // anonymous namespace

NotificationChannelsProviderAndroid::NotificationChannelsProviderAndroid()
: NotificationChannelsProviderAndroid(
base::MakeUnique<NotificationChannelsBridgeImpl>()) {}

NotificationChannelsProviderAndroid::NotificationChannelsProviderAndroid(
std::unique_ptr<NotificationChannelsBridge> bridge)
: bridge_(std::move(bridge)),
should_use_channels_(bridge_->ShouldUseChannelSettings()) {}

NotificationChannelsProviderAndroid::~NotificationChannelsProviderAndroid() =
default;

std::unique_ptr<content_settings::RuleIterator>
NotificationChannelsProviderAndroid::GetRuleIterator(
ContentSettingsType content_type,
const content_settings::ResourceIdentifier& resource_identifier,
bool incognito) const {
// TODO(crbug.com/700377) return rule iterator over all channels
return nullptr;
}

bool NotificationChannelsProviderAndroid::SetWebsiteSetting(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
const content_settings::ResourceIdentifier& resource_identifier,
base::Value* value) {
if (content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS ||
!should_use_channels_) {
return false;
}
// This provider only handles settings for specific origins.
if (primary_pattern == ContentSettingsPattern::Wildcard() &&
secondary_pattern == ContentSettingsPattern::Wildcard() &&
resource_identifier.empty()) {
return false;
}
url::Origin origin = url::Origin(GURL(primary_pattern.ToString()));
DCHECK(!origin.unique());
const std::string origin_string = origin.Serialize();
ContentSetting setting = content_settings::ValueToContentSetting(value);
switch (setting) {
case CONTENT_SETTING_ALLOW:
CreateChannelIfRequired(origin_string,
NotificationChannelStatus::ENABLED);
break;
case CONTENT_SETTING_BLOCK:
CreateChannelIfRequired(origin_string,
NotificationChannelStatus::BLOCKED);
break;
case CONTENT_SETTING_DEFAULT:
bridge_->DeleteChannel(origin_string);
break;
default:
// We rely on notification settings being one of ALLOW/BLOCK/DEFAULT.
NOTREACHED();
break;
}
return true;
}

void NotificationChannelsProviderAndroid::ClearAllContentSettingsRules(
ContentSettingsType content_type) {
// TODO(crbug.com/700377): If |content_type| == NOTIFICATIONS, delete
// all channels.
}

void NotificationChannelsProviderAndroid::ShutdownOnUIThread() {
RemoveAllObservers();
}

void NotificationChannelsProviderAndroid::CreateChannelIfRequired(
const std::string& origin_string,
NotificationChannelStatus new_channel_status) {
auto old_channel_status = bridge_->GetChannelStatus(origin_string);
if (old_channel_status == NotificationChannelStatus::UNAVAILABLE) {
bridge_->CreateChannel(
origin_string,
new_channel_status == NotificationChannelStatus::ENABLED);
} else {
// TODO(awdf): Maybe remove this DCHECK - channel status could change any
// time so this may be vulnerable to a race condition.
DCHECK(old_channel_status == new_channel_status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2017 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_NOTIFICATIONS_NOTIFICATION_CHANNELS_PROVIDER_ANDROID_H_
#define CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_CHANNELS_PROVIDER_ANDROID_H_

#include <string>

#include "base/macros.h"
#include "components/content_settings/core/browser/content_settings_observable_provider.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/browser/content_settings_rule.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/keyed_service/core/keyed_service.h"

namespace {

// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.notifications
enum NotificationChannelStatus { ENABLED, BLOCKED, UNAVAILABLE };

} // anonymous namespace

// This class provides notification content settings from system notification
// channels on Android O+. This provider takes precedence over pref-provided
// content settings, but defers to supervised user and policy settings - see
// ordering of the ProviderType enum values in HostContentSettingsMap.
class NotificationChannelsProviderAndroid
: public content_settings::ObservableProvider {
public:
// Helper class to make the JNI calls.
class NotificationChannelsBridge {
public:
virtual ~NotificationChannelsBridge() = default;
virtual bool ShouldUseChannelSettings() = 0;
virtual void CreateChannel(const std::string& origin, bool enabled) = 0;
virtual NotificationChannelStatus GetChannelStatus(
const std::string& origin) = 0;
virtual void DeleteChannel(const std::string& origin) = 0;
};

NotificationChannelsProviderAndroid();
~NotificationChannelsProviderAndroid() override;

// ProviderInterface methods:
std::unique_ptr<content_settings::RuleIterator> GetRuleIterator(
ContentSettingsType content_type,
const content_settings::ResourceIdentifier& resource_identifier,
bool incognito) const override;
bool SetWebsiteSetting(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
const content_settings::ResourceIdentifier& resource_identifier,
base::Value* value) override;
void ClearAllContentSettingsRules(ContentSettingsType content_type) override;
void ShutdownOnUIThread() override;

private:
explicit NotificationChannelsProviderAndroid(
std::unique_ptr<NotificationChannelsBridge> bridge);
friend class NotificationChannelsProviderAndroidTest;

void CreateChannelIfRequired(const std::string& origin_string,
NotificationChannelStatus new_channel_status);

std::unique_ptr<NotificationChannelsBridge> bridge_;
bool should_use_channels_;

DISALLOW_COPY_AND_ASSIGN(NotificationChannelsProviderAndroid);
};

#endif // CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_CHANNELS_PROVIDER_ANDROID_H_
Loading

0 comments on commit 338aa5e

Please sign in to comment.