Commit 93afe627 authored by Andrey Zaytsev's avatar Andrey Zaytsev Committed by Commit Bot

Safety check for Android: connected the UI with the C++ bridge

Final state: https://screenshot.googleplex.com/xh54f8vfpEL.png

Bug: 1070620
Change-Id: Ib1ac093b2a303530ab404cb0ab1cb0cfe4fdfd55
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2254118
Commit-Queue: Andrey Zaytsev <andzaytsev@google.com>
Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781286}
parent 3066d0b4
...@@ -24,8 +24,10 @@ source_set("android") { ...@@ -24,8 +24,10 @@ source_set("android") {
android_library("java") { android_library("java") {
sources = [ sources = [
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheck.java",
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridge.java", "java/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridge.java",
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheckController.java",
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheckElement.java",
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheckModel.java",
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheckSettingsFragment.java", "java/src/org/chromium/chrome/browser/safety_check/SafetyCheckSettingsFragment.java",
] ]
deps = [ deps = [
...@@ -49,7 +51,10 @@ android_library("junit") { ...@@ -49,7 +51,10 @@ android_library("junit") {
# Skip platform checks since Robolectric depends on requires_android targets. # Skip platform checks since Robolectric depends on requires_android targets.
bypass_platform_checks = true bypass_platform_checks = true
testonly = true testonly = true
sources = [ "javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridgeTest.java" ] sources = [
"javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckBridgeTest.java",
"javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckModelTest.java",
]
deps = [ deps = [
":java", ":java",
"//base:base_java", "//base:base_java",
......
...@@ -10,17 +10,20 @@ import org.chromium.chrome.browser.safety_check.SafetyCheckBridge.SafetyCheckCom ...@@ -10,17 +10,20 @@ import org.chromium.chrome.browser.safety_check.SafetyCheckBridge.SafetyCheckCom
/** /**
* Main class for all Safety check related logic. * Main class for all Safety check related logic.
*/ */
public class SafetyCheck implements SafetyCheckCommonObserver { public class SafetyCheckController implements SafetyCheckCommonObserver {
private SafetyCheckBridge mSafetyCheckBridge; private SafetyCheckBridge mSafetyCheckBridge;
private SafetyCheckModel mModel;
public SafetyCheck() { public SafetyCheckController(SafetyCheckModel model) {
mSafetyCheckBridge = new SafetyCheckBridge(SafetyCheck.this); mSafetyCheckBridge = new SafetyCheckBridge(SafetyCheckController.this);
mModel = model;
} }
/** /**
* Triggers all safety check child checks. * Triggers all safety check child checks.
*/ */
public void performSafetyCheck() { public void performSafetyCheck() {
mModel.setCheckingState();
mSafetyCheckBridge.checkSafeBrowsing(); mSafetyCheckBridge.checkSafeBrowsing();
mSafetyCheckBridge.checkPasswords(); mSafetyCheckBridge.checkPasswords();
} }
...@@ -33,7 +36,9 @@ public class SafetyCheck implements SafetyCheckCommonObserver { ...@@ -33,7 +36,9 @@ public class SafetyCheck implements SafetyCheckCommonObserver {
* //components/safety_check/safety_check.h). * //components/safety_check/safety_check.h).
*/ */
@Override @Override
public void onSafeBrowsingCheckResult(@SafeBrowsingStatus int status) {} public void onSafeBrowsingCheckResult(@SafeBrowsingStatus int status) {
mModel.updateSafeBrowsingStatus(status);
}
/** /**
* Gets invoked by the C++ code every time another credential is checked. * Gets invoked by the C++ code every time another credential is checked.
...@@ -52,5 +57,17 @@ public class SafetyCheck implements SafetyCheckCommonObserver { ...@@ -52,5 +57,17 @@ public class SafetyCheck implements SafetyCheckCommonObserver {
* //components/password_manager/core/browser/bulk_leak_check_service_interface.h). * //components/password_manager/core/browser/bulk_leak_check_service_interface.h).
*/ */
@Override @Override
public void onPasswordCheckStateChange(@BulkLeakCheckServiceState int state) {} public void onPasswordCheckStateChange(@BulkLeakCheckServiceState int state) {
if (state == BulkLeakCheckServiceState.RUNNING) {
return;
}
mSafetyCheckBridge.stopObservingPasswordsCheck();
if (state == BulkLeakCheckServiceState.IDLE) {
boolean hasPasswords = mSafetyCheckBridge.savedPasswordsExist();
int numLeaked = mSafetyCheckBridge.getNumberOfPasswordLeaksFromLastCheck();
mModel.updatePasswordsStatusOnSucess(hasPasswords, numLeaked);
} else {
mModel.updatePasswordsStatusOnError(state);
}
}
} }
// Copyright 2020 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.safety_check;
/**
* Interface for storing information related to each Safety check element
* (passwords, updates, etc) in the model.
*/
public interface SafetyCheckElement {
/**
* @return The resource ID for the corresponding status string.
*/
public int getStatusString();
}
// Copyright 2020 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.safety_check;
import org.chromium.chrome.browser.password_check.BulkLeakCheckServiceState;
/**
* Represents and stores the internal state of a Safety check.
*/
public class SafetyCheckModel {
/**
* Stores the state related to the Safe Browsing element.
*/
public enum SafeBrowsing implements SafetyCheckElement {
UNCHECKED(R.string.safety_check_unchecked),
CHECKING(R.string.safety_check_checking),
ENABLED_STANDARD(R.string.safety_check_safe_browsing_enabled_standard),
ENABLED_ENHANCED(R.string.safety_check_safe_browsing_enabled_enhanced),
DISABLED(R.string.safety_check_safe_browsing_disabled),
DISABLED_BY_ADMIN(R.string.safety_check_safe_browsing_disabled_by_admin),
ERROR(R.string.safety_check_error);
private final int mStatusString;
private SafeBrowsing(int statusString) {
mStatusString = statusString;
}
/**
* @return The resource ID for the corresponding status string.
*/
@Override
public int getStatusString() {
return mStatusString;
}
/**
* android:key element in XML corresponding to Safe Browsing.
*/
public static final String KEY = "safe_browsing";
/**
* Converts the C++ enum state to the internal representation.
* @param status Safe Browsing Status from C++.
* @return A SafeBrowsing enum element.
*/
public static SafeBrowsing fromSafeBrowsingStatus(@SafeBrowsingStatus int status) {
switch (status) {
case SafeBrowsingStatus.CHECKING:
return CHECKING;
case SafeBrowsingStatus.ENABLED:
case SafeBrowsingStatus.ENABLED_STANDARD:
return ENABLED_STANDARD;
case SafeBrowsingStatus.ENABLED_ENHANCED:
return ENABLED_ENHANCED;
case SafeBrowsingStatus.DISABLED:
return DISABLED;
case SafeBrowsingStatus.DISABLED_BY_ADMIN:
return DISABLED_BY_ADMIN;
default:
return ERROR;
}
}
}
/**
* Stores the state related to the Passwords element.
*/
public enum Passwords implements SafetyCheckElement {
UNCHECKED(R.string.safety_check_unchecked),
CHECKING(R.string.safety_check_checking),
SAFE,
COMPROMISED_EXIST,
OFFLINE,
NO_PASSWORDS(R.string.safety_check_passwords_no_passwords),
SIGNED_OUT,
QUOTA_LIMIT,
ERROR(R.string.safety_check_error);
private final int mStatusString;
private Passwords() {
mStatusString = 0;
}
private Passwords(int statusString) {
mStatusString = statusString;
}
/**
* @return The resource ID for the corresponding status string.
*/
@Override
public int getStatusString() {
return mStatusString;
}
/**
* android:key element in XML corresponding to Safe Browsing.
*/
public static final String KEY = "passwords";
public static Passwords fromErrorState(@BulkLeakCheckServiceState int state) {
switch (state) {
case BulkLeakCheckServiceState.SIGNED_OUT:
return SIGNED_OUT;
case BulkLeakCheckServiceState.QUOTA_LIMIT:
return QUOTA_LIMIT;
default:
return ERROR;
}
}
}
private SafetyCheckSettingsFragment mView;
private SafeBrowsing mSafeBrowsing;
private Passwords mPasswords;
/**
* Creates a new model for Safety check.
* @param view A Settings Fragment for Safety check.
*/
public SafetyCheckModel(SafetyCheckSettingsFragment view) {
mView = view;
}
/**
* Updates the model and the view with the "Checking" state.
*/
public void setCheckingState() {
mSafeBrowsing = SafeBrowsing.CHECKING;
mPasswords = Passwords.CHECKING;
mView.updateElementStatus(SafeBrowsing.KEY, mSafeBrowsing.getStatusString());
mView.updateElementStatus(Passwords.KEY, mPasswords.getStatusString());
}
/**
* Updates the model and the view with the results of a Safe Browsing check.
* @param status The status of Safe Browsing.
*/
public void updateSafeBrowsingStatus(@SafeBrowsingStatus int status) {
mSafeBrowsing = SafeBrowsing.fromSafeBrowsingStatus(status);
mView.updateElementStatus(SafeBrowsing.KEY, mSafeBrowsing.getStatusString());
}
/**
* Updates the model and the view with the results of a successful passwords
* check.
* @param hasPasswords Whether any passwords are saved.
* @param numLeaked Number of leaked passwords.
*/
public void updatePasswordsStatusOnSucess(boolean hasPasswords, int numLeaked) {
if (!hasPasswords) {
mPasswords = Passwords.NO_PASSWORDS;
} else if (numLeaked == 0) {
mPasswords = Passwords.SAFE;
} else {
mPasswords = Passwords.COMPROMISED_EXIST;
}
mView.updateElementStatus(Passwords.KEY, mPasswords.getStatusString());
}
/**
* Updates the model and the view with the results of a failed passwords
* check.
* @param state State of the leak check service reflecting an error.
*/
public void updatePasswordsStatusOnError(@BulkLeakCheckServiceState int state) {
mPasswords = Passwords.fromErrorState(state);
mView.updateElementStatus(Passwords.KEY, mPasswords.getStatusString());
}
}
...@@ -10,6 +10,7 @@ import android.view.View; ...@@ -10,6 +10,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import org.chromium.components.browser_ui.settings.SettingsUtils; import org.chromium.components.browser_ui.settings.SettingsUtils;
...@@ -19,8 +20,15 @@ import org.chromium.ui.widget.ButtonCompat; ...@@ -19,8 +20,15 @@ import org.chromium.ui.widget.ButtonCompat;
* Fragment containing Safety check. * Fragment containing Safety check.
*/ */
public class SafetyCheckSettingsFragment extends PreferenceFragmentCompat { public class SafetyCheckSettingsFragment extends PreferenceFragmentCompat {
private SafetyCheckController mController;
private SafetyCheckModel mModel;
@Override @Override
public void onCreatePreferences(Bundle bundle, String s) { public void onCreatePreferences(Bundle bundle, String s) {
// Create the model and the controller.
mModel = new SafetyCheckModel(this);
mController = new SafetyCheckController(mModel);
// Add all preferences and set the title.
SettingsUtils.addPreferencesFromResource(this, R.xml.safety_check_preferences); SettingsUtils.addPreferencesFromResource(this, R.xml.safety_check_preferences);
getActivity().setTitle(R.string.prefs_safety_check); getActivity().setTitle(R.string.prefs_safety_check);
} }
...@@ -30,7 +38,6 @@ public class SafetyCheckSettingsFragment extends PreferenceFragmentCompat { ...@@ -30,7 +38,6 @@ public class SafetyCheckSettingsFragment extends PreferenceFragmentCompat {
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LinearLayout view = LinearLayout view =
(LinearLayout) super.onCreateView(inflater, container, savedInstanceState); (LinearLayout) super.onCreateView(inflater, container, savedInstanceState);
// Add a button to the bottom of the preferences view. // Add a button to the bottom of the preferences view.
ButtonCompat checkButton = ButtonCompat checkButton =
(ButtonCompat) inflater.inflate(R.layout.safety_check_button, view, false); (ButtonCompat) inflater.inflate(R.layout.safety_check_button, view, false);
...@@ -39,5 +46,17 @@ public class SafetyCheckSettingsFragment extends PreferenceFragmentCompat { ...@@ -39,5 +46,17 @@ public class SafetyCheckSettingsFragment extends PreferenceFragmentCompat {
return view; return view;
} }
private void onSafetyCheckButtonClicked() {} private void onSafetyCheckButtonClicked() {
mController.performSafetyCheck();
}
/**
* Update the status string of a given Safety check element, e.g. Passwords.
* @param key An android:key String corresponding to Safety check element.
* @param statusString Resource ID of the new status string.
*/
public void updateElementStatus(String key, int statusString) {
Preference p = findPreference(key);
p.setSummary(statusString);
}
} }
// Copyright 2020 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.safety_check;
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.password_check.BulkLeakCheckServiceState;
/** Unit tests for {@link SafetyCheckModel}. */
@RunWith(BaseRobolectricTestRunner.class)
public class SafetyCheckModelTest {
@Mock
private SafetyCheckSettingsFragment mView;
private SafetyCheckModel mSafetyCheckModel;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mSafetyCheckModel = new SafetyCheckModel(mView);
}
/**
* Tests a standard workflow for Safe Browsing.
*/
@Test
public void testSafeBrowsing() {
mSafetyCheckModel.setCheckingState();
verify(mView).updateElementStatus("safe_browsing", R.string.safety_check_checking);
mSafetyCheckModel.updateSafeBrowsingStatus(SafeBrowsingStatus.ENABLED_STANDARD);
verify(mView).updateElementStatus(
"safe_browsing", R.string.safety_check_safe_browsing_enabled_standard);
}
/**
* Tests a successful workflow for the passwords check.
*/
@Test
public void testPasswordsSuccess() {
mSafetyCheckModel.setCheckingState();
verify(mView).updateElementStatus("passwords", R.string.safety_check_checking);
mSafetyCheckModel.updatePasswordsStatusOnSucess(false, 0);
verify(mView).updateElementStatus(
"passwords", R.string.safety_check_passwords_no_passwords);
}
/**
* Tests a failure workflow for the passwords check.
*/
@Test
public void testPasswordsError() {
mSafetyCheckModel.setCheckingState();
verify(mView).updateElementStatus("passwords", R.string.safety_check_checking);
mSafetyCheckModel.updatePasswordsStatusOnError(
BulkLeakCheckServiceState.TOKEN_REQUEST_FAILURE);
verify(mView).updateElementStatus("passwords", R.string.safety_check_error);
}
}
...@@ -856,6 +856,27 @@ Your Google account may have other forms of browsing history like searches and a ...@@ -856,6 +856,27 @@ Your Google account may have other forms of browsing history like searches and a
<message name="IDS_SAFETY_CHECK_UNCHECKED" desc="A given element has not been checked."> <message name="IDS_SAFETY_CHECK_UNCHECKED" desc="A given element has not been checked.">
Unchecked Unchecked
</message> </message>
<message name="IDS_SAFETY_CHECK_CHECKING" desc="A given element is being checked.">
Checking
</message>
<message name="IDS_SAFETY_CHECK_ERROR" desc="A generic error state.">
An error occurred.
</message>
<message name="IDS_SAFETY_CHECK_SAFE_BROWSING_DISABLED" desc="Text to display when Safe Browsing is disabled.">
Safe Browsing is off. Chrome recommends turning it on.
</message>
<message name="IDS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD" desc="Text to display when Safe Browsing is set to standard protection.">
Standard Protection is on
</message>
<message name="IDS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_ENHANCED" desc="Text to display when Safe Browsing is set to enhanced protection.">
Enhanced Protection is on
</message>
<message name="IDS_SAFETY_CHECK_SAFE_BROWSING_DISABLED_BY_ADMIN" desc="Text to display when Safe Browsing is disabled by admin.">
<ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Your administrator<ph name="END_LINK">&lt;/a&gt;</ph> has turned off Safe Browsing
</message>
<message name="IDS_SAFETY_CHECK_PASSWORDS_NO_PASSWORDS" desc="Text to display when no passwords are saved.">
No saved passwords. Chrome can check your passwords when you save them.
</message>
<!-- Accessibility preferences --> <!-- Accessibility preferences -->
<message name="IDS_PREFS_ACCESSIBILITY" desc="Title of Accessibility settings, which allows the user to change webpage font sizes. [CHAR-LIMIT=32]"> <message name="IDS_PREFS_ACCESSIBILITY" desc="Title of Accessibility settings, which allows the user to change webpage font sizes. [CHAR-LIMIT=32]">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment