Add a feature that allows control over DSE permission logic

(cherry picked from commit 7c6065feffecc3320c70207f18a556f0bc69b4d0)

(cherry picked from commit f08fe1a322e11f26282994e040ac696813747717)

Bug: 1228658,1238034
Change-Id: I46c0050f726e6c5472a1b9bc4d75f2cc38b5e0c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3021149
Commit-Queue: Andy Paicu <andypaicu@chromium.org>
Reviewed-by: Balazs Engedy <engedy@chromium.org>
Reviewed-by: Martin Šrámek <msramek@chromium.org>
Cr-Original-Original-Commit-Position: refs/heads/master@{#903402}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3052591
Reviewed-by: Andy Paicu <andypaicu@chromium.org>
Reviewed-by: Illia Klimov <elklm@google.com>
Auto-Submit: Andy Paicu <andypaicu@chromium.org>
Commit-Queue: Illia Klimov <elklm@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Original-Commit-Position: refs/branch-heads/4577@{#148}
Cr-Original-Branched-From: 761ddde228655e313424edec06497d0c56b0f3c4-refs/heads/master@{#902210}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3081944
Cr-Commit-Position: refs/branch-heads/4515@{#2007}
Cr-Branched-From: 488fc70865ddaa05324ac00a54a6eb783b4bc41c-refs/heads/master@{#885287}
diff --git a/chrome/browser/android/search_permissions/search_permissions_service.cc b/chrome/browser/android/search_permissions/search_permissions_service.cc
index 33f41930..51f5581d 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service.cc
+++ b/chrome/browser/android/search_permissions/search_permissions_service.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/android/search_permissions/search_geolocation_disclosure_tab_helper.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -20,6 +21,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/permissions/features.h"
 #include "components/permissions/permission_decision_auto_blocker.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
@@ -247,7 +249,8 @@
 ContentSetting SearchPermissionsService::RestoreOldSettingAndReturnPrevious(
     const GURL& dse_origin,
     ContentSettingsType type,
-    ContentSetting setting_to_restore) {
+    ContentSetting setting_to_restore,
+    bool preserve_block_setting) {
   // Read the current value of the old DSE. This is the DSE setting that we want
   // to try to apply to the new DSE origin.
   ContentSetting dse_setting = GetContentSetting(dse_origin, type);
@@ -261,6 +264,10 @@
     dse_setting = CONTENT_SETTING_BLOCK;
   }
 
+  // If `preserve_block_setting` is set we don't restore a "BLOCK" setting.
+  if (dse_setting == CONTENT_SETTING_BLOCK && preserve_block_setting)
+    setting_to_restore = CONTENT_SETTING_BLOCK;
+
   // Restore the setting for the old origin. If the user has changed the setting
   // since the origin became the DSE, we reset the setting so the user will be
   // prompted.
@@ -282,7 +289,8 @@
       ->RemoveEmbargoAndResetCounts(new_dse_origin, type);
 
   ContentSetting dse_setting = RestoreOldSettingAndReturnPrevious(
-      old_dse_origin, type, old_dse_setting_to_restore);
+      old_dse_origin, type, old_dse_setting_to_restore,
+      false /* preserve_block_setting */);
 
   ContentSetting new_dse_setting_to_restore =
       GetContentSetting(new_dse_origin, type);
@@ -313,21 +321,26 @@
 void SearchPermissionsService::InitializeSettingsIfNeeded() {
   GURL dse_origin = delegate_->GetDSEOrigin().GetURL();
 
-  // This can happen in tests or if the DSE is disabled by policy. If that's
-  // the case, we restore the old settings and erase the pref.
-  if (!dse_origin.is_valid()) {
+  // `dse_origin` can be invalid in tests or if the DSE is disabled by policy.
+  // If that's the case or if `RevertDSEAutomaticPermissions` is enabled, we
+  // restore the old settings and erase the pref.
+  const bool disabled_by_policy = !dse_origin.is_valid();
+  if (disabled_by_policy ||
+      base::FeatureList::IsEnabled(
+          permissions::features::kRevertDSEAutomaticPermissions)) {
     if (pref_service_->HasPrefPath(prefs::kDSEPermissionsSettings)) {
-      pref_service_->SetBoolean(prefs::kDSEWasDisabledByPolicy, true);
+      if (disabled_by_policy)
+        pref_service_->SetBoolean(prefs::kDSEWasDisabledByPolicy, true);
 
       PrefValue pref = GetDSEPref();
       GURL old_dse_origin(pref.dse_origin);
-      RestoreOldSettingAndReturnPrevious(old_dse_origin,
-                                         ContentSettingsType::GEOLOCATION,
-                                         pref.geolocation_setting_to_restore);
+      RestoreOldSettingAndReturnPrevious(
+          old_dse_origin, ContentSettingsType::GEOLOCATION,
+          pref.geolocation_setting_to_restore, !disabled_by_policy);
       if (pref.notifications_setting_to_restore != CONTENT_SETTING_DEFAULT) {
         RestoreOldSettingAndReturnPrevious(
             old_dse_origin, ContentSettingsType::NOTIFICATIONS,
-            pref.notifications_setting_to_restore);
+            pref.notifications_setting_to_restore, !disabled_by_policy);
       }
       pref_service_->ClearPref(prefs::kDSEPermissionsSettings);
     }
diff --git a/chrome/browser/android/search_permissions/search_permissions_service.h b/chrome/browser/android/search_permissions/search_permissions_service.h
index 3df7b2c..91d0f39 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service.h
+++ b/chrome/browser/android/search_permissions/search_permissions_service.h
@@ -120,7 +120,8 @@
   ContentSetting RestoreOldSettingAndReturnPrevious(
       const GURL& dse_origin,
       ContentSettingsType type,
-      ContentSetting setting_to_restore);
+      ContentSetting setting_to_restore,
+      bool preserve_block_setting);
 
   // Helper function for OnDSEChanged which transitions the DSE setting for a
   // specific permission. It returns the content setting to be restored later
diff --git a/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc b/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc
index 2d6338a..99f0112 100644
--- a/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc
+++ b/chrome/browser/android/search_permissions/search_permissions_service_unittest.cc
@@ -9,13 +9,17 @@
 
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/android/search_permissions/search_geolocation_disclosure_tab_helper.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.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_types.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/permissions/features.h"
 #include "components/permissions/permission_decision_auto_blocker.h"
 #include "components/permissions/permission_result.h"
 #include "components/prefs/pref_service.h"
@@ -77,7 +81,9 @@
   void SetUp() override {
     profile_ = std::make_unique<TestingProfile>();
 
-    ClearNotificationsChannels();
+    // Because notification channel settings aren't tied to the profile, they
+    // will persist across tests. We need to make sure they're clean here.
+    ClearContentSettings(ContentSettingsType::NOTIFICATIONS);
 
     auto test_delegate = std::make_unique<TestSearchEngineDelegate>();
     test_delegate_ = test_delegate.get();
@@ -88,22 +94,18 @@
   void TearDown() override {
     test_delegate_ = nullptr;
 
-    ClearNotificationsChannels();
+    // Because notification channel settings aren't tied to the profile, they
+    // will persist across tests. We need to make sure they're reset here.
+    ClearContentSettings(ContentSettingsType::NOTIFICATIONS);
 
     profile_.reset();
   }
 
-  void ClearNotificationsChannels() {
-    // Because notification channel settings aren't tied to the profile, they
-    // will persist across tests. We need to make sure they're reset here.
-    SetContentSetting(kGoogleURL, ContentSettingsType::NOTIFICATIONS,
-                      CONTENT_SETTING_DEFAULT);
-    SetContentSetting(kGoogleAusURL, ContentSettingsType::NOTIFICATIONS,
-                      CONTENT_SETTING_DEFAULT);
-    SetContentSetting(kGoogleHTTPURL, ContentSettingsType::NOTIFICATIONS,
-                      CONTENT_SETTING_DEFAULT);
-    SetContentSetting(kExampleURL, ContentSettingsType::NOTIFICATIONS,
-                      CONTENT_SETTING_DEFAULT);
+  void ClearContentSettings(ContentSettingsType type) {
+    SetContentSetting(kGoogleURL, type, CONTENT_SETTING_DEFAULT);
+    SetContentSetting(kGoogleAusURL, type, CONTENT_SETTING_DEFAULT);
+    SetContentSetting(kGoogleHTTPURL, type, CONTENT_SETTING_DEFAULT);
+    SetContentSetting(kExampleURL, type, CONTENT_SETTING_DEFAULT);
   }
 
   TestingProfile* profile() { return profile_.get(); }
@@ -637,3 +639,352 @@
   EXPECT_EQ(CONTENT_SETTING_BLOCK,
             GetContentSetting(kGoogleURL, ContentSettingsType::GEOLOCATION));
 }
+
+// Setting the `RevertDSEAutomaticPermissions` feature disables DSE permissions.
+TEST_F(SearchPermissionsServiceTest, DSEPermissionsCanBeDisabledByFeature) {
+  constexpr struct {
+    ContentSetting initial_setting;
+    ContentSetting expected_setting_after_autogrant;
+    ContentSetting expected_setting_after_autogrant_reverted;
+  } kTests[] = {
+      {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+      {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+      {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+      {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+  };
+
+  test_delegate()->ChangeDSEOrigin(kGoogleURL);
+
+  for (const auto& test : kTests) {
+    for (const auto type : {ContentSettingsType::NOTIFICATIONS,
+                            ContentSettingsType::GEOLOCATION}) {
+      // Notifications can not be set to ASK.
+      if (test.initial_setting == CONTENT_SETTING_ASK &&
+          type == ContentSettingsType::NOTIFICATIONS) {
+        continue;
+      }
+
+      ClearContentSettings(type);
+      SetContentSetting(kGoogleURL, type, test.initial_setting);
+
+      // Initialize DSE and verify the expected setting.
+      ReinitializeService(true /* clear_pref */);
+      EXPECT_EQ(test.expected_setting_after_autogrant,
+                GetContentSetting(kGoogleURL, type));
+
+      // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+      // the SearchPermissionsService.
+      {
+        base::test::ScopedFeatureList features;
+        features.InitAndEnableFeature(
+            permissions::features::kRevertDSEAutomaticPermissions);
+        ReinitializeService(false /* clear_pref */);
+        EXPECT_EQ(test.expected_setting_after_autogrant_reverted,
+                  GetContentSetting(kGoogleURL, type));
+      }
+
+      // If the feature is disabled again, DSE starts being controlled by the
+      // SearchPermissionsService.
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(test.expected_setting_after_autogrant,
+                GetContentSetting(kGoogleURL, type));
+    }
+  }
+}
+
+// Change the permission state for the DSE origin while DSE is active. Also test
+// how the `RevertDSEAutomaticPermissions` feature interacts with this scenario.
+TEST_F(SearchPermissionsServiceTest,
+       DSEOriginPermissionsChangeBeforeFeatureIsEnabled) {
+  constexpr struct {
+    ContentSetting initial_setting;
+    ContentSetting expected_setting_after_autogrant;
+    ContentSetting updated_setting;
+    ContentSetting expected_setting_after_update;
+    ContentSetting expected_setting_after_autogrant_reverted;
+    ContentSetting expected_setting_after_autogrant_reverted_disabled;
+  } kTests[] = {
+      {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
+       CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+      // Critical journey: if the user decided to change the DSE origin's
+      // setting to BLOCK then disabling DSE should still keep it at BLOCK.
+      {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+       CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+      {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
+       CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+      // Critical journey: if the user decided to change the DSE origin's
+      // setting to BLOCK then disabling DSE should still keep it at BLOCK.
+      {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+       CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+      {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW,
+       CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+      {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK,
+       CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+      {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
+       CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+      // Critical journey: if the user decided to change the DSE origin's
+      // setting to BLOCK then disabling DSE should still keep it at BLOCK.
+      {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
+       CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+  };
+
+  for (const auto& test : kTests) {
+    for (const auto type : {ContentSettingsType::NOTIFICATIONS,
+                            ContentSettingsType::GEOLOCATION}) {
+      // Notifications can not be set to ASK on Android as notification channels
+      // explicitly rely on the state being only BLOCK/ALLOW/DEFAULT.
+      if (test.initial_setting == CONTENT_SETTING_ASK &&
+          type == ContentSettingsType::NOTIFICATIONS) {
+        continue;
+      }
+
+      ClearContentSettings(type);
+      SetContentSetting(kGoogleURL, type, test.initial_setting);
+
+      // Initialize DSE and verify the expected setting.
+      ReinitializeService(true /* clear_pref */);
+      EXPECT_EQ(test.expected_setting_after_autogrant,
+                GetContentSetting(kGoogleURL, type));
+
+      // Change the setting of the DSE origin and verify the result.
+      SetContentSetting(kGoogleURL, type, test.updated_setting);
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(test.expected_setting_after_update,
+                GetContentSetting(kGoogleURL, type));
+
+      // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+      // the SearchPermissionsService.
+      {
+        base::test::ScopedFeatureList features;
+        features.InitAndEnableFeature(
+            permissions::features::kRevertDSEAutomaticPermissions);
+        ReinitializeService(false /* clear_pref */);
+        EXPECT_EQ(test.expected_setting_after_autogrant_reverted,
+                  GetContentSetting(kGoogleURL, type));
+      }
+
+      // If the feature is disabled again, DSE starts being controlled by the
+      // SearchPermissionsService.
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(test.expected_setting_after_autogrant_reverted_disabled,
+                GetContentSetting(kGoogleURL, type));
+    }
+  }
+}
+
+// Change the permission state for the DSE origin while the
+// `RevertDSEAutomaticPermissions` feature is active. Also test how disabling
+// the feature again afterwards will affect the permission.
+TEST_F(SearchPermissionsServiceTest,
+       DSEOriginPermissionsChangeAfterFeatureIsEnabled) {
+  constexpr struct {
+    ContentSetting initial_setting;
+    ContentSetting expected_setting_after_autogrant;
+    ContentSetting expected_setting_after_autogrant_reverted;
+  } kTestsBeforeSettingUpdate[] = {
+      {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+      {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+      {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+      {CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK},
+  };
+
+  constexpr struct {
+    ContentSetting updated_setting;
+    ContentSetting expected_setting_after_update;
+    ContentSetting expected_setting_after_autogrant_reverted_disabled;
+  } kTestsAfterSettingUpdate[] = {
+      {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW},
+      {CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK},
+      {CONTENT_SETTING_ASK, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+      {CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW},
+  };
+
+  // The test cases are split into two halves. Every combination of
+  // `kTestsBeforeSettingUpdate` and `kTestsBeforeSettingUpdate` entries
+  // produces a test case.
+  for (const auto& test_before : kTestsBeforeSettingUpdate) {
+    for (const auto& test_after : kTestsAfterSettingUpdate) {
+      for (const auto type : {ContentSettingsType::NOTIFICATIONS,
+                              ContentSettingsType::GEOLOCATION}) {
+        // Notifications can not be set to ASK on Android as notification
+        // channels explicitly rely on the state being only BLOCK/ALLOW/DEFAULT.
+        if (test_before.initial_setting == CONTENT_SETTING_ASK &&
+            type == ContentSettingsType::NOTIFICATIONS) {
+          continue;
+        }
+
+        ClearContentSettings(type);
+        SetContentSetting(kGoogleURL, type, test_before.initial_setting);
+
+        // Initialize DSE and verify the expected setting.
+        ReinitializeService(true /* clear_pref */);
+        EXPECT_EQ(test_before.expected_setting_after_autogrant,
+                  GetContentSetting(kGoogleURL, type));
+
+        // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+        // the SearchPermissionsService.
+        {
+          base::test::ScopedFeatureList features;
+          features.InitAndEnableFeature(
+              permissions::features::kRevertDSEAutomaticPermissions);
+          ReinitializeService(false /* clear_pref */);
+          EXPECT_EQ(test_before.expected_setting_after_autogrant_reverted,
+                    GetContentSetting(kGoogleURL, type));
+
+          // Notifications can not be set to ASK on Android as notification
+          // channels explicitly rely on the state being only
+          // BLOCK/ALLOW/DEFAULT.
+          if (test_after.updated_setting == CONTENT_SETTING_ASK &&
+              type == ContentSettingsType::NOTIFICATIONS) {
+            continue;
+          }
+
+          // Change the setting of the DSE origin and verify the result. This
+          // means that the user made a decision for the DSE origin while DSE
+          // is inactive.
+          SetContentSetting(kGoogleURL, type, test_after.updated_setting);
+          ReinitializeService(false /* clear_pref */);
+          EXPECT_EQ(test_after.expected_setting_after_update,
+                    GetContentSetting(kGoogleURL, type));
+        }
+
+        // If the feature is disabled again, DSE starts being controlled by the
+        // SearchPermissionsService. Test how it handles the user's setting
+        // change while DSE was inactive.
+        ReinitializeService(false /* clear_pref */);
+        EXPECT_EQ(test_after.expected_setting_after_autogrant_reverted_disabled,
+                  GetContentSetting(kGoogleURL, type));
+      }
+    }
+  }
+}
+
+// Tests the scenario in which the permission is disabled by default for all
+// origins.
+TEST_F(SearchPermissionsServiceTest, PermissionsDisabledByDefault) {
+  HostContentSettingsMap* hcsm =
+      HostContentSettingsMapFactory::GetForProfile(profile());
+
+  for (const auto type :
+       {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+    ClearContentSettings(type);
+    hcsm->SetDefaultContentSetting(type, CONTENT_SETTING_BLOCK);
+
+    // Global setting should still apply.
+    ReinitializeService(true /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+
+    // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by the
+    // SearchPermissionsService.
+    {
+      base::test::ScopedFeatureList features;
+      features.InitAndEnableFeature(
+          permissions::features::kRevertDSEAutomaticPermissions);
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+    }
+
+    // If the feature is disabled again, DSE starts being controlled by the
+    // SearchPermissionsService.
+    ReinitializeService(false /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+  }
+}
+
+// Tests the scenario in which the permission is disabled by default for all
+// origins after DSE has granted permission.
+TEST_F(SearchPermissionsServiceTest,
+       PermissionsDisabledByDefaultAfterAutogrant) {
+  HostContentSettingsMap* hcsm =
+      HostContentSettingsMapFactory::GetForProfile(profile());
+
+  for (const auto type :
+       {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+    ClearContentSettings(type);
+    ReinitializeService(true /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+    // Set the default content setting after the DSE has been applied.
+    hcsm->SetDefaultContentSetting(type, CONTENT_SETTING_BLOCK);
+    EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+    // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by the
+    // SearchPermissionsService.
+    {
+      base::test::ScopedFeatureList features;
+      features.InitAndEnableFeature(
+          permissions::features::kRevertDSEAutomaticPermissions);
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+    }
+
+    // If the feature is disabled again, DSE starts being controlled by the
+    // SearchPermissionsService.
+    ReinitializeService(false /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(kGoogleURL, type));
+  }
+}
+
+// Test repeatedly enabling and disabling the `RevertDSEAutomaticPermissions`
+// feature.
+TEST_F(SearchPermissionsServiceTest,
+       DSEPermissionsRepeatedlyDisabledByFeature) {
+  for (const auto type :
+       {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+    ClearContentSettings(type);
+    ReinitializeService(true /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+    int num_repetitions = 5;
+    while (num_repetitions--) {
+      // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+      // the SearchPermissionsService.
+      {
+        base::test::ScopedFeatureList features;
+        features.InitAndEnableFeature(
+            permissions::features::kRevertDSEAutomaticPermissions);
+        ReinitializeService(false /* clear_pref */);
+        EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+      }
+
+      // If the feature is disabled again, DSE starts being controlled by the
+      // SearchPermissionsService.
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+    }
+  }
+}
+
+// Test disabling DSE via the `RevertDSEAutomaticPermissions` feature after the
+// DSE origin has changed at least once.
+TEST_F(SearchPermissionsServiceTest, DSEDisabledAfterOriginChange) {
+  for (const auto type :
+       {ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS}) {
+    test_delegate()->ChangeDSEOrigin(kGoogleURL);
+    ClearContentSettings(type);
+    ReinitializeService(true /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleURL, type));
+
+    // Change DSE origin and make sure the setting is correctly updated.
+    test_delegate()->ChangeDSEOrigin(kGoogleAusURL);
+    EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleAusURL, type));
+    EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+
+    // Enable `RevertDSEAutomaticPermissions`. DSE stops being controlled by
+    // the SearchPermissionsService.
+    {
+      base::test::ScopedFeatureList features;
+      features.InitAndEnableFeature(
+          permissions::features::kRevertDSEAutomaticPermissions);
+      ReinitializeService(false /* clear_pref */);
+      EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleAusURL, type));
+      EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+    }
+
+    // If the feature is disabled again, DSE starts being controlled by the
+    // SearchPermissionsService.
+    ReinitializeService(false /* clear_pref */);
+    EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(kGoogleAusURL, type));
+    EXPECT_EQ(CONTENT_SETTING_ASK, GetContentSetting(kGoogleURL, type));
+  }
+}
diff --git a/components/permissions/features.cc b/components/permissions/features.cc
index d48e35a..744b7645 100644
--- a/components/permissions/features.cc
+++ b/components/permissions/features.cc
@@ -50,6 +50,13 @@
     "kPermissionPredictionServiceUseUrlOverride",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_ANDROID)
+// When enabled, the Default Search Engine does not automatically receive the
+// "geolocation" and "notifications" permissions. DSE only applies to Android.
+const base::Feature kRevertDSEAutomaticPermissions{
+    "RevertDSEAutomaticPermissions", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif  // defined(OS_ANDROID)
+
 }  // namespace features
 namespace feature_params {
 
diff --git a/components/permissions/features.h b/components/permissions/features.h
index bc63e26..3b4485c 100644
--- a/components/permissions/features.h
+++ b/components/permissions/features.h
@@ -5,8 +5,10 @@
 #ifndef COMPONENTS_PERMISSIONS_FEATURES_H_
 #define COMPONENTS_PERMISSIONS_FEATURES_H_
 
+#include "base/component_export.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
+#include "build/build_config.h"
 
 namespace permissions {
 namespace features {
@@ -20,6 +22,11 @@
 extern const base::Feature kPermissionChipRequestTypeSensitive;
 extern const base::Feature kPermissionPredictionServiceUseUrlOverride;
 
+#if defined(OS_ANDROID)
+COMPONENT_EXPORT(PERMISSIONS_COMMON)
+extern const base::Feature kRevertDSEAutomaticPermissions;
+#endif  // defined(OS_ANDROID)
+
 }  // namespace features
 namespace feature_params {